├── 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