├── VERSION ├── tests ├── testdata │ └── foo │ │ └── bar.txt ├── main.cpp └── unit-tests │ ├── dummy.t.cpp │ ├── boost-test.hpp │ ├── util │ └── io-util.t.cpp │ ├── unit-test-time-fixture.hpp │ ├── dummy-parser-fixture.hpp │ ├── stats-table-record.t.cpp │ ├── stats-table.t.cpp │ ├── update-handler.t.cpp │ └── file-manifest.t.cpp ├── waf ├── AUTHORS ├── .jenkins.d ├── util.sh ├── 30-coverage.sh ├── 20-tests.sh ├── 00-deps.sh ├── 10-build.sh └── 01-ndn-cxx.sh ├── .gitignore ├── .jenkins ├── .travis.yml ├── README.md ├── .waf-tools ├── coverage.py ├── cryptopp.py ├── sphinx_build.py ├── default-compiler-flags.py └── doxygen.py ├── .clang-format ├── src ├── util │ ├── shared-constants.hpp │ ├── logging.hpp │ ├── logging.cpp │ ├── io-util.hpp │ └── io-util.cpp ├── interest-queue.cpp ├── stats-table.cpp ├── stats-table-record.cpp ├── interest-queue.hpp ├── fetching-strategy-manager.hpp ├── sequential-data-fetcher.hpp ├── stats-table-record.hpp ├── stats-table.hpp ├── update-handler.hpp ├── sequential-data-fetcher.cpp ├── main.cpp ├── update-handler.cpp ├── torrent-file.hpp ├── torrent-file.cpp ├── file-manifest.hpp ├── file-manifest.cpp └── torrent-manager.hpp ├── README-dev.md └── wscript /VERSION: -------------------------------------------------------------------------------- 1 | 0.1.0 2 | -------------------------------------------------------------------------------- /tests/testdata/foo/bar.txt: -------------------------------------------------------------------------------- 1 | this is a test -------------------------------------------------------------------------------- /waf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spirosmastorakis/nTorrent/HEAD/waf -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | The PRIMARY AUTHORS are (and/or have been): 2 | 3 | * Spyridon "Spyros" Mastorakis 4 | * Mickey Sweatt 5 | -------------------------------------------------------------------------------- /.jenkins.d/util.sh: -------------------------------------------------------------------------------- 1 | has() { 2 | local p=$1 3 | shift 4 | local x 5 | for x in "$@"; do 6 | [[ "${x}" == "${p}" ]] && return 0 7 | done 8 | return 1 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Emacs temp files 2 | *~ 3 | 4 | # Mac OSX 5 | .DS_* 6 | 7 | # waf build system 8 | .waf-1* 9 | .waf3-* 10 | .lock* 11 | build/ 12 | 13 | # Compiled python code 14 | *.pyc 15 | 16 | # Other 17 | VERSION -------------------------------------------------------------------------------- /.jenkins: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 5 | 6 | for file in "$DIR"/.jenkins.d/*; do 7 | [[ -f $file && -x $file ]] || continue 8 | echo "Run: $file" 9 | "$file" 10 | done 11 | -------------------------------------------------------------------------------- /.jenkins.d/30-coverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -x 3 | set -e 4 | 5 | JDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 6 | source "$JDIR"/util.sh 7 | 8 | if [[ "$JOB_NAME" == *"code-coverage" ]]; then 9 | BASE="`pwd | sed -e 's|/|\\\/|g'`\\" 10 | (cd build && gcovr -x -f $BASE/core -f $BASE/daemon -f $BASE/rib -r ../ -o coverage.xml ./) 11 | fi 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: generic 3 | matrix: 4 | include: 5 | - os: linux 6 | dist: trusty 7 | compiler: gcc 8 | - os: linux 9 | dist: trusty 10 | compiler: clang 11 | - os: osx 12 | compiler: clang 13 | notifications: 14 | email: 15 | on_success: always 16 | on_failure: always 17 | script: 18 | - if [[ $TRAVIS_OS_NAME == linux ]]; then export NODE_LABELS="Linux Ubuntu Ubuntu-14.04"; fi 19 | - if [[ $TRAVIS_OS_NAME == osx ]]; then export NODE_LABELS="OSX OSX-10.9"; fi 20 | - echo $NODE_LABELS 21 | - ./.jenkins 22 | -------------------------------------------------------------------------------- /.jenkins.d/20-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -x 3 | set -e 4 | 5 | JDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 6 | source "$JDIR"/util.sh 7 | 8 | # Prepare environment 9 | rm -Rf ~/.ndn 10 | 11 | if has OSX $NODE_LABELS; then 12 | echo "Unlocking OSX Keychain" 13 | security unlock-keychain -p "named-data" 14 | fi 15 | 16 | ndnsec-keygen "/tmp/jenkins/$NODE_NAME" | ndnsec-install-cert - 17 | 18 | # Run unit tests 19 | if [[ -n "$XUNIT" ]]; then 20 | ./build/unit-tests --log_level=all -- --log_format2=XML --log_sink2=build/xunit-report.xml 21 | else 22 | ./build/unit-tests -l test_suite 23 | fi 24 | -------------------------------------------------------------------------------- /.jenkins.d/00-deps.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | JDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 5 | source "$JDIR"/util.sh 6 | 7 | if has OSX $NODE_LABELS; then 8 | if has OSX-10.8 $NODE_LABELS; then 9 | EXTRA_FLAGS=--c++11 10 | fi 11 | 12 | set -x 13 | brew update 14 | brew upgrade 15 | brew install boost pkg-config cryptopp $EXTRA_FLAGS 16 | brew cleanup 17 | fi 18 | 19 | if has Ubuntu $NODE_LABELS; then 20 | BOOST_PKG=libboost-all-dev 21 | if has Ubuntu-12.04 $NODE_LABELS; then 22 | BOOST_PKG=libboost1.48-all-dev 23 | fi 24 | 25 | set -x 26 | sudo apt-get update -qq -y 27 | sudo apt-get -qq -y install build-essential pkg-config $BOOST_PKG \ 28 | libcrypto++-dev libsqlite3-dev 29 | fi 30 | -------------------------------------------------------------------------------- /.jenkins.d/10-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -x 3 | set -e 4 | 5 | git submodule init 6 | git submodule sync 7 | git submodule update 8 | 9 | # Cleanup 10 | sudo ./waf -j1 --color=yes distclean 11 | 12 | # Configure/build in optimized mode with tests and precompiled headers 13 | ./waf -j1 --color=yes configure --with-tests 14 | ./waf -j1 --color=yes build 15 | 16 | # Cleanup 17 | sudo ./waf -j1 --color=yes distclean 18 | 19 | # Configure/build in optimized mode without tests and with precompiled headers 20 | ./waf -j1 --color=yes configure 21 | ./waf -j1 --color=yes build 22 | 23 | # Cleanup 24 | sudo ./waf -j1 --color=yes distclean 25 | 26 | ./waf -j1 --color=yes configure --with-tests 27 | ./waf -j1 --color=yes build 28 | 29 | # (tests will be run against debug version) 30 | 31 | # Install 32 | sudo ./waf -j1 --color=yes install 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | nTorrent: An application to provide BitTorrent-like functions in Named Data Networking (NDN) 2 | ============================================================================================ 3 | 4 | nTorrent is an NDN application to provide peer-to-peer file sharing functionality similar to 5 | BitTorrent in TCP/IP networks. 6 | 7 | Please submit any bugs or issues to the nTorrent issue tracker: 8 | http://redmine.named-data.net/projects/ntorrent/issues 9 | 10 | For the nTorrent simulation code running in ndnSIM, please take a look here: 11 | https://github.com/AkshayRaman/scenario-ntorrent 12 | 13 | ## More documentation 14 | 15 | Extensive documentation is coming soon! 16 | 17 | ## License 18 | 19 | nTorrent is an open source project licensed under LGPL 3.0 license. For more information about 20 | the license, refer to [`COPYING.md`](https://github.com/spirosmastorakis/nTorrent/blob/master/COPYING.md). 21 | -------------------------------------------------------------------------------- /.waf-tools/coverage.py: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- 2 | 3 | from waflib import TaskGen, Logs 4 | 5 | def options(opt): 6 | opt.add_option('--with-coverage', action='store_true', default=False, dest='with_coverage', 7 | help='''Set compiler flags for gcc to enable code coverage information''') 8 | 9 | def configure(conf): 10 | if conf.options.with_coverage: 11 | if not conf.options.debug: 12 | conf.fatal("Code coverage flags require debug mode compilation (add --debug)") 13 | conf.check_cxx(cxxflags=['-fprofile-arcs', '-ftest-coverage', '-fPIC'], 14 | linkflags=['-fprofile-arcs'], uselib_store='GCOV', mandatory=True) 15 | 16 | @TaskGen.feature('cxx','cc') 17 | @TaskGen.after('process_source') 18 | def add_coverage(self): 19 | if getattr(self, 'use', ''): 20 | self.use += ' GCOV' 21 | else: 22 | self.use = 'GCOV' 23 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # This style requires customized clang-format from https://github.com/cawka/clang 2 | # 3 | BasedOnStyle: GNU 4 | --- 5 | Language: Cpp 6 | AlwaysBreakAfterDefinitionReturnType: true 7 | AlwaysBreakAfterDeclarationReturnType: true 8 | ColumnLimit: 100 9 | SpaceBeforeParens: ControlStatements 10 | Cpp11BracedListStyle: true 11 | BreakBeforeBraces: Stroustrup 12 | PointerAlignment: Left 13 | PenaltyReturnTypeOnItsOwnLine: 0 14 | AllowShortBlocksOnASingleLine: false 15 | # AllowShortCaseLabelsOnASingleLine: false 16 | AllowShortFunctionsOnASingleLine: false 17 | AllowShortIfStatementsOnASingleLine: false 18 | AllowShortLoopsOnASingleLine: false 19 | 20 | BreakConstructorInitializersBeforeComma: true 21 | NamespaceIndentation: None 22 | Standard: Cpp11 23 | 24 | AlwaysBreakTemplateDeclarations: true 25 | IndentWidth: 2 26 | PenaltyBreakBeforeFirstCallParameter: 500 27 | SpacesBeforeTrailingComments: 1 28 | UseTab: Never 29 | ConstructorInitializerIndentWidth: 2 30 | 31 | SpaceBetweenTemplateAndOpeningAngle: false 32 | BreakBeforeBinaryOperators: NonAssignment 33 | ContinuationIndentWidth: 2 34 | -------------------------------------------------------------------------------- /.jenkins.d/01-ndn-cxx.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -x 3 | set -e 4 | 5 | JDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 6 | source "$JDIR"/util.sh 7 | 8 | pushd ${CACHE_DIR:-/tmp} >/dev/null 9 | 10 | INSTALLED_VERSION=$((cd ndn-cxx && git rev-parse HEAD) 2>/dev/null || echo NONE) 11 | 12 | sudo rm -Rf ndn-cxx-latest 13 | 14 | git clone --depth 1 git://github.com/named-data/ndn-cxx ndn-cxx-latest 15 | 16 | LATEST_VERSION=$((cd ndn-cxx-latest && git rev-parse HEAD) 2>/dev/null || echo UNKNOWN) 17 | 18 | if [[ $INSTALLED_VERSION != $LATEST_VERSION ]]; then 19 | sudo rm -Rf ndn-cxx 20 | mv ndn-cxx-latest ndn-cxx 21 | else 22 | sudo rm -Rf ndn-cxx-latest 23 | fi 24 | 25 | sudo rm -Rf /usr/local/include/ndn-cxx 26 | sudo rm -f /usr/local/lib/libndn-cxx* 27 | sudo rm -f /usr/local/lib/pkgconfig/libndn-cxx* 28 | 29 | pushd ndn-cxx >/dev/null 30 | 31 | ./waf configure -j1 --color=yes --enable-shared --disable-static --without-osx-keychain 32 | ./waf -j1 --color=yes 33 | sudo ./waf install -j1 --color=yes 34 | 35 | popd >/dev/null 36 | popd >/dev/null 37 | 38 | if has Linux $NODE_LABELS; then 39 | sudo ldconfig 40 | elif has FreeBSD $NODE_LABELS; then 41 | sudo ldconfig -a 42 | fi 43 | -------------------------------------------------------------------------------- /src/util/shared-constants.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | #ifndef INCLUDED_SHARED_CONSTANTS_HPP 22 | #define INCLUDED_SHARED_CONSTANTS_HPP 23 | 24 | namespace ndn { 25 | namespace ntorrent { 26 | 27 | struct SharedConstants { 28 | static const char* commonPrefix; 29 | }; 30 | 31 | } // namespace ntorrent 32 | } // namespace ndn 33 | 34 | #endif // INCLUDED_SHARED_CONSTANTS_HPP -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS.md for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #define BOOST_TEST_MAIN 1 23 | #define BOOST_TEST_DYN_LINK 1 24 | 25 | #include "util/shared-constants.hpp" 26 | 27 | #include 28 | 29 | namespace ndn { 30 | namespace ntorrent { 31 | 32 | const char * SharedConstants::commonPrefix = "/ndn/multicast"; 33 | 34 | } // namespace ntorrent 35 | } // namespace ndn 36 | -------------------------------------------------------------------------------- /tests/unit-tests/dummy.t.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #include "boost-test.hpp" 23 | 24 | namespace ndn { 25 | 26 | namespace ntorrent { 27 | 28 | namespace tests { 29 | 30 | BOOST_AUTO_TEST_SUITE(DummyTest) 31 | 32 | BOOST_AUTO_TEST_CASE(Dummy) 33 | { 34 | BOOST_CHECK_EQUAL(1, 1); 35 | } 36 | 37 | BOOST_AUTO_TEST_SUITE_END() 38 | 39 | } // namespace tests 40 | 41 | } // namespace ntorrent 42 | 43 | } // namespace ndn 44 | -------------------------------------------------------------------------------- /src/interest-queue.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #include "interest-queue.hpp" 23 | 24 | namespace ndn { 25 | namespace ntorrent { 26 | 27 | void 28 | InterestQueue::push(shared_ptr interest, DataCallback dataReceivedCallback, 29 | TimeoutCallback dataFailedCallback) 30 | { 31 | m_queue.push(std::make_tuple(interest, dataReceivedCallback, dataFailedCallback)); 32 | } 33 | 34 | queueTuple 35 | InterestQueue::pop() 36 | { 37 | queueTuple tup = m_queue.front(); 38 | m_queue.pop(); 39 | return tup; 40 | } 41 | 42 | } // namespace ntorrent 43 | } // namespace ndn 44 | -------------------------------------------------------------------------------- /README-dev.md: -------------------------------------------------------------------------------- 1 | Notes for nTorrent developers 2 | ============================= 3 | 4 | Licensing 5 | --------- 6 | 7 | Contributions to the library must be licensed under LGPL 3.0 or compatible license. If 8 | you are choosing LGPL 3.0, please use the following license boilerplate in all `.hpp` and 9 | `.cpp` files: 10 | 11 | 12 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 13 | /** 14 | * Copyright (c) [Year(s)] [Copyright Holder]. 15 | * 16 | * This file is part of nTorrent codebase. 17 | * 18 | * nTorrent library is free software: you can redistribute it and/or modify it under the 19 | * terms of the GNU Lesser General Public License as published by the Free Software 20 | * Foundation, either version 3 of the License, or (at your option) any later version. 21 | * 22 | * nTorrent library is distributed in the hope that it will be useful, but WITHOUT ANY 23 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 24 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 25 | * 26 | * You should have received copies of the GNU General Public License and GNU Lesser 27 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 28 | * . 29 | * 30 | * See AUTHORS for complete list of nTorrent authors and contributors. 31 | ////// [optional part] ////// 32 | * 33 | * @author Author's Name 34 | * @author Other Author's Name 35 | ////// [end of optional part] ////// 36 | */ 37 | -------------------------------------------------------------------------------- /tests/unit-tests/boost-test.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #ifndef NTORRENT_TESTS_BOOST_TEST_HPP 23 | #define NTORRENT_TESTS_BOOST_TEST_HPP 24 | 25 | // suppress warnings from Boost.Test 26 | #pragma GCC system_header 27 | #pragma clang system_header 28 | 29 | #include "util/logging.hpp" 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | 37 | struct NtorrentGlobalConfig { 38 | NtorrentGlobalConfig() { 39 | ndn::ntorrent::LoggingUtil::init(); 40 | boost::log::add_common_attributes(); 41 | } 42 | 43 | ~NtorrentGlobalConfig() { 44 | } 45 | }; 46 | 47 | BOOST_GLOBAL_FIXTURE(NtorrentGlobalConfig); 48 | 49 | #endif // NTORRENT_TESTS_BOOST_TEST_HPP 50 | -------------------------------------------------------------------------------- /tests/unit-tests/util/io-util.t.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #include "../boost-test.hpp" 23 | #include "util/io-util.hpp" 24 | 25 | namespace ndn { 26 | namespace ntorrent { 27 | namespace tests { 28 | 29 | BOOST_AUTO_TEST_SUITE(TestIoUtils) 30 | 31 | BOOST_AUTO_TEST_CASE(TestFindType) 32 | { 33 | BOOST_CHECK_EQUAL(IoUtil::findType(Name("NTORRENT/linux/torrent-file/sha256digest")), 0); 34 | 35 | Name n1("NTORRENT/linux/torrent-file"); 36 | n1.appendSequenceNumber(0); 37 | n1 = Name(n1.toUri() + "/sha256digest"); 38 | 39 | BOOST_CHECK_EQUAL(IoUtil::findType(n1), 0); 40 | 41 | Name n2("NTORRENT/linux/file0"); 42 | n2.appendSequenceNumber(0); 43 | n2 = Name(n2.toUri() + "/sha256digest"); 44 | 45 | BOOST_CHECK_EQUAL(IoUtil::findType(n2), 1); 46 | 47 | Name n3("NTORRENT/linux/file0"); 48 | n3.appendSequenceNumber(0); 49 | n3.appendSequenceNumber(0); 50 | n3 = Name(n3.toUri() + "/sha256digest"); 51 | 52 | BOOST_CHECK_EQUAL(IoUtil::findType(n3), 2); 53 | } 54 | 55 | BOOST_AUTO_TEST_SUITE_END() 56 | 57 | } // namespace tests 58 | } // namespace nTorrent 59 | } // namespace ndn 60 | -------------------------------------------------------------------------------- /src/stats-table.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #include "stats-table.hpp" 23 | 24 | namespace ndn { 25 | namespace ntorrent { 26 | 27 | StatsTable::StatsTable(const Name& torrentName) 28 | : m_torrentName(torrentName) 29 | { 30 | } 31 | 32 | void 33 | StatsTable::insert(const Name& prefix) 34 | { 35 | m_statsTable.push_back(StatsTableRecord(prefix)); 36 | } 37 | 38 | StatsTable::const_iterator 39 | StatsTable::find(const Name& prefix) const 40 | { 41 | for (auto i = m_statsTable.begin(); i != m_statsTable.end(); ++i) { 42 | if (i->getRecordName() == prefix) { 43 | return i; 44 | } 45 | } 46 | return StatsTable::end(); 47 | } 48 | 49 | StatsTable::iterator 50 | StatsTable::find(const Name& prefix) 51 | { 52 | for (auto i = m_statsTable.begin(); i != m_statsTable.end(); ++i) { 53 | if (i->getRecordName() == prefix) { 54 | return i; 55 | } 56 | } 57 | return StatsTable::end(); 58 | } 59 | 60 | bool 61 | StatsTable::erase(const Name& prefix) 62 | { 63 | for (auto i = m_statsTable.begin(); i != m_statsTable.end(); ++i) { 64 | if (i->getRecordName() == prefix) { 65 | m_statsTable.erase(i); 66 | return true; 67 | } 68 | } 69 | return false; 70 | } 71 | 72 | } // namespace ntorrent 73 | } // namespace ndn 74 | -------------------------------------------------------------------------------- /tests/unit-tests/unit-test-time-fixture.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #ifndef NDN_TESTS_UNIT_TESTS_UNIT_TEST_TIME_FIXTURE_HPP 23 | #define NDN_TESTS_UNIT_TESTS_UNIT_TEST_TIME_FIXTURE_HPP 24 | 25 | #include 26 | 27 | #include 28 | 29 | namespace ndn { 30 | namespace ntorrent { 31 | namespace tests { 32 | 33 | class UnitTestTimeFixture 34 | { 35 | public: 36 | UnitTestTimeFixture() 37 | : steadyClock(make_shared()) 38 | , systemClock(make_shared()) 39 | { 40 | time::setCustomClocks(steadyClock, systemClock); 41 | } 42 | 43 | ~UnitTestTimeFixture() 44 | { 45 | time::setCustomClocks(nullptr, nullptr); 46 | } 47 | 48 | void 49 | advanceClocks(const time::nanoseconds& tick, size_t nTicks = 1) 50 | { 51 | for (size_t i = 0; i < nTicks; ++i) { 52 | steadyClock->advance(tick); 53 | systemClock->advance(tick); 54 | 55 | if (io.stopped()) 56 | io.reset(); 57 | io.poll(); 58 | } 59 | } 60 | 61 | public: 62 | shared_ptr steadyClock; 63 | shared_ptr systemClock; 64 | boost::asio::io_service io; 65 | }; 66 | 67 | } // namespace tests 68 | } // namespace ntorrent 69 | } // namespace ndn 70 | 71 | #endif // NDN_TESTS_UNIT_TESTS_UNIT_TEST_TIME_FIXTURE_HPP 72 | -------------------------------------------------------------------------------- /src/util/logging.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | #ifndef UTIL_LOGGING_HPP 22 | #define UTIL_LOGGING_HPP 23 | 24 | #define BOOST_LOG_DYN_LINK 1 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | // register a global logger 34 | BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(logger, boost::log::sources::severity_logger_mt) 35 | 36 | // just a helper macro used by the macros below - don't use it in your code 37 | #define LOG(severity) BOOST_LOG_SEV(logger::get(), boost::log::trivial::severity) 38 | 39 | // ===== log macros ===== 40 | #define LOG_TRACE std::cerr 41 | #define LOG_DEBUG std::cerr 42 | #define LOG_INFO std::cerr 43 | #define LOG_WARNING std::cerr 44 | #define LOG_ERROR std::cerr 45 | #define LOG_FATAL std::cerr 46 | 47 | namespace ndn { 48 | namespace ntorrent { 49 | 50 | namespace log = boost::log::trivial; 51 | 52 | struct LoggingUtil { 53 | static log::severity_level severity_threshold; 54 | 55 | static void init(bool log_to_console = false); 56 | // Initialize the log for the application. THis method must be called in the main function in 57 | // the application before any logging may be performed. 58 | }; 59 | 60 | } // end ntorrent 61 | } // end ndn 62 | #endif // UTIL_LOGGING_HPP 63 | -------------------------------------------------------------------------------- /.waf-tools/cryptopp.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # encoding: utf-8 3 | 4 | ''' 5 | 6 | When using this tool, the wscript will look like: 7 | 8 | def options(opt): 9 | opt.tool_options('cryptopp', tooldir=["waf-tools"]) 10 | 11 | def configure(conf): 12 | conf.load('compiler_cxx cryptopp') 13 | 14 | def build(bld): 15 | bld(source='main.cpp', target='app', use='CRYPTOPP') 16 | 17 | Options are generated, in order to specify the location of cryptopp includes/libraries. 18 | 19 | 20 | ''' 21 | import sys 22 | import re 23 | from waflib import Utils,Logs,Errors 24 | from waflib.Configure import conf 25 | CRYPTOPP_DIR=['/usr','/usr/local','/opt/local','/sw'] 26 | CRYPTOPP_VERSION_FILE='config.h' 27 | CRYPTOPP_VERSION_CODE=''' 28 | #include 29 | #include 30 | int main() { std::cout << CRYPTOPP_VERSION; } 31 | ''' 32 | 33 | def options(opt): 34 | opt.add_option('--cryptopp',type='string',default='',dest='cryptopp_dir',help='''path to where cryptopp is installed, e.g. /opt/local''') 35 | @conf 36 | def __cryptopp_get_version_file(self,dir): 37 | try: 38 | return self.root.find_dir(dir).find_node('%s/%s' % ('include/cryptopp', CRYPTOPP_VERSION_FILE)) 39 | except: 40 | return None 41 | @conf 42 | def cryptopp_get_version(self,dir): 43 | val=self.check_cxx(fragment=CRYPTOPP_VERSION_CODE,includes=['%s/%s' % (dir, 'include')], execute=True, define_ret = True, mandatory=True) 44 | return val 45 | @conf 46 | def cryptopp_get_root(self,*k,**kw): 47 | root=k and k[0]or kw.get('path',None) 48 | # Logs.pprint ('RED', ' %s' %root) 49 | if root and self.__cryptopp_get_version_file(root): 50 | return root 51 | for dir in CRYPTOPP_DIR: 52 | if self.__cryptopp_get_version_file(dir): 53 | return dir 54 | if root: 55 | self.fatal('CryptoPP not found in %s'%root) 56 | else: 57 | self.fatal('CryptoPP not found, please provide a --cryptopp argument (see help)') 58 | @conf 59 | def check_cryptopp(self,*k,**kw): 60 | if not self.env['CXX']: 61 | self.fatal('load a c++ compiler first, conf.load("compiler_cxx")') 62 | 63 | var=kw.get('uselib_store','CRYPTOPP') 64 | self.start_msg('Checking CRYPTOPP') 65 | root = self.cryptopp_get_root(*k,**kw) 66 | self.env.CRYPTOPP_VERSION=self.cryptopp_get_version(root) 67 | 68 | self.env['INCLUDES_%s'%var]= '%s/%s' % (root, "include") 69 | self.env['LIB_%s'%var] = "cryptopp" 70 | self.env['LIBPATH_%s'%var] = '%s/%s' % (root, "lib") 71 | 72 | self.end_msg(self.env.CRYPTOPP_VERSION) 73 | if Logs.verbose: 74 | Logs.pprint('CYAN',' CRYPTOPP include : %s'%self.env['INCLUDES_%s'%var]) 75 | Logs.pprint('CYAN',' CRYPTOPP lib : %s'%self.env['LIB_%s'%var]) 76 | Logs.pprint('CYAN',' CRYPTOPP libpath : %s'%self.env['LIBPATH_%s'%var]) 77 | -------------------------------------------------------------------------------- /.waf-tools/sphinx_build.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # inspired by code by Hans-Martin von Gaudecker, 2012 5 | 6 | import os 7 | from waflib import Node, Task, TaskGen, Errors, Logs, Build, Utils 8 | 9 | class sphinx_build(Task.Task): 10 | color = 'BLUE' 11 | run_str = '${SPHINX_BUILD} -D ${VERSION} -D ${RELEASE} -q -b ${BUILDERNAME} -d ${DOCTREEDIR} ${SRCDIR} ${OUTDIR}' 12 | 13 | def __str__(self): 14 | env = self.env 15 | src_str = ' '.join([a.path_from(a.ctx.launch_node()) for a in self.inputs]) 16 | tgt_str = ' '.join([a.path_from(a.ctx.launch_node()) for a in self.outputs]) 17 | if self.outputs: sep = ' -> ' 18 | else: sep = '' 19 | return'%s [%s]: %s%s%s\n'%(self.__class__.__name__.replace('_task',''), 20 | self.env['BUILDERNAME'], src_str, sep, tgt_str) 21 | 22 | @TaskGen.extension('.py', '.rst') 23 | def sig_hook(self, node): 24 | node.sig=Utils.h_file(node.abspath()) 25 | 26 | @TaskGen.feature("sphinx") 27 | @TaskGen.before_method("process_source") 28 | def apply_sphinx(self): 29 | """Set up the task generator with a Sphinx instance and create a task.""" 30 | 31 | inputs = [] 32 | for i in Utils.to_list(self.source): 33 | if not isinstance(i, Node.Node): 34 | node = self.path.find_node(node) 35 | else: 36 | node = i 37 | if not node: 38 | raise ValueError('[%s] file not found' % i) 39 | inputs.append(node) 40 | 41 | task = self.create_task('sphinx_build', inputs) 42 | 43 | conf = self.path.find_node(self.config) 44 | task.inputs.append(conf) 45 | 46 | confdir = conf.parent.abspath() 47 | buildername = getattr(self, "builder", "html") 48 | srcdir = getattr(self, "srcdir", confdir) 49 | outdir = self.path.find_or_declare(getattr(self, "outdir", buildername)).get_bld() 50 | doctreedir = getattr(self, "doctreedir", os.path.join(outdir.abspath(), ".doctrees")) 51 | 52 | task.env['BUILDERNAME'] = buildername 53 | task.env['SRCDIR'] = srcdir 54 | task.env['DOCTREEDIR'] = doctreedir 55 | task.env['OUTDIR'] = outdir.abspath() 56 | task.env['VERSION'] = "version=%s" % self.VERSION 57 | task.env['RELEASE'] = "release=%s" % self.VERSION 58 | 59 | import imp 60 | confData = imp.load_source('sphinx_conf', conf.abspath()) 61 | 62 | if buildername == "man": 63 | for i in confData.man_pages: 64 | target = outdir.find_or_declare('%s.%d' % (i[1], i[4])) 65 | task.outputs.append(target) 66 | 67 | if self.install_path: 68 | self.bld.install_files("%s/man%d/" % (self.install_path, i[4]), target) 69 | else: 70 | task.outputs.append(outdir) 71 | 72 | def configure(conf): 73 | conf.find_program('sphinx-build', var='SPHINX_BUILD', mandatory=False) 74 | 75 | # sphinx docs 76 | from waflib.Build import BuildContext 77 | class sphinx(BuildContext): 78 | cmd = "sphinx" 79 | fun = "sphinx" 80 | -------------------------------------------------------------------------------- /src/stats-table-record.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #include "stats-table-record.hpp" 23 | 24 | #include 25 | 26 | namespace ndn { 27 | namespace ntorrent { 28 | 29 | StatsTableRecord::StatsTableRecord(const Name& recordName) 30 | : m_recordName(recordName) 31 | , m_sentInterests(0) 32 | , m_receivedData(0) 33 | , m_successRate(0) 34 | { 35 | } 36 | 37 | StatsTableRecord::StatsTableRecord(const StatsTableRecord& record) 38 | : m_recordName(record.getRecordName()) 39 | , m_sentInterests(record.getRecordSentInterests()) 40 | , m_receivedData(record.getRecordReceivedData()) 41 | , m_successRate(record.getRecordSuccessRate()) 42 | { 43 | } 44 | 45 | void 46 | StatsTableRecord::incrementSentInterests() 47 | { 48 | ++m_sentInterests; 49 | m_successRate = m_receivedData / float(m_sentInterests); 50 | } 51 | 52 | void 53 | StatsTableRecord::incrementReceivedData() 54 | { 55 | if (m_sentInterests == 0) { 56 | BOOST_THROW_EXCEPTION(Error("Computing success rate while having sent no Interests")); 57 | } 58 | ++m_receivedData; 59 | m_successRate = m_receivedData / float(m_sentInterests); 60 | } 61 | 62 | StatsTableRecord& 63 | StatsTableRecord::operator=(const StatsTableRecord& other) 64 | { 65 | m_recordName = other.getRecordName(); 66 | m_sentInterests = other.getRecordSentInterests(); 67 | m_receivedData = other.getRecordReceivedData(); 68 | m_successRate = other.getRecordSuccessRate(); 69 | return (*this); 70 | } 71 | 72 | bool 73 | operator==(const StatsTableRecord& lhs, const StatsTableRecord& rhs) 74 | { 75 | return (lhs.getRecordName() == rhs.getRecordName() && 76 | lhs.getRecordSentInterests() == rhs.getRecordSentInterests() && 77 | lhs.getRecordReceivedData() == rhs.getRecordReceivedData()); 78 | } 79 | 80 | bool 81 | operator!=(const StatsTableRecord& lhs, const StatsTableRecord& rhs) 82 | { 83 | return (lhs.getRecordName() != rhs.getRecordName() || 84 | lhs.getRecordSentInterests() != rhs.getRecordSentInterests() || 85 | lhs.getRecordReceivedData() != rhs.getRecordReceivedData()); 86 | } 87 | 88 | } // namespace ntorrent 89 | } // namespace ndn 90 | -------------------------------------------------------------------------------- /.waf-tools/default-compiler-flags.py: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- 2 | 3 | from waflib import Logs, Configure 4 | 5 | def options(opt): 6 | opt.add_option('--debug', '--with-debug', action='store_true', default=False, dest='debug', 7 | help='''Compile in debugging mode without optimizations (-O0 or -Og)''') 8 | 9 | def configure(conf): 10 | areCustomCxxflagsPresent = (len(conf.env.CXXFLAGS) > 0) 11 | defaultFlags = ['-std=c++0x', '-std=c++11', 12 | '-stdlib=libc++', # clang on OSX < 10.9 by default uses gcc's 13 | # libstdc++, which is not C++11 compatible 14 | '-Wall', '-Wno-nested-anon-types'] 15 | 16 | if conf.options.debug: 17 | conf.define('_DEBUG', 1) 18 | conf.env['_DEBUG'] = 1 19 | defaultFlags += ['-O0', 20 | '-Og', # gcc >= 4.8 21 | '-g3', 22 | '-fcolor-diagnostics', # clang 23 | '-fdiagnostics-color', # gcc >= 4.9 24 | '-Werror', 25 | '-Wno-unused-variable', 26 | '-Wno-error=deprecated-register', 27 | '-Wno-error=maybe-uninitialized', # Bug #1615 28 | '-Wno-error=unneeded-internal-declaration', # Bug #1588 29 | ] 30 | if areCustomCxxflagsPresent: 31 | missingFlags = [x for x in defaultFlags if x not in conf.env.CXXFLAGS] 32 | if len(missingFlags) > 0: 33 | Logs.warn("Selected debug mode, but CXXFLAGS is set to a custom value '%s'" 34 | % " ".join(conf.env.CXXFLAGS)) 35 | Logs.warn("Default flags '%s' are not activated" % " ".join(missingFlags)) 36 | else: 37 | conf.add_supported_cxxflags(defaultFlags) 38 | else: 39 | defaultFlags += ['-O2', '-g'] 40 | if not areCustomCxxflagsPresent: 41 | conf.add_supported_cxxflags(defaultFlags) 42 | 43 | # clang on OSX < 10.9 by default uses gcc's libstdc++, which is not C++11 compatible 44 | conf.add_supported_linkflags(['-stdlib=libc++']) 45 | 46 | @Configure.conf 47 | def add_supported_cxxflags(self, cxxflags): 48 | """ 49 | Check which cxxflags are supported by compiler and add them to env.CXXFLAGS variable 50 | """ 51 | self.start_msg('Checking supported CXXFLAGS') 52 | 53 | supportedFlags = [] 54 | for flag in cxxflags: 55 | if self.check_cxx(cxxflags=['-Werror', flag], mandatory=False): 56 | supportedFlags += [flag] 57 | 58 | self.end_msg(' '.join(supportedFlags)) 59 | self.env.CXXFLAGS = supportedFlags + self.env.CXXFLAGS 60 | 61 | @Configure.conf 62 | def add_supported_linkflags(self, linkflags): 63 | """ 64 | Check which linkflags are supported by compiler and add them to env.LINKFLAGS variable 65 | """ 66 | self.start_msg('Checking supported LINKFLAGS') 67 | 68 | supportedFlags = [] 69 | for flag in linkflags: 70 | if self.check_cxx(linkflags=['-Werror', flag], mandatory=False): 71 | supportedFlags += [flag] 72 | 73 | self.end_msg(' '.join(supportedFlags)) 74 | self.env.LINKFLAGS = supportedFlags + self.env.LINKFLAGS 75 | -------------------------------------------------------------------------------- /src/interest-queue.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | typedef std::tuple, ndn::DataCallback, ndn::TimeoutCallback> queueTuple; 30 | 31 | namespace ndn { 32 | namespace ntorrent { 33 | 34 | class InterestQueue 35 | { 36 | public: 37 | InterestQueue() = default; 38 | 39 | ~InterestQueue() = default; 40 | 41 | /** 42 | * @brief Push a tuple to the Interest Queue 43 | * @param interest A shared pointer to an Interest 44 | * @param dataReceivedCallback Callback to be called when data is received for the given 45 | * Interest 46 | * @param dataFailedCallback Callback to be called when we fail to retrieve data for the 47 | * given Interest 48 | * 49 | */ 50 | void 51 | push(shared_ptr interest, DataCallback dataReceivedCallback, 52 | TimeoutCallback dataFailedCallback); 53 | 54 | /** 55 | * @brief Pop a tuple from the Interest Queue 56 | * @return A tuple of a shared pointer to an Interest, a callaback for successful data 57 | * retrieval and a callback for failed data retrieval 58 | */ 59 | queueTuple 60 | pop(); 61 | 62 | /** 63 | * @brief Return the size of the queue (number of tuples) 64 | * @return The number of tuples stored in the queue 65 | */ 66 | size_t 67 | size() const; 68 | 69 | /** 70 | * @brief Check if the queue is empty 71 | * @return True if the queue is empty, otherwise false 72 | */ 73 | bool 74 | empty() const; 75 | 76 | /** 77 | * @brief Return the top element of the Interest queue 78 | * @return The top tuple element of the Interest queue 79 | */ 80 | queueTuple 81 | front() const; 82 | 83 | private: 84 | std::queue m_queue; 85 | }; 86 | 87 | inline size_t 88 | InterestQueue::size() const 89 | { 90 | return m_queue.size(); 91 | } 92 | 93 | inline bool 94 | InterestQueue::empty() const 95 | { 96 | return m_queue.empty(); 97 | } 98 | 99 | inline queueTuple 100 | InterestQueue::front() const 101 | { 102 | return m_queue.front(); 103 | } 104 | 105 | } // namespace ntorrent 106 | } // namespace ndn 107 | -------------------------------------------------------------------------------- /src/fetching-strategy-manager.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #ifndef FETCHING_STRATEGY_MANAGER_HPP 23 | #define FETCHING_STRATEGY_MANAGER_HPP 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | namespace ndn { 30 | namespace ntorrent { 31 | 32 | class FetchingStrategyManager { 33 | public: 34 | /** 35 | * @brief Default Constructor 36 | */ 37 | FetchingStrategyManager() = default; 38 | 39 | /** 40 | * @brief Class Destructor 41 | */ 42 | virtual 43 | ~FetchingStrategyManager(); 44 | 45 | /** 46 | * @brief Method called to start the torrent downloading 47 | */ 48 | virtual void 49 | start(const time::milliseconds& timeout = time::milliseconds::zero()) = 0; 50 | 51 | /** 52 | * @brief Method called to pause the torrent downloading 53 | */ 54 | virtual void 55 | pause() = 0; 56 | 57 | /** 58 | * @brief Method called to continue the torrent downloading 59 | */ 60 | virtual void 61 | resume() = 0; 62 | 63 | /** 64 | * @brief Struct representing the status of the downloading 65 | * process that will be passed to the application layer 66 | */ 67 | struct status { 68 | double downloadedPercent; 69 | }; 70 | private: 71 | /** 72 | * @brief Callback to be called when data is received 73 | */ 74 | virtual void 75 | onDataPacketReceived(const ndn::Name& name) = 0; 76 | 77 | /** 78 | * @brief Callback to be called when data retrieval failed 79 | */ 80 | virtual void 81 | onDataRetrievalFailure(const ndn::Name& name, const std::string& errorCode) = 0; 82 | 83 | /** 84 | * @brief Callback to be called when a torrent file segment is received 85 | */ 86 | virtual void 87 | onTorrentFileSegmentReceived(const std::vector& manifestNames) = 0; 88 | 89 | /** 90 | * @brief Callback to be called when a file manifest is received 91 | */ 92 | virtual void 93 | onManifestReceived(const std::vector& packetNames) = 0; 94 | }; 95 | 96 | inline 97 | FetchingStrategyManager::~FetchingStrategyManager() 98 | { 99 | } 100 | 101 | } // namespace ntorrent 102 | } // namespace ndn 103 | 104 | #endif // FETCHING_STRATEGY_MANAGER_HPP 105 | -------------------------------------------------------------------------------- /tests/unit-tests/dummy-parser-fixture.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #ifndef NDN_TESTS_UNIT_TESTS_DUMMY_PARSER_HPP 23 | #define NDN_TESTS_UNIT_TESTS_DUMMY_PARSER_HPP 24 | 25 | #include 26 | 27 | #include 28 | 29 | namespace ndn { 30 | namespace ntorrent { 31 | namespace tests { 32 | 33 | using std::vector; 34 | 35 | class DummyParser { 36 | public: 37 | DummyParser() 38 | { 39 | } 40 | 41 | ~DummyParser() 42 | { 43 | } 44 | 45 | static shared_ptr 46 | createDataPacket(const Name& packetName, const std::vector& vec) 47 | { 48 | shared_ptr data = make_shared(packetName); 49 | 50 | EncodingEstimator estimator; 51 | size_t estimatedSize = encodeContent(estimator, vec); 52 | 53 | EncodingBuffer buffer(estimatedSize, 0); 54 | encodeContent(buffer, vec); 55 | 56 | data->setContentType(tlv::ContentType_Blob); 57 | data->setContent(buffer.block()); 58 | 59 | return data; 60 | } 61 | 62 | static shared_ptr> 63 | decodeContent(const Block& content) 64 | { 65 | shared_ptr> nameVec = make_shared>(); 66 | content.parse(); 67 | // Decode the names (do not worry about the order) 68 | for (auto element = content.elements_begin(); element != content.elements_end(); element++) { 69 | element->parse(); 70 | Name name(*element); 71 | nameVec->push_back(name); 72 | } 73 | return nameVec; 74 | } 75 | private: 76 | template 77 | static size_t 78 | encodeContent(EncodingImpl& encoder, const std::vector& vec) 79 | { 80 | // Content ::= CONTENT-TYPE TLV-LENGTH 81 | // RoutableName+ 82 | 83 | // RoutableName ::= NAME-TYPE TLV-LENGTH 84 | // Name 85 | 86 | size_t totalLength = 0; 87 | for (const auto& element : vec) { 88 | size_t nameLength = 0; 89 | nameLength += element.wireEncode(encoder); 90 | totalLength += nameLength; 91 | } 92 | totalLength += encoder.prependVarNumber(totalLength); 93 | totalLength += encoder.prependVarNumber(tlv::Content); 94 | return totalLength; 95 | } 96 | }; 97 | 98 | } // namespace tests 99 | } // namespace ntorrent 100 | } // namespace ndn 101 | 102 | #endif // NDN_TESTS_UNIT_TESTS_DUMMY_PARSER_HPP 103 | -------------------------------------------------------------------------------- /src/sequential-data-fetcher.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #ifndef SEQUENTIAL_DATA_FETCHER_HPP 23 | #define SEQUENTIAL_DATA_FETCHER_HPP 24 | 25 | #include "fetching-strategy-manager.hpp" 26 | #include "torrent-manager.hpp" 27 | 28 | #include 29 | 30 | #include 31 | 32 | namespace ndn { 33 | namespace ntorrent { 34 | 35 | class SequentialDataFetcher : FetchingStrategyManager { 36 | public: 37 | class Error : public std::runtime_error 38 | { 39 | public: 40 | explicit 41 | Error(const std::string& what) 42 | : std::runtime_error(what) 43 | { 44 | } 45 | }; 46 | /** 47 | * @brief Create a new SequentialDataFetcher 48 | * @param torrentFileName The name of the torrent file 49 | * @param dataPath The path that the manager would look for already stored data packets and 50 | * will write new data packets 51 | */ 52 | SequentialDataFetcher(const ndn::Name& torrentFileName, 53 | const std::string& dataPath, 54 | bool seed = true); 55 | 56 | ~SequentialDataFetcher(); 57 | 58 | /** 59 | * @brief Start the sequential data fetcher 60 | */ 61 | void 62 | start(const time::milliseconds& timeout = time::milliseconds::zero()); 63 | 64 | /** 65 | * @brief Pause the sequential data fetcher 66 | */ 67 | void 68 | pause(); 69 | 70 | /** 71 | * @brief Resume the sequential data fetcher 72 | */ 73 | void 74 | resume(); 75 | 76 | protected: 77 | void 78 | downloadTorrentFile(); 79 | 80 | void 81 | downloadManifestFiles(const std::vector& manifestsName); 82 | 83 | void 84 | downloadPackets(const std::vector& packetsName); 85 | 86 | void 87 | implementSequentialLogic(); 88 | 89 | virtual void 90 | onDataPacketReceived(const ndn::Name& name); 91 | 92 | virtual void 93 | onDataRetrievalFailure(const ndn::Name& name, const std::string& errorCode); 94 | 95 | virtual void 96 | onManifestReceived(const std::vector& packetNames); 97 | 98 | virtual void 99 | onTorrentFileSegmentReceived(const std::vector& manifestNames); 100 | 101 | private: 102 | enum 103 | { 104 | MAX_RETRIES = 5 105 | }; 106 | std::unordered_map m_retryMap; 107 | std::string m_dataPath; 108 | ndn::Name m_torrentFileName; 109 | shared_ptr m_manager; 110 | bool m_seedFlag; 111 | }; 112 | 113 | } // namespace ntorrent 114 | } // namespace ndn 115 | 116 | #endif // SEQUENTIAL_DATA_FETCHER_HPP 117 | -------------------------------------------------------------------------------- /src/util/logging.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | 23 | #include "util/logging.hpp" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | // ===== log macros ===== 37 | namespace logging = boost::log; 38 | namespace src = boost::log::sources; 39 | namespace sinks = boost::log::sinks; 40 | namespace keywords = boost::log::keywords; 41 | namespace expr = boost::log::expressions; 42 | 43 | namespace ndn { 44 | namespace ntorrent { 45 | 46 | log::severity_level LoggingUtil::severity_threshold = log::info; 47 | 48 | void LoggingUtil::init(bool log_to_console) 49 | { 50 | // set logging level 51 | logging::core::get()->set_filter 52 | ( 53 | logging::trivial::severity >= severity_threshold 54 | ); 55 | 56 | boost::shared_ptr< logging::core > core = logging::core::get(); 57 | 58 | auto backend = 59 | boost::make_shared< sinks::text_file_backend >( 60 | keywords::file_name = "sample_%N.log", // < file name pattern > 61 | keywords::rotation_size = 10 * 1024 * 1024, // < rotate files every 10 MiB... > 62 | keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0) // < ...or at midnight > 63 | ); 64 | // Enable auto-flushing after each log record written 65 | backend->auto_flush(true); 66 | 67 | // Wrap it into the frontend and register in the core. 68 | // The backend requires synchronization in the frontend. 69 | typedef sinks::synchronous_sink< sinks::text_file_backend > sink_t; 70 | boost::shared_ptr< sink_t > sink(new sink_t(backend)); 71 | sink->set_formatter( 72 | expr::stream 73 | << expr::attr< unsigned int >("LineID") 74 | << ": [" << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S") << "]" 75 | << ": <" << logging::trivial::severity 76 | << "> " << expr::smessage 77 | ); 78 | core->add_sink(sink); 79 | 80 | if (log_to_console) { 81 | logging::add_console_log(std::cerr, 82 | keywords::format = 83 | ( 84 | expr::stream 85 | << "[" << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S") << "]" 86 | << ": <" << logging::trivial::severity 87 | << "> " << expr::smessage 88 | ) 89 | ); 90 | } 91 | } 92 | 93 | } // end ntorrent 94 | } // end ndn 95 | 96 | 97 | -------------------------------------------------------------------------------- /src/stats-table-record.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #include 23 | 24 | namespace ndn { 25 | namespace ntorrent { 26 | 27 | /** 28 | * @brief Represents a record of the stats table 29 | */ 30 | class StatsTableRecord { 31 | public: 32 | class Error : public std::runtime_error 33 | { 34 | public: 35 | explicit 36 | Error(const std::string& what) 37 | : std::runtime_error(what) 38 | { 39 | } 40 | }; 41 | 42 | /** 43 | * @brief Create a new empty record 44 | */ 45 | StatsTableRecord() = default; 46 | 47 | /** 48 | * @brief Create a new record 49 | * @param recordName The name of this record 50 | */ 51 | StatsTableRecord(const Name& recordName); 52 | 53 | /** 54 | * @brief Copy constructor 55 | * @param record An StatsTableRecord object 56 | */ 57 | StatsTableRecord(const StatsTableRecord& record); 58 | 59 | ~StatsTableRecord() = default; 60 | 61 | /** 62 | * @brief Get the name of a record 63 | */ 64 | const Name& 65 | getRecordName() const; 66 | 67 | /** 68 | * @brief Get the number of sent interests of a record 69 | */ 70 | uint64_t 71 | getRecordSentInterests() const; 72 | 73 | /** 74 | * @brief Get the number of received data packets of a record 75 | */ 76 | uint64_t 77 | getRecordReceivedData() const; 78 | 79 | /** 80 | * @brief Get the success rate of a record 81 | */ 82 | double 83 | getRecordSuccessRate() const; 84 | 85 | /** 86 | * @brief Increment the number of sent interests for a record 87 | */ 88 | void 89 | incrementSentInterests(); 90 | 91 | /** 92 | * @brief Increment the number of received data packets for a record 93 | */ 94 | void 95 | incrementReceivedData(); 96 | 97 | /** 98 | * @brief Assignment operator 99 | */ 100 | StatsTableRecord& 101 | operator=(const StatsTableRecord& other); 102 | 103 | private: 104 | Name m_recordName; 105 | uint64_t m_sentInterests; 106 | uint64_t m_receivedData; 107 | double m_successRate; 108 | }; 109 | 110 | /** 111 | * @brief Equality operator 112 | */ 113 | bool 114 | operator==(const StatsTableRecord& lhs, const StatsTableRecord& rhs); 115 | 116 | /** 117 | * @brief Inequality operator 118 | */ 119 | bool 120 | operator!=(const StatsTableRecord& lhs, const StatsTableRecord& rhs); 121 | 122 | inline const Name& 123 | StatsTableRecord::getRecordName() const 124 | { 125 | return m_recordName; 126 | } 127 | 128 | inline uint64_t 129 | StatsTableRecord::getRecordSentInterests() const 130 | { 131 | return m_sentInterests; 132 | } 133 | 134 | inline uint64_t 135 | StatsTableRecord::getRecordReceivedData() const 136 | { 137 | return m_receivedData; 138 | } 139 | 140 | inline double 141 | StatsTableRecord::getRecordSuccessRate() const 142 | { 143 | return m_successRate; 144 | } 145 | 146 | 147 | } // namespace ntorrent 148 | } // namespace ndn 149 | -------------------------------------------------------------------------------- /tests/unit-tests/stats-table-record.t.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #include "boost-test.hpp" 23 | #include "stats-table-record.hpp" 24 | 25 | #include 26 | 27 | namespace ndn { 28 | namespace ntorrent { 29 | namespace tests { 30 | 31 | BOOST_AUTO_TEST_SUITE(TestStatsTableRecord) 32 | 33 | BOOST_AUTO_TEST_CASE(TestCoreAPI) 34 | { 35 | StatsTableRecord record(Name("isp1")); 36 | 37 | BOOST_CHECK_EQUAL(record.getRecordName().toUri(), "/isp1"); 38 | BOOST_CHECK_EQUAL(record.getRecordSentInterests(), 0); 39 | BOOST_CHECK_EQUAL(record.getRecordReceivedData(), 0); 40 | BOOST_CHECK_EQUAL(record.getRecordSuccessRate(), 0); 41 | } 42 | 43 | BOOST_AUTO_TEST_CASE(TestIncrement) 44 | { 45 | StatsTableRecord record(Name("isp1")); 46 | record.incrementSentInterests(); 47 | 48 | BOOST_CHECK_EQUAL(record.getRecordName().toUri(), "/isp1"); 49 | BOOST_CHECK_EQUAL(record.getRecordSentInterests(), 1); 50 | BOOST_CHECK_EQUAL(record.getRecordReceivedData(), 0); 51 | BOOST_CHECK_EQUAL(record.getRecordSuccessRate(), 0); 52 | 53 | record.incrementReceivedData(); 54 | 55 | BOOST_CHECK_EQUAL(record.getRecordName().toUri(), "/isp1"); 56 | BOOST_CHECK_EQUAL(record.getRecordSentInterests(), 1); 57 | BOOST_CHECK_EQUAL(record.getRecordReceivedData(), 1); 58 | BOOST_CHECK_EQUAL(record.getRecordSuccessRate(), 1); 59 | } 60 | 61 | BOOST_AUTO_TEST_CASE(TestIncrementThrow) 62 | { 63 | StatsTableRecord record(Name("isp1")); 64 | BOOST_REQUIRE_THROW(record.incrementReceivedData(), StatsTableRecord::Error); 65 | 66 | BOOST_CHECK_EQUAL(record.getRecordName().toUri(), "/isp1"); 67 | BOOST_CHECK_EQUAL(record.getRecordSentInterests(), 0); 68 | BOOST_CHECK_EQUAL(record.getRecordReceivedData(), 0); 69 | BOOST_CHECK_EQUAL(record.getRecordSuccessRate(), 0); 70 | } 71 | 72 | BOOST_AUTO_TEST_CASE(TestEqualityOperator) 73 | { 74 | StatsTableRecord record1(Name("isp1")); 75 | record1.incrementSentInterests(); 76 | record1.incrementReceivedData(); 77 | 78 | StatsTableRecord record2(Name("isp1")); 79 | record2.incrementSentInterests(); 80 | record2.incrementReceivedData(); 81 | 82 | BOOST_CHECK(record1 == record2); 83 | } 84 | 85 | BOOST_AUTO_TEST_CASE(TestInequalityOperator) 86 | { 87 | StatsTableRecord record1(Name("isp2")); 88 | record1.incrementSentInterests(); 89 | record1.incrementReceivedData(); 90 | 91 | StatsTableRecord record2(Name("isp1")); 92 | record2.incrementSentInterests(); 93 | record2.incrementReceivedData(); 94 | 95 | BOOST_CHECK(record1 != record2); 96 | 97 | StatsTableRecord record3(Name("isp1")); 98 | record3.incrementSentInterests(); 99 | 100 | BOOST_CHECK(record2 != record3); 101 | } 102 | 103 | BOOST_AUTO_TEST_CASE(TestCopyConstructor) 104 | { 105 | StatsTableRecord record1(Name("isp2")); 106 | record1.incrementSentInterests(); 107 | record1.incrementReceivedData(); 108 | 109 | StatsTableRecord record2(record1); 110 | 111 | BOOST_CHECK(record1 == record2); 112 | 113 | record2.incrementSentInterests(); 114 | 115 | BOOST_CHECK(record1 != record2); 116 | 117 | record1.incrementSentInterests(); 118 | 119 | BOOST_CHECK(record1 == record2); 120 | } 121 | 122 | BOOST_AUTO_TEST_SUITE_END() 123 | 124 | } // namespace tests 125 | } // namespace ntorrent 126 | } // namespace ndn 127 | -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- 2 | VERSION='0.1.0' 3 | APPNAME='nTorrent' 4 | 5 | from waflib import Configure, Utils, Logs, Context 6 | import os 7 | 8 | def options(opt): 9 | 10 | opt.load(['compiler_c', 'compiler_cxx', 'gnu_dirs']) 11 | 12 | opt.load(['default-compiler-flags', 'coverage', 'boost', 13 | 'doxygen', 'sphinx_build'], 14 | tooldir=['.waf-tools']) 15 | 16 | opt = opt.add_option_group('nTorrent Options') 17 | 18 | opt.add_option('--with-tests', action='store_true', default=False, dest='with_tests', 19 | help='''build unit tests''') 20 | 21 | def configure(conf): 22 | conf.load(['compiler_c', 'compiler_cxx', 23 | 'default-compiler-flags', 'boost', 'gnu_dirs', 24 | 'doxygen', 'sphinx_build']) 25 | 26 | if 'PKG_CONFIG_PATH' not in os.environ: 27 | os.environ['PKG_CONFIG_PATH'] = Utils.subst_vars('${LIBDIR}/pkgconfig', conf.env) 28 | 29 | conf.check_cfg(package='libndn-cxx', args=['--cflags', '--libs'], 30 | uselib_store='NDN_CXX', mandatory=True) 31 | 32 | boost_libs = 'system random thread filesystem log log_setup' 33 | if conf.options.with_tests: 34 | conf.env['WITH_TESTS'] = 1 35 | conf.define('WITH_TESTS', 1); 36 | boost_libs += ' unit_test_framework' 37 | 38 | conf.check_boost(lib=boost_libs, mt=True) 39 | if conf.env.BOOST_VERSION_NUMBER < 104800: 40 | Logs.error("Minimum required boost version is 1.48.0") 41 | Logs.error("Please upgrade your distribution or install custom boost libraries" + 42 | " (http://redmine.named-data.net/projects/nfd/wiki/Boost_FAQ)") 43 | return 44 | 45 | # Loading "late" to prevent tests to be compiled with profiling flags 46 | conf.load('coverage') 47 | 48 | conf.write_config_header('src/config.h') 49 | 50 | def build (bld): 51 | bld( 52 | features='cxx', 53 | name='nTorrent', 54 | source=bld.path.ant_glob(['src/**/*.cpp'], 55 | excl=['src/main.cpp',]), 56 | use='version NDN_CXX BOOST', 57 | includes='src', 58 | export_includes='src', 59 | ) 60 | # main 61 | bld( 62 | target='ntorrent', 63 | features='cxx cxxprogram', 64 | source='src/main.cpp', 65 | use = 'nTorrent') 66 | 67 | # Unit tests 68 | if bld.env["WITH_TESTS"]: 69 | unittests = bld.program ( 70 | target="unit-tests", 71 | source = bld.path.ant_glob(['tests/**/*.cpp']), 72 | features=['cxx', 'cxxprogram'], 73 | use = 'nTorrent', 74 | includes = "src .", 75 | install_path = None 76 | ) 77 | 78 | # docs 79 | def docs(bld): 80 | from waflib import Options 81 | Options.commands = ['doxygen', 'sphinx'] + Options.commands 82 | 83 | def doxygen(bld): 84 | version(bld) 85 | 86 | if not bld.env.DOXYGEN: 87 | Logs.error("ERROR: cannot build documentation (`doxygen' is not found in $PATH)") 88 | else: 89 | bld(features="subst", 90 | name="doxygen-conf", 91 | source=["docs/doxygen.conf.in", 92 | "docs/named_data_theme/named_data_footer-with-analytics.html.in"], 93 | target=["docs/doxygen.conf", 94 | "docs/named_data_theme/named_data_footer-with-analytics.html"], 95 | VERSION=VERSION, 96 | HTML_FOOTER="../build/docs/named_data_theme/named_data_footer-with-analytics.html" \ 97 | if os.getenv('GOOGLE_ANALYTICS', None) \ 98 | else "../docs/named_data_theme/named_data_footer.html", 99 | GOOGLE_ANALYTICS=os.getenv('GOOGLE_ANALYTICS', ""), 100 | ) 101 | 102 | bld(features="doxygen", 103 | doxyfile='docs/doxygen.conf', 104 | use="doxygen-conf") 105 | 106 | def sphinx(bld): 107 | version(bld) 108 | 109 | if not bld.env.SPHINX_BUILD: 110 | bld.fatal("ERROR: cannot build documentation (`sphinx-build' is not found in $PATH)") 111 | else: 112 | bld(features="sphinx", 113 | outdir="docs", 114 | source=bld.path.ant_glob("docs/**/*.rst"), 115 | config="docs/conf.py", 116 | VERSION=VERSION) 117 | 118 | def version(ctx): 119 | if getattr(Context.g_module, 'VERSION_BASE', None): 120 | return 121 | 122 | Context.g_module.VERSION_BASE = Context.g_module.VERSION 123 | Context.g_module.VERSION_SPLIT = [v for v in VERSION_BASE.split('.')] 124 | 125 | try: 126 | cmd = ['git', 'describe', '--match', 'nTorrent-*'] 127 | p = Utils.subprocess.Popen(cmd, stdout=Utils.subprocess.PIPE, 128 | stderr=None, stdin=None) 129 | out = p.communicate()[0].strip() 130 | if p.returncode == 0 and out != "": 131 | Context.g_module.VERSION = out[11:] 132 | except: 133 | pass 134 | -------------------------------------------------------------------------------- /src/stats-table.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #include "stats-table-record.hpp" 23 | 24 | #include 25 | 26 | namespace ndn { 27 | namespace ntorrent { 28 | 29 | /** 30 | * @brief Represents a stats table 31 | */ 32 | class StatsTable { 33 | public: 34 | /** 35 | * @brief Create an empty stats table 36 | */ 37 | StatsTable() = default; 38 | 39 | /** 40 | * @brief Create a stats table for a specific torrent 41 | * @param torrentName The name of the torrent 42 | */ 43 | StatsTable(const Name& torrentName); 44 | 45 | ~StatsTable() = default; 46 | 47 | /** 48 | * @brief Insert a routable prefix to the stats table 49 | * @param prefix The prefix to be inserted 50 | */ 51 | void 52 | insert(const Name& prefix); 53 | 54 | /** 55 | * @brief Erase a prefix from the statsTable 56 | * @param prefix The prefix to be erased 57 | * @return True if the prefix was found and erased. Otherwise, false 58 | */ 59 | bool 60 | erase(const Name& prefix); 61 | 62 | /** 63 | * @brief Clear the stats table 64 | */ 65 | void 66 | clear(); 67 | 68 | /** 69 | * @brief Return the size of the stats table (number of prefixes included) 70 | */ 71 | size_t 72 | size() const; 73 | 74 | typedef std::vector::const_iterator const_iterator; 75 | typedef std::vector::iterator iterator; 76 | 77 | /** 78 | * @brief Constant iterator to the beginning of the stats table 79 | */ 80 | const_iterator 81 | begin() const; 82 | 83 | /** 84 | * @brief Iterator to the beginning of the stats table 85 | */ 86 | iterator 87 | begin(); 88 | 89 | /** 90 | * @brief Constant iterator to the end of the stats table 91 | */ 92 | const_iterator 93 | end() const; 94 | 95 | /** 96 | * @brief Iterator to the end of the stats table 97 | */ 98 | iterator 99 | end(); 100 | 101 | /** 102 | * @brief Find a prefix on the stats table 103 | * @param prefix The name prefix to be searched 104 | * @return A constant iterator to the prefix if found. Otherwise, return StatsTable::end() 105 | */ 106 | const_iterator 107 | find(const Name& prefix) const; 108 | 109 | /** 110 | * @brief Find a prefix on the stats table 111 | * @param prefix The name prefix to be searched 112 | * @return An iterator to the prefix if found. Otherwise, return StatsTable::end() 113 | */ 114 | iterator 115 | find(const Name& prefix); 116 | 117 | /** 118 | * @brief Comparator used for sorting the records of the stats table 119 | */ 120 | struct comparator { 121 | bool operator() (const StatsTableRecord& left, const StatsTableRecord& right) const 122 | {return left.getRecordSuccessRate() >= right.getRecordSuccessRate();} 123 | }; 124 | 125 | /** 126 | * @brief Sort the records of the stats table on desceding success rate 127 | * @param comp Optional comparator function to be used for sorting. 128 | * The default value is the provided comparator struct 129 | * 130 | * This method has to be called manually by the application to sort the 131 | * stats table. 132 | */ 133 | void 134 | sort(std::function comp = comparator()); 135 | 136 | private: 137 | // Set of StatsTableRecords 138 | std::vector m_statsTable; 139 | Name m_torrentName; 140 | }; 141 | 142 | inline void 143 | StatsTable::clear() 144 | { 145 | m_statsTable.clear(); 146 | } 147 | 148 | inline size_t 149 | StatsTable::size() const 150 | { 151 | return m_statsTable.size(); 152 | } 153 | 154 | inline StatsTable::const_iterator 155 | StatsTable::begin() const 156 | { 157 | return m_statsTable.begin(); 158 | } 159 | 160 | inline StatsTable::iterator 161 | StatsTable::begin() 162 | { 163 | return m_statsTable.begin(); 164 | } 165 | 166 | inline StatsTable::const_iterator 167 | StatsTable::end() const 168 | { 169 | return m_statsTable.end(); 170 | } 171 | 172 | inline StatsTable::iterator 173 | StatsTable::end() 174 | { 175 | return m_statsTable.end(); 176 | } 177 | 178 | inline void 179 | StatsTable::sort(std::function comp) 180 | { 181 | std::sort(m_statsTable.begin(), m_statsTable.end(), comp); 182 | } 183 | 184 | } // namespace ntorrent 185 | } // namespace ndn 186 | -------------------------------------------------------------------------------- /src/update-handler.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #ifndef UPDATE_HANDLER_H 23 | #define UPDATE_HANDLER_H 24 | 25 | #include "stats-table.hpp" 26 | #include "util/shared-constants.hpp" 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | namespace ndn { 33 | namespace ntorrent { 34 | 35 | class UpdateHandler { 36 | public: 37 | typedef std::function OnReceivedOwnRoutablePrefix; 38 | 39 | class Error : public tlv::Error 40 | { 41 | public: 42 | explicit 43 | Error(const std::string& what) 44 | : tlv::Error(what) 45 | { 46 | } 47 | }; 48 | 49 | UpdateHandler(Name torrentName, shared_ptr keyChain, 50 | shared_ptr statsTable, shared_ptr face, 51 | OnReceivedOwnRoutablePrefix onReceivedOwnRoutablePrefix = {}); 52 | 53 | ~UpdateHandler(); 54 | 55 | /** 56 | * @brief Send an ALIVE Interest 57 | * @param routablePrefix The routable prefix to be included in the LINK object attached 58 | * to this Interest 59 | */ 60 | void 61 | sendAliveInterest(StatsTable::iterator iter); 62 | 63 | /** 64 | * @brief Check whether we need to send out an "ALIVE" interest 65 | * @return True if an "ALIVE" interest should be sent out, otherwise false 66 | * 67 | * Returns true if we have less than MIN_NUM_OF_ROUTABLE_NAMES prefixes in the stats table 68 | * or all the routable prefixes has success rate less than 0.5. Otherwise, it returns false 69 | */ 70 | bool 71 | needsUpdate(); 72 | 73 | enum { 74 | // Maximum number of names to be encoded as a response to an "ALIVE" Interest 75 | MAX_NUM_OF_ENCODED_NAMES = 5, 76 | // Minimum number of routable prefixes that the peer would like to have 77 | MIN_NUM_OF_ROUTABLE_NAMES = 5, 78 | OWN_ROUTABLE_PREFIX_RETRIES = 5, 79 | }; 80 | 81 | const Name& 82 | getOwnRoutablePrefix(); 83 | 84 | private: 85 | template 86 | size_t 87 | encodeContent(EncodingImpl& encoder) const; 88 | 89 | void 90 | onInterestReceived(const InterestFilter& filter, const Interest& interest); 91 | 92 | void 93 | onRegisterFailed(const Name& prefix, const std::string& reason); 94 | 95 | /** 96 | * @brief Encode the first MAX_NUM_OF_ENCODED_NAMES prefixes of the table into a data packet 97 | * @param name The name of the data packet 98 | * @return A shared pointer to the created data packet 99 | * 100 | */ 101 | shared_ptr 102 | createDataPacket(const Name& name); 103 | 104 | /** 105 | * @brief Given a received data packet, decode the contained routable name prefixes 106 | * and insert them to the table (if not already there) 107 | * @param interest The interest that retrieved the data packet 108 | * @param data A shared pointer to the received data packet 109 | * 110 | */ 111 | void 112 | decodeDataPacketContent(const Interest& interest, const Data& data); 113 | 114 | /** 115 | * @brief Send an Interest to the local NFD to get the routable prefixes under which the 116 | * published data is available 117 | */ 118 | void 119 | learnOwnRoutablePrefix(OnReceivedOwnRoutablePrefix onReceivedOwnRoutablePrefix); 120 | 121 | void 122 | tryNextRoutablePrefix(const Interest& interest); 123 | 124 | private: 125 | Name m_torrentName; 126 | shared_ptr m_keyChain; 127 | shared_ptr m_statsTable; 128 | shared_ptr m_face; 129 | Name m_ownRoutablePrefix; 130 | size_t m_ownRoutablPrefixRetries; 131 | }; 132 | 133 | inline 134 | UpdateHandler::UpdateHandler(Name torrentName, shared_ptr keyChain, 135 | shared_ptr statsTable, shared_ptr face, 136 | OnReceivedOwnRoutablePrefix onReceivedOwnRoutablePrefix) 137 | : m_torrentName(torrentName) 138 | , m_keyChain(keyChain) 139 | , m_statsTable(statsTable) 140 | , m_face(face) 141 | , m_ownRoutablPrefixRetries(0) 142 | { 143 | this->learnOwnRoutablePrefix(onReceivedOwnRoutablePrefix); 144 | } 145 | 146 | inline 147 | UpdateHandler::~UpdateHandler() 148 | { 149 | } 150 | 151 | inline const Name& 152 | UpdateHandler::getOwnRoutablePrefix() 153 | { 154 | return m_ownRoutablePrefix; 155 | } 156 | 157 | } // namespace ntorrent 158 | } // namespace ndn 159 | 160 | #endif // UPDATE_HANDLER_H 161 | -------------------------------------------------------------------------------- /src/util/io-util.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #ifndef INCLUDED_UTIL_IO_UTIL_H 23 | #define INCLUDED_UTIL_IO_UTIL_H 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | namespace ndn { 36 | namespace ntorrent { 37 | 38 | // Import the boost;:filesystem namespace 39 | namespace Io = boost::filesystem; 40 | 41 | class TorrentFile; 42 | class FileManifest; 43 | 44 | class IoUtil { 45 | public: 46 | /* 47 | * An enum used to identify the type of content to which a name refers 48 | */ 49 | enum NAME_TYPE 50 | { 51 | TORRENT_FILE, 52 | FILE_MANIFEST, 53 | DATA_PACKET, 54 | UNKNOWN 55 | }; 56 | 57 | template 58 | static std::vector 59 | load_directory(const std::string& dirPath, 60 | ndn::io::IoEncoding encoding = ndn::io::IoEncoding::BASE64); 61 | 62 | /* 63 | * @brief create all directories for the @p dirPath. 64 | * @param dirPath a path to a directory 65 | */ 66 | static bool create_directories(const boost::filesystem::path& dirPath); 67 | 68 | 69 | static std::vector 70 | packetize_file(const boost::filesystem::path& filePath, 71 | const ndn::Name& commonPrefix, 72 | size_t dataPacketSize, 73 | size_t subManifestSize, 74 | size_t subManifestNum); 75 | 76 | /* 77 | * @brief Write the @p segment torrent segment to disk at the specified path. 78 | * @param segment The torrent file segment to be written to disk 79 | * @param path The path at which to write the torrent file segment 80 | * Write the segment to disk, return 'true' if data successfully written to disk 'false' 81 | * otherwise. Behavior is undefined unless @segment is a correct segment for the torrent file of 82 | * this manager and @p path is the directory used for all segments of this torrent file. 83 | */ 84 | static bool 85 | writeTorrentSegment(const TorrentFile& segment, const std::string& path); 86 | 87 | /* 88 | * @brief Write the @p manifest file manifest to disk at the specified @p path. 89 | * @param manifest The file manifest to be written to disk 90 | * @param path The path at which to write the file manifest 91 | * Write the file manifest to disk, return 'true' if data successfully written to disk 'false' 92 | * otherwise. Behavior is undefined unless @manifest is a correct file manifest for a file in the 93 | * torrent file of this manager and @p path is the directory used for all file manifests of this 94 | * torrent file. 95 | */ 96 | static bool 97 | writeFileManifest(const FileManifest& manifest, const std::string& path); 98 | 99 | /* 100 | * @brief Write @p packet composed of torrent date to disk. 101 | * @param packet The data packet to be written to the disk 102 | * @param filePath The path to the file on disk to which we should write the Data 103 | * Write the Data packet to disk, return 'true' if data successfully written to disk 'false' 104 | * otherwise. Behavior is undefined unless the corresponding file manifest has already been 105 | * downloaded. 106 | */ 107 | static bool 108 | writeData(const Data& packet, 109 | const FileManifest& manifest, 110 | size_t subManifestSize, 111 | const std::string& filePath); 112 | 113 | /* 114 | * @brief Read a data packet from the provided stream 115 | * @param packetFullName The fullname of the expected Data packet 116 | * @param manifest The file manifest for the requested Data packet 117 | * @param subManifestSize The number of Data packets in each catalog for this Data packet 118 | * @param filePath The path on disk to the file containing the requested data 119 | * Read the data packet from the @p is stream, validate it against the provided @p packetFullName 120 | * and @p manifest, if successful return a pointer to the packet, otherwise return nullptr. 121 | */ 122 | static std::shared_ptr 123 | readDataPacket(const Name& packetFullName, 124 | const FileManifest& manifest, 125 | size_t subManifestSize, 126 | const std::string& filePath); 127 | 128 | /* 129 | * @brief Return the type of the specified name 130 | */ 131 | static IoUtil::NAME_TYPE 132 | findType(const Name& name); 133 | 134 | }; 135 | 136 | 137 | inline 138 | bool 139 | IoUtil::create_directories(const boost::filesystem::path& dirPath) { 140 | return boost::filesystem::create_directories(dirPath); 141 | } 142 | 143 | template 144 | inline std::vector 145 | IoUtil::load_directory(const std::string& dirPath, 146 | ndn::io::IoEncoding encoding) { 147 | std::vector structures; 148 | std::set fileNames; 149 | if (Io::exists(dirPath)) { 150 | for(Io::recursive_directory_iterator it(dirPath); 151 | it != Io::recursive_directory_iterator(); 152 | ++it) 153 | { 154 | fileNames.insert(it->path().string()); 155 | } 156 | for (const auto& f : fileNames) { 157 | auto data_ptr = ndn::io::load(f, encoding); 158 | if (nullptr != data_ptr) { 159 | structures.push_back(*data_ptr); 160 | } 161 | } 162 | } 163 | structures.shrink_to_fit(); 164 | return structures; 165 | } 166 | 167 | } // namespace ntorrent 168 | } // namespace ndn 169 | 170 | #endif // INCLUDED_UTIL_IO_UTIL_H 171 | -------------------------------------------------------------------------------- /src/sequential-data-fetcher.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #include "sequential-data-fetcher.hpp" 23 | #include "util/logging.hpp" 24 | #include "util/io-util.hpp" 25 | 26 | namespace ndn { 27 | namespace ntorrent { 28 | 29 | SequentialDataFetcher::SequentialDataFetcher(const ndn::Name& torrentFileName, 30 | const std::string& dataPath, 31 | bool seed) 32 | : m_dataPath(dataPath) 33 | , m_torrentFileName(torrentFileName) 34 | , m_seedFlag(seed) 35 | { 36 | m_manager = make_shared(m_torrentFileName, m_dataPath, seed); 37 | } 38 | 39 | SequentialDataFetcher::~SequentialDataFetcher() 40 | { 41 | } 42 | 43 | void 44 | SequentialDataFetcher::start(const time::milliseconds& timeout) 45 | { 46 | m_manager->Initialize(); 47 | // downloading logic 48 | this->implementSequentialLogic(); 49 | m_manager->processEvents(timeout); 50 | } 51 | 52 | void 53 | SequentialDataFetcher::pause() 54 | { 55 | // TODO(Spyros): Implement asyncrhonous pause of the torrent downloading 56 | // For now, do nothing... 57 | throw(Error("Not implemented yet")); 58 | } 59 | 60 | void 61 | SequentialDataFetcher::resume() 62 | { 63 | // TODO(Spyros): Implement asyncrhonous re-establishment of the torrent downloading 64 | // For now, do nothing... 65 | throw(Error("Not implemented yet")); 66 | } 67 | 68 | void 69 | SequentialDataFetcher::downloadTorrentFile() 70 | { 71 | auto torrentPath = ".appdata/" + m_torrentFileName.get(-3).toUri() + "/torrent_files/"; 72 | m_manager->downloadTorrentFile(torrentPath, 73 | bind(&SequentialDataFetcher::onTorrentFileSegmentReceived, this, _1), 74 | bind(&SequentialDataFetcher::onDataRetrievalFailure, this, _1, _2)); 75 | } 76 | 77 | void 78 | SequentialDataFetcher::downloadManifestFiles(const std::vector& manifestNames) 79 | { 80 | auto manifestPath = ".appdata/" + m_torrentFileName.get(-3).toUri() + "/manifests/"; 81 | for (auto i = manifestNames.begin(); i != manifestNames.end(); i++) { 82 | m_manager->download_file_manifest(*i, 83 | manifestPath, 84 | bind(&SequentialDataFetcher::onManifestReceived, this, _1), 85 | bind(&SequentialDataFetcher::onDataRetrievalFailure, this, _1, _2)); 86 | } 87 | } 88 | 89 | void 90 | SequentialDataFetcher::downloadPackets(const std::vector& packetsName) 91 | { 92 | for (auto i = packetsName.begin(); i != packetsName.end(); i++) { 93 | m_manager->download_data_packet(*i, 94 | bind(&SequentialDataFetcher::onDataPacketReceived, this, _1), 95 | bind(&SequentialDataFetcher::onDataRetrievalFailure, this, _1, _2)); 96 | } 97 | } 98 | 99 | void 100 | SequentialDataFetcher::implementSequentialLogic() { 101 | if (!m_manager->hasAllTorrentSegments()) { 102 | this->downloadTorrentFile(); 103 | } 104 | else { 105 | LOG_INFO << m_torrentFileName << " complete" << std::endl; 106 | std::vector namesToFetch; 107 | m_manager->findFileManifestsToDownload(namesToFetch); 108 | if (!namesToFetch.empty()) { 109 | this->downloadManifestFiles(namesToFetch); 110 | } 111 | else { 112 | LOG_INFO << "All manifests complete" << std::endl; 113 | m_manager->findAllMissingDataPackets(namesToFetch); 114 | if (!namesToFetch.empty()) { 115 | this->downloadPackets(namesToFetch); 116 | } 117 | else { 118 | LOG_INFO << "All data complete" << std::endl; 119 | if (!m_seedFlag) { 120 | m_manager->shutdown(); 121 | } 122 | } 123 | } 124 | } 125 | } 126 | 127 | void 128 | SequentialDataFetcher::onDataPacketReceived(const ndn::Name& name) 129 | { 130 | // Data Packet Received 131 | LOG_INFO << "Data Packet Received: " << name; 132 | m_retryMap.clear(); 133 | } 134 | 135 | void 136 | SequentialDataFetcher::onTorrentFileSegmentReceived(const std::vector& manifestNames) 137 | { 138 | // TODO(msweatt) Add parameter for torrent file 139 | LOG_INFO << "Torrent Segment Received: " << m_torrentFileName << std::endl; 140 | m_retryMap.clear(); 141 | this->downloadManifestFiles(manifestNames); 142 | } 143 | 144 | void 145 | SequentialDataFetcher::onManifestReceived(const std::vector& packetNames) 146 | { 147 | LOG_INFO << "Manifest File Received: " 148 | << packetNames[0].getSubName(0, packetNames[0].size()- 3) << std::endl; 149 | m_retryMap.clear(); 150 | this->downloadPackets(packetNames); 151 | } 152 | 153 | void 154 | SequentialDataFetcher::onDataRetrievalFailure(const ndn::Name& name, 155 | const std::string& errorCode) 156 | { 157 | // Data retrieval failure 158 | if (m_retryMap[name] < MAX_RETRIES) { 159 | m_retryMap[name]++; 160 | uint32_t nameType = IoUtil::findType(name); 161 | if (nameType == IoUtil::TORRENT_FILE) { 162 | // this should never happen 163 | LOG_ERROR << "Torrent File Segment Downloading Failed: " << name; 164 | this->downloadTorrentFile(); 165 | } 166 | else if (nameType == IoUtil::FILE_MANIFEST) { 167 | LOG_ERROR << "Manifest File Segment Downloading Failed: " << name; 168 | this->downloadManifestFiles({ name }); 169 | } 170 | else if (nameType == IoUtil::DATA_PACKET) { 171 | LOG_ERROR << "Data Packet Downloading Failed: " << name; 172 | this->downloadPackets({ name }); 173 | } 174 | else { 175 | // This should never happen 176 | LOG_ERROR << "Unknown Packet Type Downloading Failed: " << name; 177 | } 178 | } 179 | else { 180 | m_retryMap.erase(name); 181 | LOG_INFO << "Giving up on " << name; 182 | } 183 | } 184 | 185 | } // namespace ntorrent 186 | } // namespace ndn 187 | -------------------------------------------------------------------------------- /.waf-tools/doxygen.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # encoding: UTF-8 3 | # Thomas Nagy 2008-2010 (ita) 4 | 5 | """ 6 | 7 | Doxygen support 8 | 9 | Variables passed to bld(): 10 | * doxyfile -- the Doxyfile to use 11 | 12 | When using this tool, the wscript will look like: 13 | 14 | def options(opt): 15 | opt.load('doxygen') 16 | 17 | def configure(conf): 18 | conf.load('doxygen') 19 | # check conf.env.DOXYGEN, if it is mandatory 20 | 21 | def build(bld): 22 | if bld.env.DOXYGEN: 23 | bld(features="doxygen", doxyfile='Doxyfile', ...) 24 | 25 | def doxygen(bld): 26 | if bld.env.DOXYGEN: 27 | bld(features="doxygen", doxyfile='Doxyfile', ...) 28 | """ 29 | 30 | from fnmatch import fnmatchcase 31 | import os, os.path, re, stat 32 | from waflib import Task, Utils, Node, Logs, Errors, Build 33 | from waflib.TaskGen import feature 34 | 35 | DOXY_STR = '"${DOXYGEN}" - ' 36 | DOXY_FMTS = 'html latex man rft xml'.split() 37 | DOXY_FILE_PATTERNS = '*.' + ' *.'.join(''' 38 | c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx hpp h++ idl odl cs php php3 39 | inc m mm py f90c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx 40 | '''.split()) 41 | 42 | re_rl = re.compile('\\\\\r*\n', re.MULTILINE) 43 | re_nl = re.compile('\r*\n', re.M) 44 | def parse_doxy(txt): 45 | tbl = {} 46 | txt = re_rl.sub('', txt) 47 | lines = re_nl.split(txt) 48 | for x in lines: 49 | x = x.strip() 50 | if not x or x.startswith('#') or x.find('=') < 0: 51 | continue 52 | if x.find('+=') >= 0: 53 | tmp = x.split('+=') 54 | key = tmp[0].strip() 55 | if key in tbl: 56 | tbl[key] += ' ' + '+='.join(tmp[1:]).strip() 57 | else: 58 | tbl[key] = '+='.join(tmp[1:]).strip() 59 | else: 60 | tmp = x.split('=') 61 | tbl[tmp[0].strip()] = '='.join(tmp[1:]).strip() 62 | return tbl 63 | 64 | class doxygen(Task.Task): 65 | vars = ['DOXYGEN', 'DOXYFLAGS'] 66 | color = 'BLUE' 67 | 68 | def runnable_status(self): 69 | ''' 70 | self.pars are populated in runnable_status - because this function is being 71 | run *before* both self.pars "consumers" - scan() and run() 72 | 73 | set output_dir (node) for the output 74 | ''' 75 | 76 | for x in self.run_after: 77 | if not x.hasrun: 78 | return Task.ASK_LATER 79 | 80 | if not getattr(self, 'pars', None): 81 | txt = self.inputs[0].read() 82 | self.pars = parse_doxy(txt) 83 | if not self.pars.get('OUTPUT_DIRECTORY'): 84 | self.pars['OUTPUT_DIRECTORY'] = self.inputs[0].parent.get_bld().abspath() 85 | 86 | # Override with any parameters passed to the task generator 87 | if getattr(self.generator, 'pars', None): 88 | for k, v in self.generator.pars.iteritems(): 89 | self.pars[k] = v 90 | 91 | self.doxy_inputs = getattr(self, 'doxy_inputs', []) 92 | if not self.pars.get('INPUT'): 93 | self.doxy_inputs.append(self.inputs[0].parent) 94 | else: 95 | for i in self.pars.get('INPUT').split(): 96 | if os.path.isabs(i): 97 | node = self.generator.bld.root.find_node(i) 98 | else: 99 | node = self.generator.path.find_node(i) 100 | if not node: 101 | self.generator.bld.fatal('Could not find the doxygen input %r' % i) 102 | self.doxy_inputs.append(node) 103 | 104 | if not getattr(self, 'output_dir', None): 105 | bld = self.generator.bld 106 | # First try to find an absolute path, then find or declare a relative path 107 | self.output_dir = bld.root.find_dir(self.pars['OUTPUT_DIRECTORY']) 108 | if not self.output_dir: 109 | self.output_dir = bld.path.find_or_declare(self.pars['OUTPUT_DIRECTORY']) 110 | 111 | self.signature() 112 | return Task.Task.runnable_status(self) 113 | 114 | def scan(self): 115 | exclude_patterns = self.pars.get('EXCLUDE_PATTERNS','').split() 116 | file_patterns = self.pars.get('FILE_PATTERNS','').split() 117 | if not file_patterns: 118 | file_patterns = DOXY_FILE_PATTERNS 119 | if self.pars.get('RECURSIVE') == 'YES': 120 | file_patterns = ["**/%s" % pattern for pattern in file_patterns] 121 | nodes = [] 122 | names = [] 123 | for node in self.doxy_inputs: 124 | if os.path.isdir(node.abspath()): 125 | for m in node.ant_glob(incl=file_patterns, excl=exclude_patterns): 126 | nodes.append(m) 127 | else: 128 | nodes.append(node) 129 | return (nodes, names) 130 | 131 | def run(self): 132 | dct = self.pars.copy() 133 | dct['INPUT'] = ' '.join(['"%s"' % x.abspath() for x in self.doxy_inputs]) 134 | code = '\n'.join(['%s = %s' % (x, dct[x]) for x in self.pars]) 135 | code = code.encode() # for python 3 136 | #fmt = DOXY_STR % (self.inputs[0].parent.abspath()) 137 | cmd = Utils.subst_vars(DOXY_STR, self.env) 138 | env = self.env.env or None 139 | proc = Utils.subprocess.Popen(cmd, shell=True, stdin=Utils.subprocess.PIPE, env=env, cwd=self.generator.bld.path.get_bld().abspath()) 140 | proc.communicate(code) 141 | return proc.returncode 142 | 143 | def post_run(self): 144 | nodes = self.output_dir.ant_glob('**/*', quiet=True) 145 | for x in nodes: 146 | x.sig = Utils.h_file(x.abspath()) 147 | self.outputs += nodes 148 | return Task.Task.post_run(self) 149 | 150 | class tar(Task.Task): 151 | "quick tar creation" 152 | run_str = '${TAR} ${TAROPTS} ${TGT} ${SRC}' 153 | color = 'RED' 154 | after = ['doxygen'] 155 | def runnable_status(self): 156 | for x in getattr(self, 'input_tasks', []): 157 | if not x.hasrun: 158 | return Task.ASK_LATER 159 | 160 | if not getattr(self, 'tar_done_adding', None): 161 | # execute this only once 162 | self.tar_done_adding = True 163 | for x in getattr(self, 'input_tasks', []): 164 | self.set_inputs(x.outputs) 165 | if not self.inputs: 166 | return Task.SKIP_ME 167 | return Task.Task.runnable_status(self) 168 | 169 | def __str__(self): 170 | tgt_str = ' '.join([a.nice_path(self.env) for a in self.outputs]) 171 | return '%s: %s\n' % (self.__class__.__name__, tgt_str) 172 | 173 | @feature('doxygen') 174 | def process_doxy(self): 175 | if not getattr(self, 'doxyfile', None): 176 | self.generator.bld.fatal('no doxyfile??') 177 | 178 | node = self.doxyfile 179 | if not isinstance(node, Node.Node): 180 | node = self.path.find_resource(node) 181 | if not node: 182 | raise ValueError('doxygen file not found') 183 | 184 | # the task instance 185 | dsk = self.create_task('doxygen', node) 186 | 187 | if getattr(self, 'doxy_tar', None): 188 | tsk = self.create_task('tar') 189 | tsk.input_tasks = [dsk] 190 | tsk.set_outputs(self.path.find_or_declare(self.doxy_tar)) 191 | if self.doxy_tar.endswith('bz2'): 192 | tsk.env['TAROPTS'] = ['cjf'] 193 | elif self.doxy_tar.endswith('gz'): 194 | tsk.env['TAROPTS'] = ['czf'] 195 | else: 196 | tsk.env['TAROPTS'] = ['cf'] 197 | 198 | def configure(conf): 199 | ''' 200 | Check if doxygen and tar commands are present in the system 201 | 202 | If the commands are present, then conf.env.DOXYGEN and conf.env.TAR 203 | variables will be set. Detection can be controlled by setting DOXYGEN and 204 | TAR environmental variables. 205 | ''' 206 | 207 | conf.find_program('doxygen', var='DOXYGEN', mandatory=False) 208 | conf.find_program('tar', var='TAR', mandatory=False) 209 | 210 | # doxygen docs 211 | from waflib.Build import BuildContext 212 | class doxy(BuildContext): 213 | cmd = "doxygen" 214 | fun = "doxygen" 215 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS.md for complete list of nTorrent authors and contributors. 20 | */ 21 | #include "sequential-data-fetcher.hpp" 22 | #include "torrent-file.hpp" 23 | #include "util/io-util.hpp" 24 | #include "util/logging.hpp" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | namespace fs = boost::filesystem; 37 | namespace logging = boost::log; 38 | namespace po = boost::program_options; 39 | 40 | using namespace ndn; 41 | using namespace ndn::ntorrent; 42 | 43 | namespace ndn { 44 | 45 | class Error : public std::runtime_error 46 | { 47 | public: 48 | explicit 49 | Error(const std::string& what) 50 | : runtime_error(what) 51 | { 52 | } 53 | }; 54 | 55 | namespace ntorrent { 56 | 57 | const char * SharedConstants::commonPrefix = "/ndn/nTorrent"; 58 | 59 | } // end ntorrent 60 | } // end ndn 61 | 62 | // TODO(msweatt) Add options verification 63 | int main(int argc, char *argv[]) 64 | { 65 | try { 66 | po::options_description desc("Allowed options"); 67 | desc.add_options() 68 | // TODO(msweatt) Consider adding flagged args for other parameters 69 | ("help,h", "produce help message") 70 | ("generate,g" , "-g ? ? ? ?") 71 | ("seed,s", "After download completes, continue to seed") 72 | ("dump,d", "-d Dump the contents of the Data stored at the .") 73 | ("log-level", po::value(), "trace | debug | info | warming | error | fatal | console") 74 | ("args", po::value >(), "For arguments you want to specify without flags") 75 | ; 76 | po::positional_options_description p; 77 | p.add("args", -1); 78 | 79 | po::variables_map vm; 80 | po::store(po::command_line_parser(argc, argv). 81 | options(desc).allow_unregistered().positional(p).run(), vm); 82 | po::notify(vm); 83 | 84 | if (vm.count("help")) { 85 | std::cout << desc << std::endl; 86 | return 1; 87 | } 88 | bool log_to_console = false; 89 | if (vm.count("log-level")) { 90 | std::unordered_map supported_levels = { 91 | { "trace" , log::severity_level::trace}, 92 | { "debug" , log::severity_level::debug}, 93 | { "info" , log::severity_level::info}, 94 | { "warning", log::severity_level::warning}, 95 | { "error" , log::severity_level::error}, 96 | { "fatal" , log::severity_level::fatal}, 97 | { "console", log::severity_level::trace} 98 | }; 99 | auto log_str = vm["log-level"].as(); 100 | if (supported_levels.count(log_str)) { 101 | LoggingUtil::severity_threshold = supported_levels[log_str]; 102 | log_to_console = ("console" == log_str); 103 | } 104 | else { 105 | throw ndn::Error("Unsupported log level: " + log_str); 106 | } 107 | } 108 | // setup log 109 | LoggingUtil::init(log_to_console); 110 | logging::add_common_attributes(); 111 | 112 | if (vm.count("args")) { 113 | auto args = vm["args"].as>(); 114 | // if generate mode 115 | if (vm.count("generate")) { 116 | if (args.size() < 1 || args.size() > 5) { 117 | throw ndn::Error("wrong number of arguments for generate"); 118 | } 119 | auto dataPath = args[0]; 120 | auto outputPath = args.size() >= 2 ? args[1] : ".appdata/"; 121 | auto namesPerSegment = args.size() >= 3 ? boost::lexical_cast(args[2]) : 1024; 122 | auto namesPerManifest = args.size() >= 4 ? boost::lexical_cast(args[3]) : 1024; 123 | auto dataPacketSize = args.size() == 5 ? boost::lexical_cast(args[4]) : 1024; 124 | 125 | const auto& content = TorrentFile::generate(dataPath, 126 | namesPerSegment, 127 | namesPerManifest, 128 | dataPacketSize); 129 | const auto& torrentSegments = content.first; 130 | std::vector manifests; 131 | for (const auto& ms : content.second) { 132 | manifests.insert(manifests.end(), ms.first.begin(), ms.first.end()); 133 | } 134 | auto torrentPrefix = fs::canonical(dataPath).filename().string(); 135 | outputPath += ("/" + torrentPrefix); 136 | auto torrentPath = outputPath + "/torrent_files/"; 137 | // write all the torrent segments 138 | for (const TorrentFile& t : torrentSegments) { 139 | if (!IoUtil::writeTorrentSegment(t, torrentPath)) { 140 | LOG_ERROR << "Write failed: " << t.getName() << std::endl; 141 | return -1; 142 | } 143 | } 144 | auto manifestPath = outputPath + "/manifests/"; 145 | for (const FileManifest& m : manifests) { 146 | if (!IoUtil::writeFileManifest(m, manifestPath)) { 147 | LOG_ERROR << "Write failed: " << m.getName() << std::endl; 148 | return -1; 149 | } 150 | } 151 | } 152 | // if dump mode 153 | else if(vm.count("dump")) { 154 | if (args.size() != 1) { 155 | throw ndn::Error("wrong number of arguments for dump"); 156 | } 157 | auto filePath = args[0]; 158 | auto data = io::load(filePath); 159 | if (nullptr != data) { 160 | std::cout << data->getFullName() << std::endl; 161 | } 162 | else { 163 | throw ndn::Error("Invalid data."); 164 | } 165 | } 166 | // standard torrent mode 167 | else { 168 | // 169 | if (args.size() != 2) { 170 | throw ndn::Error("wrong number of arguments for torrent"); 171 | } 172 | auto torrentName = args[0]; 173 | auto dataPath = args[1]; 174 | auto seedFlag = (vm.count("seed") != 0); 175 | SequentialDataFetcher fetcher(torrentName, dataPath, seedFlag); 176 | fetcher.start(); 177 | } 178 | } 179 | else { 180 | std::cout << desc << std::endl; 181 | return 1; 182 | } 183 | } 184 | catch(std::exception& e) { 185 | std::cerr << "error: " << e.what() << "\n"; 186 | return 1; 187 | } 188 | catch(...) { 189 | std::cerr << "Exception of unknown type!\n"; 190 | } 191 | return 0; 192 | } 193 | -------------------------------------------------------------------------------- /tests/unit-tests/stats-table.t.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #include "boost-test.hpp" 23 | #include "stats-table.hpp" 24 | 25 | #include 26 | 27 | namespace ndn { 28 | namespace ntorrent { 29 | namespace tests { 30 | 31 | BOOST_AUTO_TEST_SUITE(TestStatsTable) 32 | 33 | BOOST_AUTO_TEST_CASE(TestCoreAPI) 34 | { 35 | StatsTable table(Name("linux15.01")); 36 | table.insert(Name("isp1")); 37 | table.insert(Name("isp2")); 38 | table.insert(Name("isp3")); 39 | 40 | BOOST_CHECK(table.find(Name("isp1")) != table.end()); 41 | BOOST_CHECK(table.find(Name("isp2")) != table.end()); 42 | BOOST_CHECK(table.find(Name("isp3")) != table.end()); 43 | BOOST_CHECK(table.find(Name("isp4")) == table.end()); 44 | 45 | BOOST_CHECK_EQUAL(table.erase(Name("isp2")), true); 46 | BOOST_CHECK_EQUAL(table.erase(Name("isp4")), false); 47 | 48 | BOOST_CHECK(table.find(Name("isp1")) != table.end()); 49 | BOOST_CHECK(table.find(Name("isp2")) == table.end()); 50 | BOOST_CHECK(table.find(Name("isp3")) != table.end()); 51 | BOOST_CHECK(table.find(Name("isp4")) == table.end()); 52 | } 53 | 54 | BOOST_AUTO_TEST_CASE(TestStatsTableModification) 55 | { 56 | StatsTable table(Name("linux15.01")); 57 | table.insert(Name("isp1")); 58 | table.insert(Name("isp2")); 59 | table.insert(Name("isp3")); 60 | 61 | BOOST_CHECK_EQUAL(table.size(), 3); 62 | 63 | auto entry = table.find(Name("isp2")); 64 | entry->incrementSentInterests(); 65 | entry->incrementReceivedData(); 66 | 67 | auto i = table.begin(); 68 | BOOST_CHECK(!(entry == table.end())); 69 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp1"); 70 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0); 71 | 72 | i++; 73 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp2"); 74 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 1); 75 | 76 | i++; 77 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp3"); 78 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0); 79 | 80 | entry = table.find(Name("isp1")); 81 | BOOST_CHECK(!(entry == table.end())); 82 | entry->incrementSentInterests(); 83 | entry->incrementReceivedData(); 84 | entry->incrementSentInterests(); 85 | 86 | i = table.begin(); 87 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp1"); 88 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0.5); 89 | 90 | i++; 91 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp2"); 92 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 1); 93 | 94 | i++; 95 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp3"); 96 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0); 97 | 98 | entry = table.find(Name("isp3")); 99 | BOOST_CHECK(!(entry == table.end())); 100 | entry->incrementSentInterests(); 101 | entry->incrementReceivedData(); 102 | entry->incrementSentInterests(); 103 | entry->incrementSentInterests(); 104 | entry->incrementSentInterests(); 105 | 106 | i = table.begin(); 107 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp1"); 108 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0.5); 109 | 110 | i++; 111 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp2"); 112 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 1); 113 | 114 | i++; 115 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp3"); 116 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0.25); 117 | } 118 | 119 | BOOST_AUTO_TEST_CASE(TestTableSort) 120 | { 121 | StatsTable table(Name("linux15.01")); 122 | table.insert(Name("isp1")); 123 | table.insert(Name("isp2")); 124 | table.insert(Name("isp3")); 125 | 126 | BOOST_CHECK_EQUAL(table.size(), 3); 127 | 128 | auto entry = table.find(Name("isp2")); 129 | entry->incrementSentInterests(); 130 | entry->incrementReceivedData(); 131 | 132 | auto i = table.begin(); 133 | BOOST_CHECK(!(entry == table.end())); 134 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp1"); 135 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0); 136 | 137 | i++; 138 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp2"); 139 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 1); 140 | 141 | i++; 142 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp3"); 143 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0); 144 | 145 | entry = table.find(Name("isp1")); 146 | BOOST_CHECK(!(entry == table.end())); 147 | entry->incrementSentInterests(); 148 | entry->incrementReceivedData(); 149 | entry->incrementSentInterests(); 150 | 151 | i = table.begin(); 152 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp1"); 153 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0.5); 154 | 155 | i++; 156 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp2"); 157 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 1); 158 | 159 | i++; 160 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp3"); 161 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0); 162 | 163 | entry = table.find(Name("isp3")); 164 | BOOST_CHECK(!(entry == table.end())); 165 | entry->incrementSentInterests(); 166 | entry->incrementReceivedData(); 167 | entry->incrementSentInterests(); 168 | entry->incrementSentInterests(); 169 | entry->incrementSentInterests(); 170 | 171 | i = table.begin(); 172 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp1"); 173 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0.5); 174 | 175 | i++; 176 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp2"); 177 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 1); 178 | 179 | i++; 180 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp3"); 181 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0.25); 182 | 183 | table.sort(); 184 | 185 | i = table.begin(); 186 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp2"); 187 | BOOST_CHECK_EQUAL(i->getRecordSentInterests(), 1); 188 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 1); 189 | 190 | i++; 191 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp1"); 192 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0.5); 193 | 194 | i++; 195 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp3"); 196 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0.25); 197 | 198 | BOOST_CHECK_EQUAL(table.erase(Name("isp1")), true); 199 | BOOST_CHECK_EQUAL(table.size(), 2); 200 | 201 | i = table.begin(); 202 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp2"); 203 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 1); 204 | 205 | i++; 206 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp3"); 207 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0.25); 208 | 209 | entry = table.find(Name("isp2")); 210 | BOOST_CHECK(!(entry == table.end())); 211 | entry->incrementSentInterests(); 212 | entry->incrementSentInterests(); 213 | entry->incrementSentInterests(); 214 | 215 | table.sort(); 216 | 217 | i = table.begin(); 218 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp3"); 219 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0.25); 220 | 221 | i++; 222 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp2"); 223 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0.25); 224 | } 225 | 226 | BOOST_AUTO_TEST_SUITE_END() 227 | 228 | } // namespace tests 229 | } // namespace ntorrent 230 | } // namespace ndn 231 | -------------------------------------------------------------------------------- /src/update-handler.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #include "update-handler.hpp" 23 | #include "util/logging.hpp" 24 | 25 | #include 26 | 27 | #include 28 | 29 | namespace ndn { 30 | namespace ntorrent { 31 | 32 | void 33 | UpdateHandler::sendAliveInterest(StatsTable::iterator iter) 34 | { 35 | Name prependedComponents(SharedConstants::commonPrefix); 36 | Name interestName = Name(prependedComponents.toUri() + "/NTORRENT" + m_torrentName.toUri() + 37 | "/ALIVE" + m_ownRoutablePrefix.toUri()); 38 | 39 | shared_ptr i = make_shared(interestName); 40 | 41 | // Create and set the forwarding hint 42 | Delegation del; 43 | del.preference = 1; 44 | del.name = iter->getRecordName(); 45 | DelegationList list({del}); 46 | 47 | i->setForwardingHint(list); 48 | i->setMustBeFresh(true); 49 | 50 | LOG_DEBUG << "Sending ALIVE Interest: " << *i << std::endl; 51 | 52 | m_face->expressInterest(*i, bind(&UpdateHandler::decodeDataPacketContent, this, _1, _2), 53 | bind(&UpdateHandler::tryNextRoutablePrefix, this, _1), 54 | bind(&UpdateHandler::tryNextRoutablePrefix, this, _1)); 55 | } 56 | 57 | shared_ptr 58 | UpdateHandler::createDataPacket(const Name& name) 59 | { 60 | // Parse the sender's routable prefix contained in the name 61 | Name sendersRoutablePrefix = name.getSubName(2 + 2 + m_torrentName.size()); 62 | 63 | if (m_statsTable->find(sendersRoutablePrefix) == m_statsTable->end()) { 64 | m_statsTable->insert(sendersRoutablePrefix); 65 | } 66 | 67 | shared_ptr data = make_shared(name); 68 | 69 | EncodingEstimator estimator; 70 | size_t estimatedSize = encodeContent(estimator); 71 | 72 | EncodingBuffer buffer(estimatedSize, 0); 73 | encodeContent(buffer); 74 | 75 | data->setContentType(tlv::ContentType_Blob); 76 | data->setFreshnessPeriod(time::milliseconds(100)); 77 | data->setContent(buffer.block()); 78 | 79 | return data; 80 | } 81 | 82 | template 83 | size_t 84 | UpdateHandler::encodeContent(EncodingImpl& encoder) const 85 | { 86 | // Content ::= CONTENT-TYPE TLV-LENGTH 87 | // RoutableName+ 88 | 89 | // RoutableName ::= NAME-TYPE TLV-LENGTH 90 | // Name 91 | 92 | size_t totalLength = 0; 93 | // Encode the names of the first five entries of the stats table 94 | uint32_t namesEncoded = 0; 95 | for (const auto& entry : *m_statsTable) { 96 | if (namesEncoded >= MAX_NUM_OF_ENCODED_NAMES) { 97 | break; 98 | } 99 | size_t nameLength = 0; 100 | nameLength += entry.getRecordName().wireEncode(encoder); 101 | totalLength += nameLength; 102 | ++namesEncoded; 103 | } 104 | totalLength += encoder.prependVarNumber(totalLength); 105 | totalLength += encoder.prependVarNumber(tlv::Content); 106 | return totalLength; 107 | } 108 | 109 | void 110 | UpdateHandler::decodeDataPacketContent(const Interest& interest, const Data& data) 111 | { 112 | // Content ::= CONTENT-TYPE TLV-LENGTH 113 | // RoutableName+ 114 | 115 | // RoutableName ::= NAME-TYPE TLV-LENGTH 116 | // Name 117 | 118 | LOG_INFO << "ALIVE data packet received: " << data.getName() << std::endl; 119 | 120 | if (data.getContentType() != tlv::ContentType_Blob) { 121 | BOOST_THROW_EXCEPTION(Error("Expected Content Type Blob")); 122 | } 123 | 124 | const Block& content = data.getContent(); 125 | content.parse(); 126 | 127 | // Decode the names (maintain their ordering) 128 | for (auto element = content.elements_end() - 1; element != content.elements_begin() - 1; element--) { 129 | element->parse(); 130 | Name name(*element); 131 | if (name.empty()) { 132 | //BOOST_THROW_EXCEPTION(Error("Empty routable name was received")); 133 | continue; 134 | } 135 | if (m_statsTable->find(name) == m_statsTable->end() && name != m_ownRoutablePrefix) { 136 | m_statsTable->insert(name); 137 | } 138 | } 139 | } 140 | 141 | bool 142 | UpdateHandler::needsUpdate() 143 | { 144 | if (m_statsTable->size() < MIN_NUM_OF_ROUTABLE_NAMES) { 145 | return true; 146 | } 147 | for (auto i = m_statsTable->begin(); i != m_statsTable->end(); i++) { 148 | if (i->getRecordSuccessRate() >= 0.5) { 149 | return false; 150 | } 151 | } 152 | return true; 153 | } 154 | 155 | void 156 | UpdateHandler::learnOwnRoutablePrefix(OnReceivedOwnRoutablePrefix onReceivedOwnRoutablePrefix) 157 | { 158 | Interest i(Name("/localhop/nfd/rib/routable-prefixes")); 159 | i.setInterestLifetime(time::milliseconds(1000)); 160 | 161 | // parse the first contained routable prefix and set it as the ownRoutablePrefix 162 | auto prefixReceived = [this, onReceivedOwnRoutablePrefix] (const Interest& interest, 163 | const Data& data) { 164 | const Block& content = data.getContent(); 165 | content.parse(); 166 | 167 | auto element = content.elements_begin(); 168 | element->parse(); 169 | Name ownRoutablePrefix(*element); 170 | m_ownRoutablePrefix = ownRoutablePrefix; 171 | LOG_DEBUG << "Own routable prefix received: " << m_ownRoutablePrefix << std::endl; 172 | 173 | Name prependedComponents(SharedConstants::commonPrefix); 174 | m_face->setInterestFilter(Name(prependedComponents.toUri() + "/NTORRENT" + m_torrentName.toUri() + 175 | "/ALIVE"), 176 | bind(&UpdateHandler::onInterestReceived, this, _1, _2), 177 | RegisterPrefixSuccessCallback(), 178 | bind(&UpdateHandler::onRegisterFailed, this, _1, _2)); 179 | 180 | onReceivedOwnRoutablePrefix(); 181 | }; 182 | 183 | auto prefixRetrievalFailed = [this, onReceivedOwnRoutablePrefix] (const Interest&) { 184 | ++m_ownRoutablPrefixRetries; 185 | LOG_ERROR << "Own Routable Prefix Retrieval Failed. Trying again." << std::endl; 186 | // If we fail, we will retry OWN_ROUTABLE_PREFIX_RETRIES times 187 | if (m_ownRoutablPrefixRetries < OWN_ROUTABLE_PREFIX_RETRIES) { 188 | this->learnOwnRoutablePrefix(onReceivedOwnRoutablePrefix); 189 | } 190 | else { 191 | m_face->getIoService().stop(); 192 | } 193 | }; 194 | 195 | //TODO(Spyros): For now, we do nothing when we receive a NACK 196 | m_face->expressInterest(i, prefixReceived, nullptr, prefixRetrievalFailed); 197 | } 198 | 199 | void 200 | UpdateHandler::onInterestReceived(const InterestFilter& filter, const Interest& interest) 201 | { 202 | LOG_INFO << "ALIVE Interest Received: " << interest.getName().toUri() << std::endl; 203 | shared_ptr data = this->createDataPacket(interest.getName()); 204 | m_keyChain->sign(*data, signingWithSha256()); 205 | m_face->put(*data); 206 | } 207 | 208 | void 209 | UpdateHandler::onRegisterFailed(const Name& prefix, const std::string& reason) 210 | { 211 | LOG_ERROR << "ERROR: Failed to register prefix \"" 212 | << prefix << "\" in local hub's daemon (" << reason << ")" 213 | << std::endl; 214 | m_face->shutdown(); 215 | } 216 | 217 | void 218 | UpdateHandler::tryNextRoutablePrefix(const Interest& interest) 219 | { 220 | const Name& name = interest.getForwardingHint().begin()->name; 221 | auto iter = m_statsTable->find(name); 222 | 223 | if (iter != m_statsTable->end()) { 224 | if (iter + 1 == m_statsTable->end()) { 225 | iter = m_statsTable->begin(); 226 | } 227 | else { 228 | ++iter; 229 | } 230 | } 231 | else { 232 | iter = m_statsTable->begin(); 233 | } 234 | 235 | shared_ptr newInterest = make_shared(interest); 236 | 237 | // Create and set the forwarding hint 238 | Delegation del; 239 | del.preference = 1; 240 | del.name = iter->getRecordName(); 241 | 242 | DelegationList list({del}); 243 | 244 | newInterest->setForwardingHint(list); 245 | 246 | m_face->expressInterest(*newInterest, bind(&UpdateHandler::decodeDataPacketContent, this, _1, _2), 247 | bind(&UpdateHandler::tryNextRoutablePrefix, this, _1), 248 | bind(&UpdateHandler::tryNextRoutablePrefix, this, _1)); 249 | } 250 | 251 | } // namespace ntorrent 252 | } // namespace ndn 253 | -------------------------------------------------------------------------------- /src/torrent-file.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #ifndef TORRENT_FILE_HPP 23 | #define TORRENT_FILE_HPP 24 | 25 | #include "file-manifest.hpp" 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | namespace ndn { 34 | 35 | namespace ntorrent { 36 | 37 | class TorrentFile : public Data { 38 | public: 39 | class Error : public Data::Error 40 | { 41 | public: 42 | explicit 43 | Error(const std::string& what) 44 | : Data::Error(what) 45 | { 46 | } 47 | }; 48 | static ndn::Name 49 | torrentFileName(const Name& name); 50 | 51 | /** 52 | * @brief Create a new empty TorrentFile. 53 | */ 54 | TorrentFile() = default; 55 | 56 | /** 57 | * @brief Create a new TorrentFile. 58 | * @param torrentFileName The name of the torrent-file 59 | * @param torrentFilePtr A pointer (name) to the next segment of the torrent-file 60 | * @param commonPrefix The common name prefix of the manifest file names included in the catalog 61 | * @param catalog The catalog containing the name of each file manifest 62 | */ 63 | TorrentFile(const Name& torrentFileName, 64 | const Name& torrentFilePtr, 65 | const Name& commonPrefix, 66 | const std::vector& catalog); 67 | 68 | /** 69 | * @brief Create a new TorrentFile. 70 | * @param torrentFileName The name of the torrent-file 71 | * @param commonPrefix The common name prefix of the manifest file names included in the catalog 72 | * @param catalog The catalog containing the name of each file manifest 73 | */ 74 | TorrentFile(const Name& torrentFileName, 75 | const Name& commonPrefix, 76 | const std::vector& catalog); 77 | 78 | /** 79 | * @brief Create a new TorrentFile 80 | * @param block The block format of the torrent-file 81 | */ 82 | explicit 83 | TorrentFile(const Block& block); 84 | 85 | /** 86 | * @brief Get the name of the TorrentFile 87 | */ 88 | const Name& 89 | getName() const; 90 | 91 | /** 92 | * @brief Get the common prefix of the file manifest names of this torrent-file 93 | */ 94 | const Name& 95 | getCommonPrefix() const; 96 | 97 | /** 98 | * @brief Get a shared pointer to the name of the next segment of the torrent-file. 99 | * 100 | * If there is no next segment, it returns a nullptr 101 | */ 102 | shared_ptr 103 | getTorrentFilePtr() const; 104 | 105 | /** 106 | * @brief Get the catalog of names of the file manifests 107 | */ 108 | const std::vector& 109 | getCatalog() const; 110 | 111 | /** 112 | * @brief Get the segment number for this torrent file 113 | */ 114 | size_t 115 | getSegmentNumber() const; 116 | 117 | /** 118 | * @brief Get the directory name for this torrent file 119 | */ 120 | std::string 121 | getTorrentFilePath() const; 122 | 123 | /** 124 | * @brief Decode from wire format 125 | */ 126 | void 127 | wireDecode(const Block& wire); 128 | 129 | /** 130 | * @brief Finalize torrent-file before signing the data packet 131 | * 132 | * This method has to be called (every time) right before signing or encoding 133 | * the torrent-file 134 | */ 135 | void 136 | finalize(); 137 | 138 | /** 139 | * @brief Insert a name to the catalog of file manifest names 140 | */ 141 | void 142 | insert(const Name& name); 143 | 144 | /** 145 | * @brief Erase a name from the catalog of file manifest names 146 | */ 147 | bool 148 | erase(const Name& name); 149 | 150 | /** 151 | * @brief Get the size of the catalog of file manifest names 152 | */ 153 | size_t 154 | catalogSize() const; 155 | 156 | /** 157 | * @brief Given a directory path for the torrent file, it generates the torrent file 158 | * 159 | * @param directoryPath The path to the directory for which we are to create a torrent-file 160 | * @param torrentFilePrefix The prefix to be used for the name of this torrent-file 161 | * @param namesPerSegment The number of manifest names to be included in each segment of the 162 | * torrent-file 163 | * @param returnData Determines whether the data would be returned in memory or it will be 164 | * stored on disk without being returned 165 | * 166 | * Generates the torrent-file for the directory at the specified 'directoryPath', 167 | * splitting the torrent-file into multiple segments, each one of which contains 168 | * at most 'namesPerSegment' number of manifest names 169 | * 170 | **/ 171 | static std::pair, 172 | std::vector, 173 | std::vector>>> 174 | generate(const std::string& directoryPath, 175 | size_t namesPerSegment, 176 | size_t subManifestSize, 177 | size_t dataPacketSize, 178 | bool returnData = false); 179 | 180 | protected: 181 | /** 182 | * @brief prepend torrent file as a Content block to the encoder 183 | */ 184 | template 185 | size_t 186 | encodeContent(EncodingImpl& encoder) const; 187 | 188 | void 189 | encodeContent(); 190 | 191 | void 192 | decodeContent(); 193 | 194 | private: 195 | /** 196 | * @brief Check whether the torrent-file has a pointer to the next segment 197 | */ 198 | bool 199 | hasTorrentFilePtr() const; 200 | 201 | /** 202 | * @brief Create a catalog of suffixes for the names of the file manifests 203 | * 204 | * To optimize encoding and decoding, we encode the name of the file manifests 205 | * as suffixes along with their common prefix. 206 | * 207 | */ 208 | void 209 | createSuffixCatalog(); 210 | 211 | /** 212 | * @brief Construct the catalog of long names from a catalog of suffixes for the file 213 | * manifests' name 214 | * 215 | * After decoding a torrent-file from its wire format, we construct the catalog of 216 | * long names from the decoded common prefix and suffixes 217 | * 218 | */ 219 | void 220 | constructLongNames(); 221 | 222 | /** 223 | * @brief Insert a suffix to the suffix catalog 224 | */ 225 | void 226 | insertToSuffixCatalog(const PartialName& suffix); 227 | 228 | /** 229 | * @brief Set the pointer of the current torrent-file segment to the next segment 230 | */ 231 | void 232 | setTorrentFilePtr(const Name& ptrName); 233 | 234 | private: 235 | Name m_commonPrefix; 236 | Name m_torrentFilePtr; 237 | std::vector m_suffixCatalog; 238 | std::vector m_catalog; 239 | }; 240 | 241 | inline 242 | ndn::Name 243 | TorrentFile::torrentFileName(const Name& name) { 244 | return (name.get(name.size() - 2).isSequenceNumber()) 245 | ? name.getSubName(0, name.size() - 2) 246 | : name.getSubName(0, name.size() - 1); 247 | } 248 | 249 | inline bool 250 | TorrentFile::hasTorrentFilePtr() const 251 | { 252 | return !m_torrentFilePtr.empty(); 253 | } 254 | 255 | inline const std::vector& 256 | TorrentFile::getCatalog() const 257 | { 258 | return m_catalog; 259 | } 260 | 261 | inline size_t 262 | TorrentFile::catalogSize() const 263 | { 264 | return m_catalog.size(); 265 | } 266 | 267 | inline void 268 | TorrentFile::insert(const Name& name) 269 | { 270 | m_catalog.push_back(name); 271 | } 272 | 273 | inline void 274 | TorrentFile::insertToSuffixCatalog(const PartialName& suffix) 275 | { 276 | m_suffixCatalog.push_back(suffix); 277 | } 278 | 279 | inline void 280 | TorrentFile::setTorrentFilePtr(const Name& ptrName) 281 | { 282 | m_torrentFilePtr = ptrName; 283 | } 284 | 285 | inline const Name& 286 | TorrentFile::getName() const 287 | { 288 | return Data::getName(); 289 | } 290 | 291 | inline const Name& 292 | TorrentFile::getCommonPrefix() const 293 | { 294 | return m_commonPrefix; 295 | } 296 | 297 | inline std::string 298 | TorrentFile::getTorrentFilePath() const 299 | { 300 | Name commonPrefix(SharedConstants::commonPrefix); 301 | return torrentFileName(getFullName()).get(commonPrefix.size() + 1).toUri(); 302 | } 303 | 304 | inline size_t 305 | TorrentFile::getSegmentNumber() const 306 | { 307 | const auto& lastComponent = getName().get(getName().size() - 1); 308 | return lastComponent.isSequenceNumber() ? lastComponent.toSequenceNumber() : 0; 309 | } 310 | 311 | 312 | } // namespace ntorrent 313 | 314 | } // namespace ndn 315 | 316 | #endif // TORRENT_FILE_HPP 317 | -------------------------------------------------------------------------------- /src/util/io-util.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #include "util/io-util.hpp" 23 | 24 | #include "file-manifest.hpp" 25 | #include "torrent-file.hpp" 26 | #include "util/logging.hpp" 27 | 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | 34 | namespace fs = boost::filesystem; 35 | 36 | using std::string; 37 | using std::vector; 38 | 39 | namespace ndn { 40 | namespace ntorrent { 41 | 42 | std::vector 43 | IoUtil::packetize_file(const fs::path& filePath, 44 | const ndn::Name& commonPrefix, 45 | size_t dataPacketSize, 46 | size_t subManifestSize, 47 | size_t subManifestNum) 48 | { 49 | BOOST_ASSERT(0 < dataPacketSize); 50 | size_t APPROX_BUFFER_SIZE = std::numeric_limits::max(); // 2 * 1024 * 1024 *1024 51 | auto file_size = fs::file_size(filePath); 52 | auto start_offset = subManifestNum * subManifestSize * dataPacketSize; 53 | // determine the number of bytes in this submanifest 54 | auto subManifestLength = subManifestSize * dataPacketSize; 55 | auto remainingFileLength = file_size - start_offset; 56 | subManifestLength = remainingFileLength < subManifestLength 57 | ? remainingFileLength 58 | : subManifestLength; 59 | vector packets; 60 | packets.reserve(subManifestLength/dataPacketSize + 1); 61 | fs::ifstream fs(filePath, fs::ifstream::binary); 62 | if (!fs) { 63 | BOOST_THROW_EXCEPTION(Data::Error("IO Error when opening" + filePath.string())); 64 | } 65 | // ensure that buffer is large enough to contain whole packets 66 | // buffer size is either the entire file or the smallest number of data packets >= 2 GB 67 | auto buffer_size = 68 | subManifestLength < APPROX_BUFFER_SIZE ? 69 | subManifestLength : 70 | APPROX_BUFFER_SIZE % dataPacketSize == 0 ? 71 | APPROX_BUFFER_SIZE : 72 | APPROX_BUFFER_SIZE + dataPacketSize - (APPROX_BUFFER_SIZE % dataPacketSize); 73 | vector file_bytes; 74 | file_bytes.reserve(buffer_size); 75 | size_t bytes_read = 0; 76 | fs.seekg(start_offset); 77 | while(fs && bytes_read < subManifestLength && !fs.eof()) { 78 | // read the file into the buffer 79 | fs.read(&file_bytes.front(), buffer_size); 80 | auto read_size = fs.gcount(); 81 | if (fs.bad() || read_size < 0) { 82 | BOOST_THROW_EXCEPTION(Data::Error("IO Error when reading" + filePath.string())); 83 | } 84 | bytes_read += read_size; 85 | char *curr_start = &file_bytes.front(); 86 | for (size_t i = 0u; i < buffer_size; i += dataPacketSize) { 87 | // Build a packet from the data 88 | Name packetName = commonPrefix; 89 | packetName.appendSequenceNumber(packets.size()); 90 | Data d(packetName); 91 | auto content_length = i + dataPacketSize > buffer_size ? buffer_size - i : dataPacketSize; 92 | d.setContent(encoding::makeBinaryBlock(tlv::Content, curr_start, content_length)); 93 | curr_start += content_length; 94 | // append to the collection 95 | packets.push_back(d); 96 | } 97 | file_bytes.clear(); 98 | // recompute the buffer_size 99 | buffer_size = 100 | subManifestLength - bytes_read < APPROX_BUFFER_SIZE ? 101 | subManifestLength - bytes_read : 102 | APPROX_BUFFER_SIZE % dataPacketSize == 0 ? 103 | APPROX_BUFFER_SIZE : 104 | APPROX_BUFFER_SIZE + dataPacketSize - (APPROX_BUFFER_SIZE % dataPacketSize); 105 | } 106 | fs.close(); 107 | packets.shrink_to_fit(); 108 | ndn::security::KeyChain key_chain; 109 | // sign all the packets 110 | for (auto& p : packets) { 111 | key_chain.sign(p, signingWithSha256()); 112 | } 113 | return packets; 114 | } 115 | 116 | bool IoUtil::writeTorrentSegment(const TorrentFile& segment, const std::string& path) 117 | { 118 | // validate that this torrent segment belongs to our torrent 119 | auto segmentNum = segment.getSegmentNumber(); 120 | // write to disk at path 121 | if (!fs::exists(path)) { 122 | fs::create_directories(path); 123 | } 124 | auto filename = path + to_string(segmentNum); 125 | // if there is already a file on disk for this torrent segment, determine if we should override 126 | if (fs::exists(filename)) { 127 | auto segmentOnDisk_ptr = io::load(filename); 128 | if (nullptr != segmentOnDisk_ptr && *segmentOnDisk_ptr == segment) { 129 | return false; 130 | } 131 | } 132 | io::save(segment, filename); 133 | // add to collection 134 | return true; 135 | } 136 | 137 | bool IoUtil::writeFileManifest(const FileManifest& manifest, const std::string& path) 138 | { 139 | auto subManifestNum = manifest.submanifest_number(); 140 | fs::path filename = path + manifest.file_name() + "/" + to_string(subManifestNum); 141 | 142 | // write to disk at path 143 | if (!fs::exists(filename.parent_path())) { 144 | boost::filesystem::create_directories(filename.parent_path()); 145 | } 146 | // if there is already a file on disk for this file manifest, determine if we should override 147 | if (fs::exists(filename)) { 148 | auto submanifestOnDisk_ptr = io::load(filename.string()); 149 | if (nullptr != submanifestOnDisk_ptr && *submanifestOnDisk_ptr == manifest) { 150 | return false; 151 | } 152 | } 153 | io::save(manifest, filename.string()); 154 | return true; 155 | } 156 | bool 157 | IoUtil::writeData(const Data& packet, 158 | const FileManifest& manifest, 159 | size_t subManifestSize, 160 | const std::string& filePath) 161 | { 162 | fs::ofstream os (filePath, fs::ofstream::binary | fs::ofstream::out | fs::ofstream::app); 163 | auto packetName = packet.getName(); 164 | auto packetNum = packetName.get(packetName.size() - 1).toSequenceNumber(); 165 | auto dataPacketSize = manifest.data_packet_size(); 166 | auto initial_offset = manifest.submanifest_number() * subManifestSize * dataPacketSize; 167 | auto packetOffset = initial_offset + packetNum * dataPacketSize; 168 | // write data to disk 169 | os.seekp(packetOffset); 170 | try { 171 | auto content = packet.getContent(); 172 | std::vector data(content.value_begin(), content.value_end()); 173 | return os.write(&data[0], data.size()).good() && os.flush().good(); 174 | } 175 | catch (io::Error &e) { 176 | LOG_ERROR << e.what() << std::endl; 177 | return false; 178 | } 179 | } 180 | 181 | std::shared_ptr 182 | IoUtil::readDataPacket(const Name& packetFullName, 183 | const FileManifest& manifest, 184 | size_t subManifestSize, 185 | const std::string& filePath) 186 | { 187 | fs::fstream is (filePath, fs::fstream::in | fs::fstream::binary); 188 | auto dataPacketSize = manifest.data_packet_size(); 189 | auto start_offset = manifest.submanifest_number() * subManifestSize * dataPacketSize; 190 | auto packetNum = packetFullName.get(packetFullName.size() - 2).toSequenceNumber(); 191 | // seek to packet 192 | is.sync(); 193 | is.seekg(start_offset + packetNum * dataPacketSize); 194 | if (is.tellg() < 0) { 195 | LOG_ERROR << "bad seek" << std::endl; 196 | } 197 | // read contents 198 | std::vector bytes(dataPacketSize); 199 | is.read(&bytes.front(), dataPacketSize); 200 | auto read_size = is.gcount(); 201 | if (is.bad() || read_size < 0) { 202 | LOG_ERROR << "Bad read" << std::endl; 203 | return nullptr; 204 | } 205 | // construct packet 206 | auto packetName = packetFullName.getSubName(0, packetFullName.size() - 1); 207 | auto d = make_shared(packetName); 208 | d->setContent(encoding::makeBinaryBlock(tlv::Content, &bytes.front(), read_size)); 209 | ndn::security::KeyChain key_chain; 210 | key_chain.sign(*d, signingWithSha256()); 211 | return d->getFullName() == packetFullName ? d : nullptr; 212 | } 213 | 214 | IoUtil::NAME_TYPE 215 | IoUtil::findType(const Name& name) 216 | { 217 | NAME_TYPE rval = UNKNOWN; 218 | if (name.get(name.size() - 2).toUri() == "torrent-file" || 219 | name.get(name.size() - 3).toUri() == "torrent-file") { 220 | rval = TORRENT_FILE; 221 | } 222 | else if (name.get(name.size() - 2).isSequenceNumber() && 223 | name.get(name.size() - 3).isSequenceNumber()) { 224 | rval = DATA_PACKET; 225 | } 226 | else if (name.get(name.size() - 2).isSequenceNumber() && 227 | !(name.get(name.size() - 3).isSequenceNumber())) { 228 | rval = FILE_MANIFEST; 229 | } 230 | return rval; 231 | } 232 | 233 | 234 | 235 | } // namespace ntorrent 236 | } // namespace ndn} 237 | -------------------------------------------------------------------------------- /tests/unit-tests/update-handler.t.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #include "boost-test.hpp" 23 | #include "update-handler.hpp" 24 | #include "unit-test-time-fixture.hpp" 25 | #include "dummy-parser-fixture.hpp" 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | namespace ndn { 32 | namespace ntorrent { 33 | namespace tests { 34 | 35 | using util::DummyClientFace; 36 | using std::vector; 37 | 38 | class TestUpdateHandler : public UpdateHandler { 39 | public: 40 | TestUpdateHandler(Name torrentName, shared_ptr keyChain, 41 | shared_ptr statsTable, shared_ptr face) 42 | : UpdateHandler(torrentName, keyChain, statsTable, face) 43 | { 44 | } 45 | 46 | ~TestUpdateHandler() 47 | { 48 | } 49 | 50 | Name 51 | getOwnRoutablePrefix() 52 | { 53 | return UpdateHandler::getOwnRoutablePrefix(); 54 | } 55 | }; 56 | 57 | class FaceFixture : public UnitTestTimeFixture 58 | { 59 | public: 60 | explicit 61 | FaceFixture() 62 | : face1(new DummyClientFace(io, {true, true})) 63 | , face2(new DummyClientFace(io, {true, true})) 64 | { 65 | } 66 | 67 | public: 68 | shared_ptr face1; 69 | shared_ptr face2; 70 | }; 71 | 72 | BOOST_FIXTURE_TEST_SUITE(TestUpdateHandlerClass, FaceFixture) 73 | 74 | BOOST_AUTO_TEST_CASE(TestInitialSetup) 75 | { 76 | shared_ptr table1 = make_shared(Name("linux15.01")); 77 | 78 | shared_ptr keyChain = make_shared(); 79 | 80 | TestUpdateHandler handler1(Name("linux15.01"), keyChain, table1, face1); 81 | advanceClocks(time::milliseconds(1), 10); 82 | // Create a data packet containing one name as content 83 | shared_ptr d = DummyParser::createDataPacket(Name("/localhop/nfd/rib/routable-prefixes"), 84 | { Name("ucla") }); 85 | keyChain->sign(*d); 86 | face1->receive(*d); 87 | 88 | BOOST_CHECK_EQUAL(handler1.getOwnRoutablePrefix().toUri(), "/ucla"); 89 | 90 | shared_ptr table2 = make_shared(Name("linux15.01")); 91 | TestUpdateHandler handler2(Name("linux15.01"), keyChain, table2, face2); 92 | advanceClocks(time::milliseconds(1), 10); 93 | // Create a data packet containing one name as content 94 | d = DummyParser::createDataPacket(Name("/localhop/nfd/rib/routable-prefixes"), 95 | { Name("arizona") }); 96 | keyChain->sign(*d); 97 | face2->receive(*d); 98 | 99 | BOOST_CHECK_EQUAL(handler2.getOwnRoutablePrefix().toUri(), "/arizona"); 100 | } 101 | 102 | BOOST_AUTO_TEST_CASE(TestAliveInterestExchange) 103 | { 104 | shared_ptr table1 = make_shared(Name("linux15.01")); 105 | table1->insert(Name("isp1")); 106 | table1->insert(Name("isp2")); 107 | table1->insert(Name("isp3")); 108 | 109 | shared_ptr keyChain = make_shared(); 110 | 111 | TestUpdateHandler handler1(Name("linux15.01"), keyChain, table1, face1); 112 | advanceClocks(time::milliseconds(1), 10); 113 | // Create a data packet containing one name as content 114 | shared_ptr d = DummyParser::createDataPacket(Name("/localhop/nfd/rib/routable-prefixes"), 115 | { Name("ucla") }); 116 | keyChain->sign(*d); 117 | face1->receive(*d); 118 | 119 | shared_ptr table2 = make_shared(Name("linux15.01")); 120 | table2->insert(Name("ucla")); 121 | TestUpdateHandler handler2(Name("linux15.01"), keyChain, table2, face2); 122 | advanceClocks(time::milliseconds(1), 10); 123 | // Create a data packet containing one name as content 124 | d = DummyParser::createDataPacket(Name("/localhop/nfd/rib/routable-prefixes"), 125 | { Name("arizona") }); 126 | keyChain->sign(*d); 127 | face2->receive(*d); 128 | 129 | handler2.sendAliveInterest(table2->begin()); 130 | 131 | advanceClocks(time::milliseconds(1), 40); 132 | Interest interest(Name("ndn/multicast/NTORRENT/linux15.01/ALIVE/test")); 133 | face1->receive(interest); 134 | 135 | advanceClocks(time::milliseconds(1), 10); 136 | std::vector dataVec = face1->sentData; 137 | 138 | BOOST_CHECK_EQUAL(dataVec.size(), 1); 139 | BOOST_CHECK_EQUAL(dataVec[0].getName().toUri(), "/ndn/multicast/NTORRENT/linux15.01/ALIVE/test"); 140 | 141 | auto block = dataVec[0].getContent(); 142 | shared_ptr> nameVec = DummyParser::decodeContent(block); 143 | auto it = nameVec->begin(); 144 | 145 | BOOST_CHECK_EQUAL(it->toUri(), "/test"); 146 | ++it; 147 | BOOST_CHECK_EQUAL(it->toUri(), "/isp3"); 148 | ++it; 149 | BOOST_CHECK_EQUAL(it->toUri(), "/isp2"); 150 | ++it; 151 | BOOST_CHECK_EQUAL(it->toUri(), "/isp1"); 152 | 153 | BOOST_CHECK_EQUAL((table1->end() - 1)->getRecordName().toUri(), "/test"); 154 | 155 | d = DummyParser::createDataPacket(Name("ndn/multicast/NTORRENT/linux15.01/ALIVE/arizona"), 156 | { Name("isp1"), Name("isp2"), Name("isp3") }); 157 | keyChain->sign(*d); 158 | 159 | advanceClocks(time::milliseconds(1), 40); 160 | face2->receive(*d); 161 | 162 | auto i = table2->begin(); 163 | ++i; 164 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp1"); 165 | ++i; 166 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp2"); 167 | ++i; 168 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp3"); 169 | 170 | table1->erase(Name("isp1")); 171 | table1->erase(Name("isp2")); 172 | 173 | table1->insert(Name("isp4")); 174 | table1->insert(Name("isp5")); 175 | table1->insert(Name("isp6")); 176 | table1->insert(Name("isp7")); 177 | table1->insert(Name("isp8")); 178 | table1->insert(Name("isp9")); 179 | 180 | handler2.sendAliveInterest(table2->begin()); 181 | 182 | advanceClocks(time::milliseconds(1), 40); 183 | Interest interest2(Name("ndn/multicast/NTORRENT/linux15.01/ALIVE/arizona")); 184 | face1->receive(interest2); 185 | 186 | advanceClocks(time::milliseconds(1), 10); 187 | 188 | dataVec = face1->sentData; 189 | BOOST_CHECK_EQUAL(dataVec.size(), 2); 190 | 191 | auto iter = dataVec.begin() + 1; 192 | advanceClocks(time::milliseconds(1), 30); 193 | face2->receive(*iter); 194 | 195 | i = table2->begin(); 196 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/ucla"); 197 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0); 198 | BOOST_CHECK_EQUAL(i->getRecordSentInterests(), 0); 199 | BOOST_CHECK_EQUAL(i->getRecordReceivedData(), 0); 200 | ++i; 201 | 202 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp1"); 203 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0); 204 | BOOST_CHECK_EQUAL(i->getRecordSentInterests(), 0); 205 | BOOST_CHECK_EQUAL(i->getRecordReceivedData(), 0); 206 | ++i; 207 | 208 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp2"); 209 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0); 210 | BOOST_CHECK_EQUAL(i->getRecordSentInterests(), 0); 211 | BOOST_CHECK_EQUAL(i->getRecordReceivedData(), 0); 212 | ++i; 213 | 214 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp3"); 215 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0); 216 | BOOST_CHECK_EQUAL(i->getRecordSentInterests(), 0); 217 | BOOST_CHECK_EQUAL(i->getRecordReceivedData(), 0); 218 | ++i; 219 | 220 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/test"); 221 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0); 222 | BOOST_CHECK_EQUAL(i->getRecordSentInterests(), 0); 223 | BOOST_CHECK_EQUAL(i->getRecordReceivedData(), 0); 224 | ++i; 225 | 226 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp4"); 227 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0); 228 | BOOST_CHECK_EQUAL(i->getRecordSentInterests(), 0); 229 | BOOST_CHECK_EQUAL(i->getRecordReceivedData(), 0); 230 | ++i; 231 | 232 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp5"); 233 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0); 234 | BOOST_CHECK_EQUAL(i->getRecordSentInterests(), 0); 235 | BOOST_CHECK_EQUAL(i->getRecordReceivedData(), 0); 236 | ++i; 237 | 238 | BOOST_CHECK_EQUAL(i->getRecordName().toUri(), "/isp6"); 239 | BOOST_CHECK_EQUAL(i->getRecordSuccessRate(), 0); 240 | BOOST_CHECK_EQUAL(i->getRecordSentInterests(), 0); 241 | BOOST_CHECK_EQUAL(i->getRecordReceivedData(), 0); 242 | ++i; 243 | 244 | BOOST_CHECK(i == table2->end()); 245 | } 246 | 247 | BOOST_AUTO_TEST_CASE(TestNeedsUpdate) 248 | { 249 | shared_ptr table1 = make_shared(Name("linux15.01")); 250 | table1->insert(Name("isp1")); 251 | table1->insert(Name("isp2")); 252 | table1->insert(Name("isp3")); 253 | 254 | shared_ptr keyChain = make_shared(); 255 | 256 | UpdateHandler handler1(Name("linux15.01"), keyChain, table1, face1); 257 | 258 | BOOST_CHECK(handler1.needsUpdate()); 259 | 260 | auto i = table1->begin() + 1; 261 | i->incrementSentInterests(); 262 | i->incrementSentInterests(); 263 | i->incrementReceivedData(); 264 | 265 | BOOST_CHECK(handler1.needsUpdate()); 266 | 267 | table1->insert(Name("isp4")); 268 | table1->insert(Name("isp5")); 269 | 270 | BOOST_CHECK(!handler1.needsUpdate()); 271 | } 272 | 273 | BOOST_AUTO_TEST_SUITE_END() 274 | 275 | } // namespace tests 276 | } // namespace ntorrent 277 | } // namespace ndn 278 | -------------------------------------------------------------------------------- /src/torrent-file.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #include "torrent-file.hpp" 23 | #include "util/io-util.hpp" 24 | #include "util/shared-constants.hpp" 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | #include 32 | #include 33 | 34 | namespace fs = boost::filesystem; 35 | 36 | namespace ndn { 37 | namespace ntorrent { 38 | 39 | BOOST_CONCEPT_ASSERT((WireEncodable)); 40 | BOOST_CONCEPT_ASSERT((WireDecodable)); 41 | static_assert(std::is_base_of::value, 42 | "TorrentFile::Error should inherit from Data::Error"); 43 | 44 | TorrentFile::TorrentFile(const Name& torrentFileName, 45 | const Name& torrentFilePtr, 46 | const Name& commonPrefix, 47 | const std::vector& catalog) 48 | : Data(torrentFileName) 49 | , m_commonPrefix(commonPrefix) 50 | , m_torrentFilePtr(torrentFilePtr) 51 | , m_catalog(catalog) 52 | { 53 | } 54 | 55 | TorrentFile::TorrentFile(const Name& torrentFileName, 56 | const Name& commonPrefix, 57 | const std::vector& catalog) 58 | : Data(torrentFileName) 59 | , m_commonPrefix(commonPrefix) 60 | , m_catalog(catalog) 61 | { 62 | } 63 | 64 | TorrentFile::TorrentFile(const Block& block) 65 | { 66 | this->wireDecode(block); 67 | } 68 | 69 | void 70 | TorrentFile::createSuffixCatalog() 71 | { 72 | for (auto i = m_catalog.begin() ; i != m_catalog.end(); ++i) { 73 | m_suffixCatalog.push_back((*i).getSubName(m_commonPrefix.size())); 74 | } 75 | } 76 | 77 | shared_ptr 78 | TorrentFile::getTorrentFilePtr() const 79 | { 80 | if (this->hasTorrentFilePtr()) { 81 | return make_shared(m_torrentFilePtr); 82 | } 83 | return nullptr; 84 | } 85 | 86 | void 87 | TorrentFile::constructLongNames() 88 | { 89 | for (auto i = m_suffixCatalog.begin(); i != m_suffixCatalog.end(); ++i) { 90 | Name commonPrefix = m_commonPrefix; 91 | m_catalog.push_back(commonPrefix.append((*i))); 92 | } 93 | } 94 | 95 | template 96 | size_t 97 | TorrentFile::encodeContent(EncodingImpl& encoder) const 98 | { 99 | // TorrentFileContent ::= CONTENT-TYPE TLV-LENGTH 100 | // Suffix+ 101 | // CommonPrefix 102 | // TorrentFilePtr? 103 | 104 | // Suffix ::= NAME-TYPE TLV-LENGTH 105 | // Name 106 | 107 | // CommonPrefix ::= NAME-TYPE TLV-LENGTH 108 | // Name 109 | 110 | // TorrentFilePtr ::= NAME-TYPE TLV-LENGTH 111 | // Name 112 | 113 | size_t totalLength = 0; 114 | for (const auto& name : m_suffixCatalog | boost::adaptors::reversed) { 115 | size_t fileManifestSuffixLength = 0; 116 | fileManifestSuffixLength += name.wireEncode(encoder); 117 | totalLength += fileManifestSuffixLength; 118 | } 119 | totalLength += m_commonPrefix.wireEncode(encoder); 120 | if (!m_torrentFilePtr.empty()) { 121 | size_t torrentFilePtrLength = 0; 122 | torrentFilePtrLength += m_torrentFilePtr.wireEncode(encoder); 123 | totalLength += torrentFilePtrLength; 124 | } 125 | 126 | totalLength += encoder.prependVarNumber(totalLength); 127 | totalLength += encoder.prependVarNumber(tlv::Content); 128 | return totalLength; 129 | } 130 | 131 | bool 132 | TorrentFile::erase(const Name& name) 133 | { 134 | auto found = std::find(m_catalog.begin(), m_catalog.end(), name); 135 | if (found != m_catalog.end()) { 136 | m_catalog.erase(found); 137 | return true; 138 | } 139 | return false; 140 | } 141 | 142 | void 143 | TorrentFile::encodeContent() 144 | { 145 | resetWire(); 146 | 147 | EncodingEstimator estimator; 148 | size_t estimatedSize = encodeContent(estimator); 149 | 150 | EncodingBuffer buffer(estimatedSize, 0); 151 | encodeContent(buffer); 152 | 153 | setContentType(tlv::ContentType_Blob); 154 | setContent(buffer.block()); 155 | } 156 | 157 | void 158 | TorrentFile::decodeContent() 159 | { 160 | // TorrentFileContent ::= CONTENT-TYPE TLV-LENGTH 161 | // Suffix+ 162 | // CommonPrefix 163 | // TorrentFilePtr? 164 | 165 | // Suffix ::= NAME-TYPE TLV-LENGTH 166 | // Name 167 | 168 | // CommonPrefix ::= NAME-TYPE TLV-LENGTH 169 | // Name 170 | 171 | // TorrentFilePtr ::= NAME-TYPE TLV-LENGTH 172 | // Name 173 | 174 | if (getContentType() != tlv::ContentType_Blob) { 175 | BOOST_THROW_EXCEPTION(Error("Expected Content Type Blob")); 176 | } 177 | 178 | const Block& content = Data::getContent(); 179 | content.parse(); 180 | 181 | // Check whether there is a TorrentFilePtr 182 | auto element = content.elements_begin(); 183 | if (content.elements_end() == element) { 184 | BOOST_THROW_EXCEPTION(Error("Torrent-file with empty content")); 185 | } 186 | element->parse(); 187 | Name name(*element); 188 | if (name.empty()) 189 | BOOST_THROW_EXCEPTION(Error("Empty name included in the torrent-file")); 190 | 191 | if (name.get(name.size() - 3) == name::Component("torrent-file")) { 192 | m_torrentFilePtr = name; 193 | ++element; 194 | m_commonPrefix = Name(*element); 195 | if (m_commonPrefix.empty()) { 196 | BOOST_THROW_EXCEPTION(Error("Common prefix cannot be empty")); 197 | } 198 | } 199 | else { 200 | m_commonPrefix = name; 201 | } 202 | element++; 203 | for (; element != content.elements_end(); ++element) { 204 | element->parse(); 205 | Name fileManifestSuffix(*element); 206 | if (fileManifestSuffix.empty()) 207 | BOOST_THROW_EXCEPTION(Error("Empty manifest file name included in the torrent-file")); 208 | this->insertToSuffixCatalog(fileManifestSuffix); 209 | } 210 | if (m_suffixCatalog.size() == 0) { 211 | BOOST_THROW_EXCEPTION(Error("Torrent-file with empty catalog of file manifest names")); 212 | } 213 | } 214 | 215 | void 216 | TorrentFile::wireDecode(const Block& wire) 217 | { 218 | m_catalog.clear(); 219 | m_suffixCatalog.clear(); 220 | Data::wireDecode(wire); 221 | this->decodeContent(); 222 | this->constructLongNames(); 223 | } 224 | 225 | void 226 | TorrentFile::finalize() 227 | { 228 | this->createSuffixCatalog(); 229 | this->encodeContent(); 230 | m_suffixCatalog.clear(); 231 | } 232 | 233 | std::pair, 234 | std::vector, 235 | std::vector>>> 236 | TorrentFile::generate(const std::string& directoryPath, 237 | size_t namesPerSegment, 238 | size_t subManifestSize, 239 | size_t dataPacketSize, 240 | bool returnData) 241 | { 242 | //TODO(spyros) Adapt this support subdirectories in 'directoryPath' 243 | BOOST_ASSERT(0 < namesPerSegment); 244 | 245 | std::vector torrentSegments; 246 | 247 | fs::path path(directoryPath); 248 | if (!Io::exists(path)) { 249 | BOOST_THROW_EXCEPTION(Error(directoryPath + ": no such directory.")); 250 | } 251 | 252 | Name directoryPathName(directoryPath); 253 | Io::recursive_directory_iterator directoryPtr(fs::system_complete(directoryPath).string()); 254 | 255 | std::string prefix = std::string(SharedConstants::commonPrefix) + "/NTORRENT"; 256 | Name commonPrefix(prefix + 257 | directoryPathName.getSubName(directoryPathName.size() - 1).toUri()); 258 | 259 | Name torrentName(commonPrefix.toUri() + "/torrent-file"); 260 | TorrentFile currentTorrentFile(torrentName, commonPrefix, {}); 261 | std::vector, std::vector>> manifestPairs; 262 | // sort all the file names lexicographically 263 | std::set fileNames; 264 | for (auto i = directoryPtr; i != Io::recursive_directory_iterator(); ++i) { 265 | fileNames.insert(i->path().string()); 266 | } 267 | size_t manifestFileCounter = 0u; 268 | for (const auto& fileName : fileNames) { 269 | Name manifestPrefix(prefix + 270 | directoryPathName.getSubName(directoryPathName.size() - 1).toUri()); 271 | std::pair, std::vector> currentManifestPair = 272 | FileManifest::generate(fileName, 273 | manifestPrefix, subManifestSize, 274 | dataPacketSize, returnData); 275 | 276 | if (manifestFileCounter != 0 && 0 == manifestFileCounter % namesPerSegment) { 277 | torrentSegments.push_back(currentTorrentFile); 278 | Name currentTorrentName = torrentName; 279 | currentTorrentName.appendSequenceNumber(static_cast(manifestFileCounter)); 280 | currentTorrentFile = TorrentFile(currentTorrentName, commonPrefix, {}); 281 | } 282 | currentTorrentFile.insert(currentManifestPair.first[0].getFullName()); 283 | currentManifestPair.first.shrink_to_fit(); 284 | currentManifestPair.second.shrink_to_fit(); 285 | manifestPairs.push_back(currentManifestPair); 286 | ++manifestFileCounter; 287 | } 288 | 289 | // Sign and append the last torrent-file 290 | security::KeyChain keyChain; 291 | currentTorrentFile.finalize(); 292 | keyChain.sign(currentTorrentFile, signingWithSha256()); 293 | torrentSegments.push_back(currentTorrentFile); 294 | 295 | for (auto it = torrentSegments.rbegin() + 1; it != torrentSegments.rend(); ++it) { 296 | auto next = it - 1; 297 | it->setTorrentFilePtr(next->getFullName()); 298 | it->finalize(); 299 | keyChain.sign(*it, signingWithSha256()); 300 | } 301 | 302 | torrentSegments.shrink_to_fit(); 303 | manifestPairs.shrink_to_fit(); 304 | return std::make_pair(torrentSegments, manifestPairs); 305 | } 306 | 307 | } // namespace ntorrent 308 | 309 | } // namespace ndn 310 | -------------------------------------------------------------------------------- /src/file-manifest.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | #ifndef INCLUDED_FILE_MANIFEST_HPP 22 | #define INCLUDED_FILE_MANIFEST_HPP 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | #include "util/shared-constants.hpp" 34 | 35 | namespace ndn { 36 | namespace ntorrent { 37 | 38 | class FileManifest : public Data { 39 | /** 40 | * \class FileManifest 41 | * 42 | * \brief A value semantic type for File manifests 43 | * 44 | */ 45 | public: 46 | // TYPES 47 | class Error : public Data::Error 48 | { 49 | public: 50 | explicit 51 | Error(const std::string& what) 52 | : Data::Error(what) 53 | { 54 | } 55 | }; 56 | 57 | public: 58 | // CLASS METHODS 59 | static std::vector 60 | generate(const std::string& filePath, 61 | const ndn::Name& manifestPrefix, 62 | size_t subManifestSize, 63 | size_t dataPacketSize); 64 | 65 | 66 | static std::pair, std::vector> 67 | generate(const std::string& filePath, 68 | const ndn::Name& manifestPrefix, 69 | size_t subManifestSize, 70 | size_t dataPacketSize, 71 | bool returnData); 72 | 73 | static 74 | Name 75 | manifestPrefix(const Name& manifestName); 76 | /** 77 | * \brief Generates the FileManifest(s) and Data packets for the file at the specified 'filePath' 78 | * 79 | * @param filePath The path to the file for which we are to create a manifest 80 | * @param manifestPrefix The prefix to be used for the name of this manifest 81 | * @param subManifestSize The maximum number of data packets to be included in a sub-manifest 82 | * @param dataPacketSize The maximum number of bytes per Data packet packets for the file 83 | * @param returnData If true also return the Data 84 | * 85 | * @throws Error if there is any I/O issue when trying to read the filePath. 86 | * 87 | * Generates the FileManfiest(s) for the file at the specified 'filePath', splitting the manifest 88 | * into sub-manifests of size at most the specified 'subManifestSize'. Each sub-manifest is 89 | * composed of a catalog of Data packets of at most the specified 'dataPacketSize'. Returns all 90 | * of the manifests that were created in order. The behavior is undefined unless the 91 | * trailing component of of the manifestPrefix is a subComponent filePath and 92 | '* O < subManifestSize' and '0 < dataPacketSize'. 93 | */ 94 | 95 | // CREATORS 96 | FileManifest() = default; 97 | 98 | ~FileManifest() = default; 99 | /// Destroy this object 100 | 101 | FileManifest(const Name& name, 102 | size_t dataPacketSize, 103 | const Name& commonPrefix, 104 | const std::vector& catalog = std::vector(), 105 | std::shared_ptr subManifestPtr = nullptr); 106 | /** 107 | * \brief Creates a new FileManifest with the specified values 108 | * 109 | * @param name The Name of this FileManifest 110 | * @param dataPacketSize The size (except the last) of each data packet in this FileManifest 111 | * @param commonPrefix The common prefix used for all named in the catalog 112 | * @param catalog The collection of Names for this FileManifest 113 | * @param subManifestPtr (optional) The Name for the sub-manifest in this for this file 114 | */ 115 | 116 | FileManifest(const Name& name, 117 | size_t dataPacketSize, 118 | const Name& catalogPrefix, 119 | std::vector&& catalog, 120 | std::shared_ptr subManifestPtr = nullptr); 121 | /** 122 | * \brief Creates a new FileManifest with the specified values 123 | * 124 | * @param name The Name of this FileManifest 125 | * @param dataPacketSize The size (except the last) of each data packet in this FileManifest 126 | * @param catalogPrefix the common prefix used for all named in the catalog 127 | * @param catalog The collection of Names for this FileManifest 128 | * @param subManifestPtr (optional) The Name for the sub-manifest in this for this file 129 | */ 130 | 131 | explicit 132 | FileManifest(const Block& block); 133 | 134 | FileManifest(const FileManifest& other) = default; 135 | /// Creates a new FileManifest with same value as the specified 'other' 136 | 137 | // ACCESSORS 138 | const Name& 139 | name() const; 140 | /// Returns the 'name' of this FileManifest 141 | 142 | const Name& 143 | catalog_prefix() const; 144 | /// Returns the 'catalogPrefix' of this FileManfiest. 145 | 146 | size_t 147 | data_packet_size() const; 148 | /// Returns the 'data_packet_size' of this FileManifest 149 | 150 | size_t 151 | submanifest_number() const; 152 | /// Return the submanifest number for this FileManifest 153 | 154 | std::string 155 | file_name() const; 156 | /// Return the file name for this FileManifest 157 | 158 | std::shared_ptr 159 | submanifest_ptr() const; 160 | /// Returns the 'submanifest_ptr' of this FileManifest, or 'nullptr' is none exists 161 | 162 | const std::vector& 163 | catalog() const; 164 | /// Returns an unmodifiable reference to the 'catalog' of this FileManifest 165 | 166 | private: 167 | template 168 | size_t 169 | encodeContent(EncodingImpl& encoder) const; 170 | /// Encodes the contents of this object and append the contents to the 'encoder' 171 | 172 | public: 173 | // MANIPULATORS 174 | FileManifest& 175 | operator=(const FileManifest& rhs) = default; 176 | /// Assigns the value of the specific 'rhs' object to this object. 177 | 178 | void 179 | set_submanifest_ptr(std::shared_ptr subManifestPtr); 180 | /// Sets the sub-manifest pointer of manifest to the specified 'subManifestPtr' 181 | 182 | void 183 | push_back(const Name& name); 184 | /// Appends a Name to the catalog 185 | 186 | void 187 | reserve(size_t capacity); 188 | /// Reserve memory in the catalog adequate to hold at least 'capacity' Names. 189 | 190 | bool 191 | remove(const Name& name); 192 | /// If 'name' in catalog, removes first instance and returns 'true', otherwise returns 'false'. 193 | 194 | void 195 | wireDecode(const Block& wire); 196 | /** 197 | * \brief Decodes the wire and assign its contents to this FileManifest 198 | * 199 | * @param wire the write to be decoded 200 | * @throws Error if decoding fails 201 | */ 202 | 203 | void 204 | finalize(); 205 | /* 206 | * \brief Performs all final processing on this manifest 207 | * 208 | * This method should be called once this manifest is populated and *must* be called before 209 | * signing it or calling wireEncode(). 210 | */ 211 | 212 | private: 213 | void 214 | decodeContent(); 215 | /// Decodes the contents of this Data packet, assigning its contents to this FileManifest. 216 | 217 | void 218 | encodeContent(); 219 | /// Encodes the contents of this FileManifest into the content section of its Data packet. 220 | 221 | // DATA 222 | private: 223 | size_t m_dataPacketSize; 224 | Name m_catalogPrefix; 225 | std::vector m_catalog; 226 | std::shared_ptr m_submanifestPtr; 227 | }; 228 | 229 | /// Non-member functions 230 | bool operator==(const FileManifest& lhs, const FileManifest& rhs); 231 | /// Returns 'true' if 'lhs' and 'rhs' have the same value, 'false' otherwise. 232 | 233 | bool operator!=(const FileManifest& lhs, const FileManifest& rhs); 234 | /// Returns 'true' if 'lhs' and 'rhs' have different values, and 'false' otherwise. 235 | 236 | inline 237 | std::vector 238 | FileManifest::generate(const std::string& filePath, 239 | const ndn::Name& manifestPrefix, 240 | size_t subManifestSize, 241 | size_t dataPacketSize) 242 | { 243 | return generate(filePath, manifestPrefix, subManifestSize, dataPacketSize, false).first; 244 | } 245 | 246 | inline 247 | Name 248 | FileManifest::manifestPrefix(const Name& manifestName) { 249 | return manifestName.getSubName(0, manifestName.size() - 2); 250 | } 251 | 252 | inline 253 | FileManifest::FileManifest( 254 | const Name& name, 255 | size_t dataPacketSize, 256 | const Name& catalogPrefix, 257 | const std::vector& catalog, 258 | std::shared_ptr subManifestPtr) 259 | : Data(name) 260 | , m_dataPacketSize(dataPacketSize) 261 | , m_catalogPrefix(catalogPrefix) 262 | , m_catalog(catalog) 263 | , m_submanifestPtr(subManifestPtr) 264 | { 265 | } 266 | 267 | 268 | inline 269 | FileManifest::FileManifest( 270 | const Name& name, 271 | size_t dataPacketSize, 272 | const Name& catalogPrefix, 273 | std::vector&& catalog, 274 | std::shared_ptr subManifestPtr) 275 | : Data(name) 276 | , m_dataPacketSize(dataPacketSize) 277 | , m_catalogPrefix(catalogPrefix) 278 | , m_catalog(catalog) 279 | , m_submanifestPtr(subManifestPtr) 280 | { 281 | } 282 | 283 | inline 284 | FileManifest::FileManifest(const Block& block) 285 | : Data() 286 | , m_dataPacketSize(0) 287 | , m_catalogPrefix("") 288 | , m_catalog() 289 | , m_submanifestPtr(nullptr) 290 | { 291 | wireDecode(block); 292 | } 293 | 294 | inline const Name& 295 | FileManifest::name() const 296 | { 297 | return getName(); 298 | } 299 | 300 | inline size_t 301 | FileManifest::data_packet_size() const 302 | { 303 | return m_dataPacketSize; 304 | } 305 | 306 | inline const Name& 307 | FileManifest::catalog_prefix() const 308 | { 309 | return m_catalogPrefix; 310 | } 311 | 312 | inline const std::vector& 313 | FileManifest::catalog() const 314 | { 315 | return m_catalog; 316 | } 317 | 318 | inline std::shared_ptr 319 | FileManifest::submanifest_ptr() const 320 | { 321 | return m_submanifestPtr; 322 | } 323 | 324 | inline size_t 325 | FileManifest::submanifest_number() const 326 | { 327 | return name().get(name().size() - 1).toSequenceNumber(); 328 | } 329 | 330 | inline void 331 | FileManifest::set_submanifest_ptr(std::shared_ptr subManifestPtr) 332 | { 333 | m_submanifestPtr = subManifestPtr; 334 | } 335 | 336 | inline void 337 | FileManifest::reserve(size_t capacity) 338 | { 339 | m_catalog.reserve(capacity); 340 | } 341 | 342 | } // end ntorrent 343 | } // end ndn 344 | 345 | #endif // INCLUDED_FILE_MANIFEST_HPP 346 | -------------------------------------------------------------------------------- /src/file-manifest.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | #include "file-manifest.hpp" 22 | 23 | #include "util/io-util.hpp" 24 | #include "util/shared-constants.hpp" 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | 39 | using std::vector; 40 | using std::streamsize; 41 | using boost::irange; 42 | 43 | namespace fs = boost::filesystem; 44 | 45 | namespace ndn { 46 | namespace ntorrent { 47 | 48 | BOOST_CONCEPT_ASSERT((boost::EqualityComparable)); 49 | BOOST_CONCEPT_ASSERT((WireEncodable)); 50 | BOOST_CONCEPT_ASSERT((WireDecodable)); 51 | static_assert(std::is_base_of::value, 52 | "FileManifest::Error should inherit from Data::Error"); 53 | 54 | static ndn::Name 55 | get_name_of_manifest(const std::string& filePath, const Name& manifestPrefix) 56 | { 57 | Name full_path(fs::system_complete(filePath).string()); 58 | // Search the filePath for the leading component that matches 59 | auto name_component_iter = std::find(full_path.rbegin(), 60 | full_path.rend(), 61 | *manifestPrefix.rbegin()); 62 | 63 | if (full_path.rend() == name_component_iter) { 64 | BOOST_THROW_EXCEPTION(FileManifest::Error("No matching name component between" + 65 | manifestPrefix.toUri() + " and " + 66 | full_path.toUri())); 67 | } 68 | ndn::Name manifestName(SharedConstants::commonPrefix + std::string("/NTORRENT/")); 69 | // Rebuild the name to be the suffix from the matching component 70 | for (auto it = (name_component_iter.base() - 1); full_path.end() != it; ++it) { 71 | manifestName.append(*it); 72 | } 73 | return manifestName; 74 | } 75 | 76 | // CLASS METHODS 77 | std::pair, std::vector> 78 | FileManifest::generate(const std::string& filePath, 79 | const Name& manifestPrefix, 80 | size_t subManifestSize, 81 | size_t dataPacketSize, 82 | bool returnData) 83 | { 84 | BOOST_ASSERT(0 < subManifestSize); 85 | BOOST_ASSERT(0 < dataPacketSize); 86 | std::vector manifests; 87 | fs::path path(filePath); 88 | if (!Io::exists(path)) { 89 | BOOST_THROW_EXCEPTION(Error(filePath + ": no such file.")); 90 | } 91 | size_t file_length = Io::file_size(filePath); 92 | // If the file_length is not evenly divisible by subManifestSize add 1, otherwise 0 93 | size_t numSubManifests = file_length / (subManifestSize * dataPacketSize) + 94 | !!(file_length % (subManifestSize * dataPacketSize)); 95 | // Find the prefix for the Catalog 96 | auto manifestName = get_name_of_manifest(filePath, manifestPrefix); 97 | std::vector allPackets; 98 | if (returnData) { 99 | allPackets.reserve(numSubManifests * subManifestSize); 100 | } 101 | manifests.reserve(numSubManifests); 102 | for (auto subManifestNum : irange(0, numSubManifests)) { 103 | auto curr_manifest_name = manifestName; 104 | // append the packet number 105 | curr_manifest_name.appendSequenceNumber(manifests.size()); 106 | FileManifest curr_manifest(curr_manifest_name, dataPacketSize, manifestPrefix); 107 | auto packets = IoUtil::packetize_file(path, 108 | curr_manifest_name, 109 | dataPacketSize, 110 | subManifestSize, 111 | subManifestNum); 112 | if (returnData) { 113 | allPackets.insert(allPackets.end(), packets.begin(), packets.end()); 114 | } 115 | curr_manifest.reserve(packets.size()); 116 | // Collect all the Data packets into the sub-manifests 117 | for (const auto& p: packets) { 118 | curr_manifest.push_back(p.getFullName()); 119 | } 120 | // append the last manifest 121 | manifests.push_back(curr_manifest); 122 | } 123 | allPackets.shrink_to_fit(); 124 | manifests.shrink_to_fit(); 125 | // Set all the submanifest_ptrs and sign all the manifests 126 | security::KeyChain key_chain; 127 | manifests.back().finalize(); 128 | key_chain.sign(manifests.back(), signingWithSha256()); 129 | for (auto it = manifests.rbegin() + 1; it != manifests.rend(); ++it) { 130 | auto next = it - 1; 131 | it->set_submanifest_ptr(std::make_shared(next->getFullName())); 132 | it->finalize(); 133 | key_chain.sign(*it, signingWithSha256()); 134 | } 135 | return {manifests, allPackets}; 136 | } 137 | 138 | void 139 | FileManifest::wireDecode(const Block& wire) 140 | { 141 | Data::wireDecode(wire); 142 | this->decodeContent(); 143 | } 144 | 145 | template 146 | size_t 147 | FileManifest::encodeContent(ndn::EncodingImpl& encoder) const { 148 | // ManifestContent ::= CONTENT-TYPE TLV-LENGTH 149 | // DataPacketName* 150 | // CatalogPrefix 151 | // DataPacketSize 152 | // FileManifestPtr? 153 | 154 | // DataPacketName ::= NAME-TYPE TLV-LENGTH 155 | // Name 156 | 157 | // CatalogPrefix ::= NAME-TYPE TLV-LENGTH 158 | // Name 159 | 160 | // DataPacketSize ::= CONTENT-TYPE TLV-LENGTH 161 | // nonNegativeInteger 162 | 163 | // FileManifestPtr ::= NAME-TYPE TLV-LENGTH 164 | // Name 165 | 166 | size_t totalLength = 0; 167 | 168 | // build suffix catalog 169 | vector suffixCatalog; 170 | suffixCatalog.reserve(m_catalog.size()); 171 | for (auto name: m_catalog) { 172 | if (!m_catalogPrefix.isPrefixOf(name)) { 173 | BOOST_THROW_EXCEPTION(Error(name.toUri() + " does not have the prefix " 174 | + m_catalogPrefix.toUri())); 175 | } 176 | name = name.getSubName(m_catalogPrefix.size()); 177 | if (name.empty()) { 178 | BOOST_THROW_EXCEPTION(Error("Manifest cannot include empty string")); 179 | } 180 | suffixCatalog.push_back(name); 181 | } 182 | 183 | for (const auto& name : suffixCatalog | boost::adaptors::reversed) { 184 | totalLength += name.wireEncode(encoder); 185 | } 186 | 187 | totalLength += m_catalogPrefix.wireEncode(encoder); 188 | 189 | totalLength += prependNonNegativeIntegerBlock(encoder, tlv::Content, m_dataPacketSize); 190 | 191 | if (nullptr != m_submanifestPtr) { 192 | totalLength += m_submanifestPtr->wireEncode(encoder); 193 | } 194 | 195 | totalLength += encoder.prependVarNumber(totalLength); 196 | totalLength += encoder.prependVarNumber(tlv::Content); 197 | return totalLength; 198 | 199 | } 200 | 201 | // MANIPULATORS 202 | void 203 | FileManifest::push_back(const Name& name) 204 | { 205 | BOOST_ASSERT(name != m_catalogPrefix); 206 | BOOST_ASSERT(m_catalogPrefix.isPrefixOf(name)); 207 | m_catalog.push_back(name.toUri()); 208 | } 209 | 210 | bool 211 | FileManifest::remove(const ndn::Name& name) { 212 | const auto it = std::find(m_catalog.begin(), m_catalog.end(), name); 213 | if (m_catalog.end() == it) { 214 | return false; 215 | } 216 | m_catalog.erase(it); 217 | return true; 218 | } 219 | 220 | void 221 | FileManifest::finalize() { 222 | m_catalog.shrink_to_fit(); 223 | encodeContent(); 224 | } 225 | 226 | void FileManifest::encodeContent() { 227 | // Name 228 | // /ImplicitDigest 229 | // Content 230 | // MetaData 231 | // DataPtr* 232 | // ManifestPointer? 233 | // 234 | // DataPtr := HashValue 235 | // ManifestPtr := HashValue 236 | // HashValue := OCTET[32] 237 | 238 | // MetaData := Property* 239 | // Property := DataSize | Signature 240 | resetWire(); 241 | 242 | EncodingEstimator estimator; 243 | size_t estimatedSize = encodeContent(estimator); 244 | 245 | EncodingBuffer buffer(estimatedSize, 0); 246 | encodeContent(buffer); 247 | 248 | setContentType(tlv::ContentType_Blob); 249 | setContent(buffer.block()); 250 | } 251 | 252 | void 253 | FileManifest::decodeContent() { 254 | // ManifestContent ::= CONTENT-TYPE TLV-LENGTH 255 | // DataPacketName* 256 | // CatalogPrefix 257 | // DataPacketSize 258 | // FileManifestPtr? 259 | 260 | 261 | // DataPacketName ::= NAME-TYPE TLV-LENGTH 262 | // Name 263 | 264 | // CatalogPrefix ::= NAME-TYPE TLV-LENGTH 265 | // Name 266 | 267 | // DataPacketSize ::= CONTENT-TYPE TLV-LENGTH 268 | // nonNegativeInteger 269 | 270 | // FileManifestPtr ::= NAME-TYPE TLV-LENGTH 271 | // Name 272 | 273 | if (getContentType() != tlv::ContentType_Blob) { 274 | BOOST_THROW_EXCEPTION(Error("Expected Content Type Blob")); 275 | } 276 | 277 | const Block& content = Data::getContent(); 278 | content.parse(); 279 | 280 | auto element = content.elements_begin(); 281 | if (content.elements_end() == element) { 282 | BOOST_THROW_EXCEPTION(Error("FileManifest with empty content")); 283 | } 284 | if (element->type() == tlv::Name) { 285 | Name name(*element); 286 | m_submanifestPtr = std::make_shared(name); 287 | ++element; 288 | } 289 | 290 | // DataPacketSize 291 | m_dataPacketSize = readNonNegativeInteger(*element); 292 | ++element; 293 | // CatalogPrefix 294 | m_catalogPrefix = Name(*element); 295 | ++element; 296 | // Catalog 297 | m_catalog.clear(); 298 | for (; element != content.elements_end(); ++element) { 299 | element->parse(); 300 | Name name = m_catalogPrefix; 301 | name.append(Name(*element)); 302 | if (name == m_catalogPrefix) { 303 | BOOST_THROW_EXCEPTION(Error("Empty name included in a FileManifest")); 304 | } 305 | push_back(name); 306 | } 307 | } 308 | 309 | bool operator==(const FileManifest& lhs, const FileManifest& rhs) { 310 | return lhs.name() == rhs.name() 311 | && lhs.data_packet_size() == rhs.data_packet_size() 312 | && (lhs.submanifest_ptr() == rhs.submanifest_ptr() /* shallow equality */ 313 | || ( nullptr != lhs.submanifest_ptr() 314 | && nullptr != rhs.submanifest_ptr() 315 | && *rhs.submanifest_ptr() == *lhs.submanifest_ptr() 316 | ) 317 | ) 318 | && lhs.catalog() == rhs.catalog(); 319 | } 320 | 321 | bool operator!=(const FileManifest& lhs, const FileManifest& rhs) { 322 | return lhs.name() != rhs.name() 323 | || lhs.data_packet_size() != rhs.data_packet_size() 324 | || (lhs.submanifest_ptr() != rhs.submanifest_ptr() /* shallow equality */ 325 | && (nullptr == lhs.submanifest_ptr() 326 | || nullptr == rhs.submanifest_ptr() 327 | || *rhs.submanifest_ptr() != *lhs.submanifest_ptr() 328 | ) 329 | ) 330 | || lhs.catalog() != rhs.catalog(); 331 | } 332 | 333 | std::string 334 | FileManifest::file_name() const 335 | { 336 | Name scheme(SharedConstants::commonPrefix); 337 | return name().getSubName(1 + scheme.size(), 338 | name().size() - (2 + scheme.size())).toUri(); 339 | } 340 | 341 | 342 | } // end ntorrent 343 | } // end ndn 344 | -------------------------------------------------------------------------------- /src/torrent-manager.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #ifndef INCLUDED_TORRENT_FILE_MANAGER_H 23 | #define INCLUDED_TORRENT_FILE_MANAGER_H 24 | 25 | #include "file-manifest.hpp" 26 | #include "interest-queue.hpp" 27 | #include "torrent-file.hpp" 28 | #include "update-handler.hpp" 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | namespace ndn { 45 | namespace ntorrent { 46 | 47 | class TorrentManager : noncopyable { 48 | /** 49 | * \class TorrentManager 50 | * 51 | * \brief A class to manage the interaction with the system in seeding/leaching a torrent 52 | */ 53 | public: 54 | typedef std::function DataReceivedCallback; 55 | typedef std::function&)> ManifestReceivedCallback; 56 | typedef std::function&)> TorrentFileReceivedCallback; 57 | typedef std::function FailedCallback; 58 | typedef std::tuple PendingInterestQueueEntry; 59 | typedef std::unordered_map PendingInterestQueue; 60 | 61 | /* 62 | * \brief Create a new Torrent manager with the specified parameters. 63 | * @param torrentFileName The full name of the initial segment of the torrent file 64 | * @param dataPath The path to the location on disk to use for the torrent data 65 | * @param face Optional face object to be used for data retrieval 66 | * 67 | * The behavior is undefined unless Initialize() is called before calling any other method on a 68 | * TorrentManger object. 69 | */ 70 | TorrentManager(const ndn::Name& torrentFileName, 71 | const std::string& dataPath, 72 | bool seed = true, 73 | std::shared_ptr face = nullptr); 74 | 75 | /* 76 | * @brief Initialize the state of this object. 77 | * 78 | * Read and validate from disk all torrent file segments, file manifests, and data packets for 79 | * the torrent file managed by this object initializing all state in this manager respectively. 80 | * Also seeds all validated data. 81 | */ 82 | void 83 | Initialize(); 84 | 85 | /** 86 | * brief Return 'true' if all segments of the torrent file downloaded, 'false' otherwise. 87 | */ 88 | bool 89 | hasAllTorrentSegments() const; 90 | 91 | /* 92 | * @brief Return 'true' if all segments of this file manifest are downloaded, 'false' otherwise. 93 | */ 94 | bool 95 | hasAllManifestSegments(const Name& manifestName) const; 96 | 97 | /* 98 | * \brief Given a data packet name, find whether we have already downloaded this packet 99 | * @param dataName The name of the data packet to download 100 | * @return True if we have already downloaded this packet, false otherwise 101 | * 102 | */ 103 | bool 104 | hasDataPacket(const Name& dataName) const; 105 | 106 | /* 107 | * \brief Find the torrent file segment that we should download 108 | * (either we have nothing or we have them all) 109 | * @return A shared_ptr to the name of the segment to download or 110 | * nullptr if we have all the segments 111 | * 112 | */ 113 | shared_ptr 114 | findTorrentFileSegmentToDownload() const; 115 | 116 | /* 117 | * \brief Given a file manifest segment name, find the next file manifest segment 118 | * that we should download 119 | * @param manifestName The name of the file manifest segment that we want to download 120 | * @return A shared_ptr to the name of the segment to download or 121 | * nullptr if we have all the segments 122 | * 123 | */ 124 | shared_ptr 125 | findManifestSegmentToDownload(const Name& manifestName) const; 126 | 127 | /* 128 | * \brief Find the segments of all the file manifests that we are missing 129 | * @param manifestNames The name of the manifest file segments to download (currently missing) 130 | * This parameter is used as an output vector of names 131 | */ 132 | void 133 | findFileManifestsToDownload(std::vector& manifestNames) const; 134 | 135 | /* 136 | * \brief Find the names of the data packets of a file manifest that we are currently missing 137 | * @param manifestName The name of the manifest 138 | * @param packetNames The name of the data packets to be downloaded 139 | * (used as an output vector of names) 140 | * 141 | * No matter what segment of a manifest file the manifestName parameter might refer to, the 142 | * missing data packets starting from the first segment of this manifest file would be returned 143 | * 144 | */ 145 | void 146 | findDataPacketsToDownload(const Name& manifestName, std::vector& packetNames) const; 147 | 148 | /* 149 | * \brief Find all the data packets that we are currently missing 150 | * @param packetNames The name of the data packets to be downloaded 151 | * (used as an output vector of names) 152 | * 153 | */ 154 | void 155 | findAllMissingDataPackets(std::vector& packetNames) const; 156 | 157 | bool 158 | hasPendingInterests() const; 159 | 160 | /* 161 | * @brief Stop all network activities of this manager 162 | */ 163 | void 164 | shutdown(); 165 | /* 166 | * @brief Download the torrent file 167 | * @param path The path to write the downloaded segments 168 | * @param onSuccess Callback to be called if we successfully download all the 169 | * segments of the torrent file. It passes the name of the file manifest 170 | * to be downloaded to the callback 171 | * @param onFailed Callaback to be called if we fail to download a segment of 172 | * the torrent file. It passes the name of the torrent file segment that 173 | * failed to download and a failure reason to the callback 174 | * 175 | * This method provides non-blocking downloading of all the torrent file segments 176 | */ 177 | void 178 | downloadTorrentFile(const std::string& path, 179 | TorrentFileReceivedCallback onSuccess = {}, 180 | FailedCallback onFailed = {}); 181 | 182 | /* 183 | * @brief Download a file manifest 184 | * @param manifestName The name of the manifest file to be downloaded 185 | * @param path The path to write the downloaded segments 186 | * @param onSuccess Callback to be called if we successfully download all the 187 | * segments of the file manifest. It passes the names of the data packets 188 | * to be downloaded to the callback 189 | * @param onFailed Callaback to be called if we fail to download a segment of 190 | * the file manifest. It passes the name of the data packet that failed 191 | * to download and a failure reason 192 | * 193 | * This method provides non-blocking downloading of all the file manifest segments 194 | * 195 | */ 196 | void 197 | download_file_manifest(const Name& manifestName, 198 | const std::string& path, 199 | ManifestReceivedCallback onSuccess, 200 | FailedCallback onFailed); 201 | 202 | /* 203 | * @brief Download a data packet 204 | * @param packetName The name of the data packet to be downloaded 205 | * @param onSuccess Callback to be called if we successfully download the data packet. 206 | * It passes the name of the data packet to the callback 207 | * @param onFailed Callaback to be called if we fail to download the requested data packet 208 | * It passes the name of the data packet to the callback and a failure reason 209 | * 210 | * This method writes the downloaded data packet to m_dataPath on disk 211 | * 212 | */ 213 | void 214 | download_data_packet(const Name& packetName, 215 | DataReceivedCallback onSuccess, 216 | FailedCallback onFailed); 217 | 218 | // Seed the specified 'data' to the network. 219 | void 220 | seed(const Data& data); 221 | 222 | /** 223 | * @brief Process any data to receive or call timeout callbacks and update prefix list (if needed) 224 | * By default this blocks until all operations are complete. 225 | */ 226 | void 227 | processEvents(const time::milliseconds& timeout = time::milliseconds(0)); 228 | 229 | protected: 230 | /** 231 | * \brief Write @p packet composed of torrent date to disk. 232 | * @param packet The data packet to be written to the disk 233 | * Write the Data packet to disk, return 'true' if data successfully written to disk 'false' 234 | * otherwise. Behavior is undefined unless the corresponding file manifest has already been 235 | * downloaded. 236 | */ 237 | bool 238 | writeData(const Data& packet); 239 | 240 | /* 241 | * \brief Write the @p segment torrent segment to disk at the specified path. 242 | * @param segment The torrent file segment to be written to disk 243 | * @param path The path at which to write the torrent file segment 244 | * Write the segment to disk, return 'true' if data successfully written to disk 'false' 245 | * otherwise. Behavior is undefined unless @segment is a correct segment for the torrent file of 246 | * this manager and @p path is the directory used for all segments of this torrent file. 247 | */ 248 | bool 249 | writeTorrentSegment(const TorrentFile& segment, const std::string& path); 250 | 251 | /* 252 | * \brief Write the @p manifest file manifest to disk at the specified @p path. 253 | * @param manifest The file manifest to be written to disk 254 | * @param path The path at which to write the file manifest 255 | * Write the file manifest to disk, return 'true' if data successfully written to disk 'false' 256 | * otherwise. Behavior is undefined unless @manifest is a correct file manifest for a file in the 257 | * torrent file of this manager and @p path is the directory used for all file manifests of this 258 | * torrent file. 259 | */ 260 | bool 261 | writeFileManifest(const FileManifest& manifest, const std::string& path); 262 | 263 | /* 264 | * \brief Download the segments of the torrent file 265 | * @param name The name of the torrent file to be downloaded 266 | * @param path The path to write the torrent file on disk 267 | * @param onSuccess Optional callback to be called when all the segments of the torrent file 268 | * have been downloaded. The default value is an empty callback. 269 | * @param onFailed Optional callback to be called when we fail to download a segment of the 270 | * torrent file. The default value is an empty callback. 271 | * 272 | */ 273 | void 274 | downloadTorrentFileSegment(const ndn::Name& name, 275 | const std::string& path, 276 | TorrentFileReceivedCallback onSuccess, 277 | FailedCallback onFailed); 278 | 279 | /* 280 | * \brief Download the segments of a file manifest 281 | * @param manifestName The name of the file manifest to be downloaded 282 | * @param path The path to write the file manifest on disk 283 | * @param packetNames A vector containing the name of the data packets in the file manifest 284 | * @param onSuccess Callback to be called when all the segments of the file manifest have been 285 | * downloaded 286 | * @param onFailed Callback to be called when we fail to download a file manifest segment 287 | * 288 | */ 289 | void 290 | downloadFileManifestSegment(const Name& manifestName, 291 | const std::string& path, 292 | std::shared_ptr> packetNames, 293 | ManifestReceivedCallback onSuccess, 294 | FailedCallback onFailed); 295 | 296 | enum { 297 | // Number of times to retry if a routable prefix fails to retrieve data 298 | MAX_NUM_OF_RETRIES = 5, 299 | // Number of Interests to be sent before sorting the stats table 300 | SORTING_INTERVAL = 100, 301 | // Maximum window size used for sending new Interests out 302 | WINDOW_SIZE = 50 303 | }; 304 | 305 | void onDataReceived(const Data& data); 306 | 307 | void 308 | onInterestReceived(const InterestFilter& filter, const Interest& interest); 309 | 310 | void 311 | onRegisterFailed(const Name& prefix, const std::string& reason); 312 | 313 | void 314 | eraseOwnRoutablePrefix(); 315 | 316 | protected: 317 | // A map from each fileManifest a bitmap of which Data packets this manager currently has 318 | mutable std::unordered_map> m_fileStates; 319 | // A map for each initial manifest to the size for the sub-manifest 320 | std::unordered_map m_subManifestSizes; 321 | // The segments of the TorrentFile this manager has 322 | std::vector m_torrentSegments; 323 | // The FileManifests this manager has 324 | std::vector m_fileManifests; 325 | // The name of the initial segment of the torrent file for this manager 326 | Name m_torrentFileName; 327 | // The path to the location on disk of the Data packet for this manager 328 | std::string m_dataPath; 329 | 330 | private: 331 | shared_ptr 332 | createInterest(Name name); 333 | 334 | void 335 | sendInterest(); 336 | 337 | void 338 | nackCallBack(const Interest& i, const lp::Nack& n); 339 | 340 | // A flag to determine if upon completion we should continue seeding 341 | bool m_seedFlag; 342 | // Face used for network communication 343 | std::shared_ptr m_face; 344 | // Stats table where routable prefixes are stored 345 | StatsTable m_statsTable; 346 | // Iterator to the routable prefix that we currently use 347 | StatsTable::iterator m_stats_table_iter; 348 | // Number of retries per routable prefix 349 | uint64_t m_retries; 350 | // Number of Interests sent since last sorting 351 | uint64_t m_sortingCounter; 352 | // Keychain instance 353 | shared_ptr m_keyChain; 354 | // A collection for all interests that have been sent for which we have not received a response 355 | PendingInterestQueue m_pendingInterests; 356 | // A queue to hold all interests for requested data that we have yet to send 357 | shared_ptr m_interestQueue; 358 | // TODO(spyros) Fix and reintegrate update handler 359 | // // Update Handler instance 360 | shared_ptr m_updateHandler; 361 | }; 362 | 363 | inline 364 | TorrentManager::TorrentManager(const ndn::Name& torrentFileName, 365 | const std::string& dataPath, 366 | bool seed, 367 | std::shared_ptr face) 368 | : m_fileStates() 369 | , m_torrentSegments() 370 | , m_fileManifests() 371 | , m_torrentFileName(torrentFileName) 372 | , m_dataPath(dataPath) 373 | , m_seedFlag(seed) 374 | , m_face(face) 375 | , m_retries(0) 376 | , m_sortingCounter(0) 377 | , m_keyChain(new KeyChain()) 378 | { 379 | m_interestQueue = make_shared(); 380 | 381 | if(face == nullptr) { 382 | m_face = make_shared(); 383 | } 384 | 385 | // Hardcoded prefixes for now 386 | // TODO(Spyros): Think of something more clever to bootstrap... 387 | m_statsTable.insert("ndn/edu/wustl"); 388 | m_statsTable.insert("ndn/edu/wustl"); 389 | m_stats_table_iter = m_statsTable.begin(); 390 | } 391 | 392 | inline 393 | void 394 | TorrentManager::processEvents(const time::milliseconds& timeout) 395 | { 396 | m_face->processEvents(timeout); 397 | } 398 | 399 | inline 400 | bool 401 | TorrentManager::hasAllTorrentSegments() const 402 | { 403 | return findTorrentFileSegmentToDownload() == nullptr; 404 | } 405 | 406 | inline 407 | bool 408 | TorrentManager::hasAllManifestSegments(const Name& manifestName) const 409 | { 410 | return nullptr == findManifestSegmentToDownload(manifestName); 411 | } 412 | 413 | inline 414 | bool 415 | TorrentManager::hasPendingInterests() const 416 | { 417 | return !m_pendingInterests.empty() || !m_interestQueue->empty(); 418 | } 419 | 420 | } // end ntorrent 421 | } // end ndn 422 | 423 | #endif // INCLUDED_TORRENT_FILE_MANAGER_H 424 | -------------------------------------------------------------------------------- /tests/unit-tests/file-manifest.t.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /** 3 | * Copyright (c) 2016 Regents of the University of California. 4 | * 5 | * This file is part of the nTorrent codebase. 6 | * 7 | * nTorrent is free software: you can redistribute it and/or modify it under the 8 | * terms of the GNU Lesser General Public License as published by the Free Software 9 | * Foundation, either version 3 of the License, or (at your option) any later version. 10 | * 11 | * nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received copies of the GNU General Public License and GNU Lesser 16 | * General Public License along with nTorrent, e.g., in COPYING.md file. If not, see 17 | * . 18 | * 19 | * See AUTHORS for complete list of nTorrent authors and contributors. 20 | */ 21 | 22 | #include "file-manifest.hpp" 23 | #include "boost-test.hpp" 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | namespace fs = boost::filesystem; 38 | 39 | //BOOST_TEST_DONT_PRINT_LOG_VALUE(std::nullptr_t) 40 | BOOST_TEST_DONT_PRINT_LOG_VALUE(std::vector) 41 | BOOST_TEST_DONT_PRINT_LOG_VALUE(std::vector) 42 | BOOST_TEST_DONT_PRINT_LOG_VALUE(std::vector::iterator) 43 | BOOST_TEST_DONT_PRINT_LOG_VALUE(std::vector) 44 | BOOST_TEST_DONT_PRINT_LOG_VALUE(std::vector::iterator) 45 | 46 | namespace ndn { 47 | namespace ntorrent { 48 | namespace tests { 49 | 50 | using std::vector; 51 | using ndn::Name; 52 | 53 | BOOST_AUTO_TEST_SUITE(TestFileManifest) 54 | 55 | BOOST_AUTO_TEST_CASE(CheckPrimaryAccessorsAndManipulators) 56 | { 57 | FileManifest m1("/file0/1A2B3C4D", 256, "/foo/"); 58 | 59 | // Check the values on most simply constructed m1 60 | BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name()); 61 | BOOST_CHECK_EQUAL(256, m1.data_packet_size()); 62 | //BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr()); 63 | BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix()); 64 | BOOST_CHECK_EQUAL(vector({}), m1.catalog()); 65 | 66 | // Append names to catalog and recheck all salient attributes 67 | m1.push_back("/foo/0/ABC123"); 68 | m1.push_back("/foo/1/DEADBEFF"); 69 | m1.push_back("/foo/2/CAFEBABE"); 70 | 71 | BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name()); 72 | BOOST_CHECK_EQUAL(256, m1.data_packet_size()); 73 | //BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr()); 74 | BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix()); 75 | BOOST_CHECK_EQUAL(vector({"/foo/0/ABC123", "/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), 76 | m1.catalog()); 77 | 78 | // Remove a value from the catalog and recheck all salient attributes 79 | BOOST_CHECK_EQUAL(true, m1.remove("/foo/0/ABC123")); 80 | 81 | BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name()); 82 | BOOST_CHECK_EQUAL(256, m1.data_packet_size()); 83 | //BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr()); 84 | BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix()); 85 | BOOST_CHECK_EQUAL(vector({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), m1.catalog()); 86 | 87 | // Try to remove a value no longer in the catalog, and recheck that all salient attributes 88 | BOOST_CHECK_EQUAL(false, m1.remove("/foo/0/ABC123")); 89 | 90 | BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name()); 91 | BOOST_CHECK_EQUAL(256, m1.data_packet_size()); 92 | //BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr()); 93 | BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix()); 94 | BOOST_CHECK_EQUAL(vector({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), m1.catalog()); 95 | 96 | // Try to remove a value never in the catalog, and recheck that all salient attributes 97 | BOOST_CHECK_EQUAL(false, m1.remove("/bar/0/ABC123")); 98 | BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name()); 99 | BOOST_CHECK_EQUAL(256, m1.data_packet_size()); 100 | //BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr()); 101 | BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix()); 102 | BOOST_CHECK_EQUAL(vector({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), 103 | m1.catalog()); 104 | 105 | // Remove a value from the end of the list 106 | BOOST_CHECK_EQUAL(true, m1.remove("/foo/2/CAFEBABE")); 107 | 108 | BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name()); 109 | BOOST_CHECK_EQUAL(256, m1.data_packet_size()); 110 | //BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr()); 111 | BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix()); 112 | BOOST_CHECK_EQUAL(vector({"/foo/1/DEADBEFF"}), 113 | m1.catalog()); 114 | } 115 | 116 | BOOST_AUTO_TEST_CASE(CheckValueCtors) 117 | { 118 | FileManifest m1("/file0/1A2B3C4D", 119 | 256, 120 | "/foo/", 121 | {"/foo/0/ABC123", "/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}, 122 | std::make_shared("/file0/1/5E6F7G8H")); 123 | 124 | BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name()); 125 | BOOST_CHECK_EQUAL(256, m1.data_packet_size()); 126 | BOOST_CHECK_EQUAL("/file0/1/5E6F7G8H", *m1.submanifest_ptr()); 127 | BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix()); 128 | BOOST_CHECK_EQUAL(vector({"/foo/0/ABC123", "/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), 129 | m1.catalog()); 130 | 131 | // Remove a value from the catalog and recheck all salient attributes 132 | BOOST_CHECK_EQUAL(true, m1.remove("/foo/0/ABC123")); 133 | 134 | BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name()); 135 | BOOST_CHECK_EQUAL(256, m1.data_packet_size()); 136 | BOOST_CHECK_EQUAL("/file0/1/5E6F7G8H", *m1.submanifest_ptr()); 137 | BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix()); 138 | BOOST_CHECK_EQUAL(vector({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), m1.catalog()); 139 | 140 | // Try to remove a value no longer in the catalog, and recheck that all salient attributes 141 | BOOST_CHECK_EQUAL(false, m1.remove("/foo/0/ABC123")); 142 | 143 | BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name()); 144 | BOOST_CHECK_EQUAL(256, m1.data_packet_size()); 145 | BOOST_CHECK_EQUAL("/file0/1/5E6F7G8H", *m1.submanifest_ptr()); 146 | BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix()); 147 | BOOST_CHECK_EQUAL(vector({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), m1.catalog()); 148 | 149 | // Try to remove a value never in the catalog, and recheck that all salient attributes 150 | BOOST_CHECK_EQUAL(false, m1.remove("/bar/0/ABC123")); 151 | BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name()); 152 | BOOST_CHECK_EQUAL(256, m1.data_packet_size()); 153 | BOOST_CHECK_EQUAL("/file0/1/5E6F7G8H", *m1.submanifest_ptr()); 154 | BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix()); 155 | BOOST_CHECK_EQUAL(vector({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), m1.catalog()); 156 | 157 | // Remove a value from the end of the list 158 | BOOST_CHECK_EQUAL(true, m1.remove("/foo/2/CAFEBABE")); 159 | 160 | BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name()); 161 | BOOST_CHECK_EQUAL(256, m1.data_packet_size()); 162 | BOOST_CHECK_EQUAL("/file0/1/5E6F7G8H", *m1.submanifest_ptr()); 163 | BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix()); 164 | BOOST_CHECK_EQUAL(vector({"/foo/1/DEADBEFF"}), m1.catalog()); 165 | } 166 | 167 | BOOST_AUTO_TEST_CASE(CheckAssignmentOperatorEqaulityAndCopyCtor) 168 | { 169 | // Construct two manifests with the same attributes, and check that they related equal 170 | { 171 | FileManifest m1("/file0/1A2B3C4D", 172 | 256, 173 | "/foo/", 174 | vector({}), 175 | std::make_shared("/file0/1/5E6F7G8H")); 176 | 177 | FileManifest m2("/file0/1A2B3C4D", 178 | 256, 179 | "/foo/", 180 | vector({}), 181 | std::make_shared("/file0/1/5E6F7G8H")); 182 | 183 | BOOST_CHECK_EQUAL(m1, m2); 184 | BOOST_CHECK(!(m1 != m2)); 185 | 186 | // Change value of one 187 | m1.push_back("/foo/0/ABC123"); 188 | BOOST_CHECK_NE(m1, m2); 189 | BOOST_CHECK(!(m1 == m2)); 190 | 191 | // Update other 192 | m2.push_back("/foo/0/ABC123"); 193 | BOOST_CHECK_EQUAL(m1, m2); 194 | BOOST_CHECK(!(m1 != m2)); 195 | 196 | // Change value again 197 | m1.remove("/foo/0/ABC123"); 198 | BOOST_CHECK_NE(m1, m2); 199 | BOOST_CHECK(!(m1 == m2)); 200 | 201 | m2.remove("/foo/0/ABC123"); 202 | BOOST_CHECK_EQUAL(m1, m2); 203 | BOOST_CHECK(!(m1 != m2)); 204 | } 205 | 206 | // Set sub-manifest pointer in one and not the other 207 | { 208 | FileManifest m1("/file0/1A2B3C4D", 209 | 256, 210 | "/foo/", 211 | vector({}), 212 | std::make_shared("/file0/1/5E6F7G8H")); 213 | 214 | FileManifest m2("/file0/1A2B3C4D", 256, "/foo/"); 215 | BOOST_CHECK_NE(m1, m2); 216 | BOOST_CHECK(!(m1 == m2)); 217 | 218 | std::swap(m1, m2); 219 | BOOST_CHECK_NE(m1, m2); 220 | BOOST_CHECK(!(m1 == m2)); 221 | } 222 | 223 | // Construct two manifests using copy ctor for one 224 | { 225 | FileManifest m1("/file0/1A2B3C4D", 256, "/foo/"); 226 | FileManifest m2(m1); 227 | 228 | BOOST_CHECK_EQUAL(m1, m2); 229 | BOOST_CHECK(!(m1 != m2)); 230 | 231 | // Change value of one 232 | m1.push_back("/foo/0/ABC123"); 233 | BOOST_CHECK_NE(m1, m2); 234 | BOOST_CHECK(!(m1 == m2)); 235 | 236 | // Update other 237 | m2.push_back("/foo/0/ABC123"); 238 | BOOST_CHECK_EQUAL(m1, m2); 239 | BOOST_CHECK(!(m1 != m2)); 240 | 241 | // Change value again 242 | m1.remove("/foo/0/ABC123"); 243 | BOOST_CHECK_NE(m1, m2); 244 | BOOST_CHECK(!(m1 == m2)); 245 | 246 | m2.remove("/foo/0/ABC123"); 247 | BOOST_CHECK_EQUAL(m1, m2); 248 | BOOST_CHECK(!(m1 != m2)); 249 | } 250 | 251 | // Use assignment operator 252 | { 253 | FileManifest m1("/file1/1A2B3C4D", 256, "/foo/"); 254 | FileManifest m2("/file1/5E6F7G8H", 256, "/foo/"); 255 | 256 | BOOST_CHECK_NE(m1, m2); 257 | BOOST_CHECK(!(m1 == m2)); 258 | 259 | m2 = m1; 260 | BOOST_CHECK_EQUAL(m1, m2); 261 | BOOST_CHECK(!(m1 != m2)); 262 | 263 | // Change value of one 264 | m1.push_back("/foo/0/ABC123"); 265 | BOOST_CHECK_NE(m1, m2); 266 | BOOST_CHECK(!(m1 == m2)); 267 | 268 | // Update other 269 | m2.push_back("/foo/0/ABC123"); 270 | BOOST_CHECK_EQUAL(m1, m2); 271 | BOOST_CHECK(!(m1 != m2)); 272 | 273 | // Change value again 274 | m1.remove("/foo/0/ABC123"); 275 | BOOST_CHECK_NE(m1, m2); 276 | BOOST_CHECK(!(m1 == m2)); 277 | 278 | m2.remove("/foo/0/ABC123"); 279 | BOOST_CHECK_EQUAL(m1, m2); 280 | BOOST_CHECK(!(m1 != m2)); 281 | } 282 | } 283 | 284 | BOOST_AUTO_TEST_CASE(CheckEncodeDecode) 285 | { 286 | // Construct new FileManifest from wire encoding of another FileManifest 287 | { 288 | FileManifest m1("/file0/1A2B3C4D", 289 | 256, 290 | "/foo/", 291 | {"/foo/0/ABC123", "/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}, 292 | std::make_shared("/file0/1/5E6F7G8H")); 293 | 294 | KeyChain keyChain; 295 | m1.finalize(); 296 | keyChain.sign(m1); 297 | BOOST_CHECK_EQUAL(m1, FileManifest(m1.wireEncode())); 298 | 299 | // Change value and be sure that wireEncoding still works 300 | m1.remove("/foo/2/CAFEBABE"); 301 | m1.finalize(); 302 | keyChain.sign(m1); 303 | BOOST_CHECK_EQUAL(m1, FileManifest(m1.wireEncode())); 304 | 305 | // Explicitly call wireEncode and ensure the value works 306 | FileManifest m2 = m1; 307 | m1.remove("/foo/0/ABC123"); 308 | keyChain.sign(m1); 309 | m1.finalize(); 310 | m2.wireDecode(m1.wireEncode()); 311 | BOOST_CHECK_EQUAL(m1, m2); 312 | } 313 | { 314 | FileManifest m1("/file0/1A2B3C4D", 315 | 256, 316 | "/foo/", 317 | {"/foo/0/ABC123", "/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}); 318 | KeyChain keyChain; 319 | m1.finalize(); 320 | keyChain.sign(m1); 321 | BOOST_CHECK_EQUAL(m1, FileManifest(m1.wireEncode())); 322 | } 323 | } 324 | 325 | BOOST_AUTO_TEST_CASE(CheckGenerateFileManifest) 326 | { 327 | const size_t TEST_FILE_LEN = fs::file_size("tests/testdata/foo/bar.txt"); 328 | const struct { 329 | size_t d_dataPacketSize; 330 | size_t d_subManifestSize; 331 | const char *d_filePath; 332 | const char *d_catalogPrefix; 333 | bool d_getData; 334 | bool d_shouldThrow; 335 | } DATA [] = { 336 | // Affirmative tests 337 | {1 , TEST_FILE_LEN, "tests/testdata/foo/bar.txt" , "/ndn/multicast/NTORRENT/foo/", true, false }, 338 | {10 , 10 , "tests/testdata/foo/bar.txt" , "/ndn/multicast/NTORRENT/foo/", true, false }, 339 | {10 , 1 , "tests/testdata/foo/bar.txt" , "/ndn/multicast/NTORRENT/foo/", true, false }, 340 | {1 , 10 , "tests/testdata/foo/bar.txt" , "/ndn/multicast/NTORRENT/foo/", true, false }, 341 | {1 , 1 , "tests/testdata/foo/bar.txt" , "/ndn/multicast/NTORRENT/foo/", true, false }, 342 | {1024 , 1 , "tests/testdata/foo/bar1.txt", "/ndn/multicast/NTORRENT/foo/", true, false }, 343 | {1024 , 100 , "tests/testdata/foo/bar1.txt", "/ndn/multicast/NTORRENT/foo/", true, false }, 344 | {TEST_FILE_LEN, 1 , "tests/testdata/foo/bar.txt" , "/ndn/multicast/NTORRENT/foo/", true, false }, 345 | // Negative tests 346 | // non-existent file 347 | {128 , 128 , "tests/testdata/foo/fake.txt", "/ndn/multicast/NTORRENT/foo/", false, true }, 348 | // prefix mismatch 349 | {128 , 128 , "tests/testdata/foo/bar.txt", "/ndn/multicast/NTORRENT/bar/", false, true }, 350 | // scaling test 351 | // {10240 , 5120 , "tests/testdata/foo/huge_file", "/ndn/multicast/NTORRENT/foo/", false, false }, 352 | // assertion failures (tests not supported on platforms) 353 | // {0 , 128 , "tests/testdata/foo/bar.txt", "/NTORRENT/bar/", true }, 354 | // {128 , 0 , "tests/testdata/foo/bar.txt", "/NTORRENT/bar/", true }, 355 | }; 356 | enum { NUM_DATA = sizeof DATA / sizeof *DATA }; 357 | for (int i = 0; i < NUM_DATA; ++i) { 358 | auto dataPacketSize = DATA[i].d_dataPacketSize; 359 | auto subManifestSize = DATA[i].d_subManifestSize; 360 | auto filePath = DATA[i].d_filePath; 361 | auto catalogPrefix = DATA[i].d_catalogPrefix; 362 | auto getData = DATA[i].d_getData; 363 | auto shouldThrow = DATA[i].d_shouldThrow; 364 | 365 | std::pair, std::vector> manifestsDataPair; 366 | if (shouldThrow) { 367 | BOOST_CHECK_THROW(FileManifest::generate(filePath, 368 | catalogPrefix, 369 | subManifestSize, 370 | dataPacketSize, 371 | getData), 372 | FileManifest::Error); 373 | } 374 | else { 375 | manifestsDataPair = FileManifest::generate(filePath, 376 | catalogPrefix, 377 | subManifestSize, 378 | dataPacketSize, 379 | getData); 380 | auto fileSize = fs::file_size(filePath); 381 | auto EXP_NUM_DATA_PACKETS = fileSize / dataPacketSize + !!(fileSize % dataPacketSize); 382 | auto EXP_NUM_FILE_MANIFESTS = EXP_NUM_DATA_PACKETS / subManifestSize + 383 | !!(EXP_NUM_DATA_PACKETS % subManifestSize); 384 | auto manifests = manifestsDataPair.first; 385 | auto data = manifestsDataPair.second; 386 | 387 | // Verify the basic attributes of the manifest 388 | BOOST_CHECK_EQUAL(manifests.size(), EXP_NUM_FILE_MANIFESTS); 389 | for (auto it = manifests.begin(); it != manifests.end(); ++it) { 390 | // Verify that each file manifest is signed. 391 | BOOST_CHECK_NO_THROW(it->getFullName()); 392 | BOOST_CHECK_EQUAL(it->data_packet_size(), dataPacketSize); 393 | BOOST_CHECK_EQUAL(it->catalog_prefix(), catalogPrefix); 394 | BOOST_CHECK_EQUAL(*it, FileManifest(it->wireEncode())); 395 | if (it != manifests.end() -1) { 396 | BOOST_CHECK_EQUAL(it->catalog().size(), subManifestSize); 397 | BOOST_CHECK_EQUAL(*(it->submanifest_ptr()), (it+1)->getFullName()); 398 | } 399 | else { 400 | BOOST_CHECK_LE(it->catalog().size(), subManifestSize); 401 | //BOOST_CHECK_EQUAL(it->submanifest_ptr(), nullptr); 402 | } 403 | } 404 | // test that we can write the manifest to the disk and read it out again 405 | { 406 | std::string dirPath = "tests/testdata/temp/"; 407 | boost::filesystem::create_directory(dirPath); 408 | auto fileNum = 0; 409 | for (auto &m : manifests) { 410 | fileNum++; 411 | auto filename = dirPath + to_string(fileNum); 412 | io::save(m, filename); 413 | } 414 | // read them back out 415 | fileNum = 0; 416 | for (auto &m : manifests) { 417 | fileNum++; 418 | auto filename = dirPath + to_string(fileNum); 419 | auto manifest_ptr = io::load(filename); 420 | //BOOST_CHECK_NE(manifest_ptr, nullptr); 421 | BOOST_CHECK_EQUAL(m, *manifest_ptr); 422 | } 423 | fs::remove_all(dirPath); 424 | } 425 | // confirm the manifests catalogs includes exactly the data packets 426 | if (getData) { 427 | BOOST_CHECK_EQUAL(EXP_NUM_DATA_PACKETS, data.size()); 428 | auto data_it = data.begin(); 429 | size_t total_manifest_length = 0; 430 | for (auto& m : manifests) { 431 | total_manifest_length += m.wireEncode().value_size(); 432 | for (auto& data_name : m.catalog()) { 433 | BOOST_CHECK_EQUAL(data_name, data_it->getFullName()); 434 | ++data_it; 435 | } 436 | } 437 | BOOST_CHECK_EQUAL(data_it, data.end()); 438 | std::vector data_bytes; 439 | data_bytes.reserve(fileSize); 440 | for (const auto& d : data) { 441 | auto content = d.getContent(); 442 | data_bytes.insert(data_bytes.end(), content.value_begin(), content.value_end()); 443 | } 444 | data_bytes.shrink_to_fit(); 445 | // load the contents of the file from disk 446 | fs::ifstream is(filePath, fs::ifstream::binary | fs::ifstream::in); 447 | is >> std::noskipws; 448 | std::istream_iterator start(is), end; 449 | std::vector file_bytes; 450 | file_bytes.reserve(fileSize); 451 | file_bytes.insert(file_bytes.begin(), start, end); 452 | file_bytes.shrink_to_fit(); 453 | BOOST_CHECK_EQUAL(file_bytes, data_bytes); 454 | } 455 | else { 456 | BOOST_CHECK(data.empty()); 457 | BOOST_CHECK(data.capacity() == 0); 458 | } 459 | } 460 | } 461 | } 462 | 463 | BOOST_AUTO_TEST_SUITE_END() 464 | 465 | } // namespace tests 466 | } // namespace nTorrent 467 | } // namespace ndn 468 | --------------------------------------------------------------------------------