moved library
This commit is contained in:
parent
514421b6f1
commit
99186a54c9
124
cpp/alert.cpp
Executable file
124
cpp/alert.cpp
Executable file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2003, Arvid Norberg, Daniel Wallin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "libtorrent/alert.hpp"
|
||||
|
||||
namespace libtorrent {
|
||||
|
||||
alert::alert(severity_t severity, const std::string& msg)
|
||||
: m_msg(msg)
|
||||
, m_severity(severity)
|
||||
, m_timestamp(boost::posix_time::second_clock::universal_time())
|
||||
{
|
||||
}
|
||||
|
||||
alert::~alert()
|
||||
{
|
||||
}
|
||||
|
||||
boost::posix_time::ptime alert::timestamp() const
|
||||
{
|
||||
return m_timestamp;
|
||||
}
|
||||
|
||||
const std::string& alert::msg() const
|
||||
{
|
||||
return m_msg;
|
||||
}
|
||||
|
||||
alert::severity_t alert::severity() const
|
||||
{
|
||||
return m_severity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
alert_manager::alert_manager()
|
||||
: m_severity(alert::none)
|
||||
{}
|
||||
|
||||
alert_manager::~alert_manager()
|
||||
{
|
||||
while (!m_alerts.empty())
|
||||
{
|
||||
delete m_alerts.front();
|
||||
m_alerts.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void alert_manager::post_alert(const alert& alert_)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_mutex);
|
||||
if (m_severity > alert_.severity()) return;
|
||||
|
||||
// the internal limit is 100 alerts
|
||||
if (m_alerts.size() == 100)
|
||||
{
|
||||
alert* result = m_alerts.front();
|
||||
m_alerts.pop();
|
||||
delete result;
|
||||
}
|
||||
m_alerts.push(alert_.clone().release());
|
||||
}
|
||||
|
||||
std::auto_ptr<alert> alert_manager::get()
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_mutex);
|
||||
|
||||
assert(!m_alerts.empty());
|
||||
|
||||
alert* result = m_alerts.front();
|
||||
m_alerts.pop();
|
||||
return std::auto_ptr<alert>(result);
|
||||
}
|
||||
|
||||
bool alert_manager::pending() const
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_mutex);
|
||||
|
||||
return !m_alerts.empty();
|
||||
}
|
||||
|
||||
void alert_manager::set_severity(alert::severity_t severity)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_mutex);
|
||||
|
||||
m_severity = severity;
|
||||
}
|
||||
|
||||
bool alert_manager::should_post(alert::severity_t severity) const
|
||||
{
|
||||
return severity >= m_severity;
|
||||
}
|
||||
|
||||
} // namespace libtorrent
|
||||
|
||||
201
cpp/allocate_resources.cpp
Normal file
201
cpp/allocate_resources.cpp
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2003, Magnus Jonsson, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
//The Standard Library defines the two template functions std::min()
|
||||
//and std::max() in the <algorithm> header. In general, you should
|
||||
//use these template functions for calculating the min and max values
|
||||
//of a pair. Unfortunately, Visual C++ does not define these function
|
||||
// templates. This is because the names min and max clash with
|
||||
//the traditional min and max macros defined in <windows.h>.
|
||||
//As a workaround, Visual C++ defines two alternative templates with
|
||||
//identical functionality called _cpp_min() and _cpp_max(). You can
|
||||
//use them instead of std::min() and std::max().To disable the
|
||||
//generation of the min and max macros in Visual C++, #define
|
||||
//NOMINMAX before #including <windows.h>.
|
||||
|
||||
#ifdef _WIN32
|
||||
//support boost1.32.0(2004-11-19 18:47)
|
||||
//now all libs can be compiled and linked with static module
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include "libtorrent/allocate_resources.hpp"
|
||||
#include "libtorrent/size_type.hpp"
|
||||
#include "libtorrent/peer_connection.hpp"
|
||||
#include "libtorrent/torrent.hpp"
|
||||
#include "libtorrent/aux_/allocate_resources_impl.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <boost/limits.hpp>
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1310
|
||||
#define for if (false) {} else for
|
||||
#else
|
||||
#include <boost/iterator/transform_iterator.hpp>
|
||||
#endif
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
int saturated_add(int a, int b)
|
||||
{
|
||||
assert(a >= 0);
|
||||
assert(b >= 0);
|
||||
assert(a <= resource_request::inf);
|
||||
assert(b <= resource_request::inf);
|
||||
assert(resource_request::inf + resource_request::inf < 0);
|
||||
|
||||
unsigned int sum = unsigned(a) + unsigned(b);
|
||||
if (sum > unsigned(resource_request::inf))
|
||||
sum = resource_request::inf;
|
||||
|
||||
assert(sum >= unsigned(a) && sum >= unsigned(b));
|
||||
return int(sum);
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1310
|
||||
|
||||
namespace detail
|
||||
{
|
||||
struct iterator_wrapper
|
||||
{
|
||||
typedef std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator orig_iter;
|
||||
|
||||
orig_iter iter;
|
||||
|
||||
iterator_wrapper(orig_iter i): iter(i) {}
|
||||
void operator++() { ++iter; }
|
||||
torrent& operator*() { return *(iter->second); }
|
||||
bool operator==(const iterator_wrapper& i) const
|
||||
{ return iter == i.iter; }
|
||||
bool operator!=(const iterator_wrapper& i) const
|
||||
{ return iter != i.iter; }
|
||||
};
|
||||
|
||||
struct iterator_wrapper2
|
||||
{
|
||||
typedef std::map<tcp::endpoint, peer_connection*>::iterator orig_iter;
|
||||
|
||||
orig_iter iter;
|
||||
|
||||
iterator_wrapper2(orig_iter i): iter(i) {}
|
||||
void operator++() { ++iter; }
|
||||
peer_connection& operator*() { return *(iter->second); }
|
||||
bool operator==(const iterator_wrapper2& i) const
|
||||
{ return iter == i.iter; }
|
||||
bool operator!=(const iterator_wrapper2& i) const
|
||||
{ return iter != i.iter; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void allocate_resources(
|
||||
int resources
|
||||
, std::map<sha1_hash, boost::shared_ptr<torrent> >& c
|
||||
, resource_request torrent::* res)
|
||||
{
|
||||
aux::allocate_resources_impl(
|
||||
resources
|
||||
, detail::iterator_wrapper(c.begin())
|
||||
, detail::iterator_wrapper(c.end())
|
||||
, res);
|
||||
}
|
||||
|
||||
void allocate_resources(
|
||||
int resources
|
||||
, std::map<tcp::endpoint, peer_connection*>& c
|
||||
, resource_request peer_connection::* res)
|
||||
{
|
||||
aux::allocate_resources_impl(
|
||||
resources
|
||||
, detail::iterator_wrapper2(c.begin())
|
||||
, detail::iterator_wrapper2(c.end())
|
||||
, res);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
namespace aux
|
||||
{
|
||||
peer_connection& pick_peer(
|
||||
std::pair<boost::shared_ptr<stream_socket>
|
||||
, boost::intrusive_ptr<peer_connection> > const& p)
|
||||
{
|
||||
return *p.second;
|
||||
}
|
||||
|
||||
peer_connection& pick_peer2(
|
||||
std::pair<tcp::endpoint, peer_connection*> const& p)
|
||||
{
|
||||
return *p.second;
|
||||
}
|
||||
|
||||
torrent& deref(std::pair<sha1_hash, boost::shared_ptr<torrent> > const& p)
|
||||
{
|
||||
return *p.second;
|
||||
}
|
||||
}
|
||||
|
||||
void allocate_resources(
|
||||
int resources
|
||||
, std::map<sha1_hash, boost::shared_ptr<torrent> >& c
|
||||
, resource_request torrent::* res)
|
||||
{
|
||||
typedef std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator orig_iter;
|
||||
typedef std::pair<sha1_hash, boost::shared_ptr<torrent> > in_param;
|
||||
typedef boost::transform_iterator<torrent& (*)(in_param const&), orig_iter> new_iter;
|
||||
|
||||
aux::allocate_resources_impl(
|
||||
resources
|
||||
, new_iter(c.begin(), &aux::deref)
|
||||
, new_iter(c.end(), &aux::deref)
|
||||
, res);
|
||||
}
|
||||
|
||||
void allocate_resources(
|
||||
int resources
|
||||
, std::map<tcp::endpoint, peer_connection*>& c
|
||||
, resource_request peer_connection::* res)
|
||||
{
|
||||
typedef std::map<tcp::endpoint, peer_connection*>::iterator orig_iter;
|
||||
typedef std::pair<tcp::endpoint, peer_connection*> in_param;
|
||||
typedef boost::transform_iterator<peer_connection& (*)(in_param const&), orig_iter> new_iter;
|
||||
|
||||
aux::allocate_resources_impl(
|
||||
resources
|
||||
, new_iter(c.begin(), &aux::pick_peer2)
|
||||
, new_iter(c.end(), &aux::pick_peer2)
|
||||
, res);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace libtorrent
|
||||
1571
cpp/bt_peer_connection.cpp
Executable file
1571
cpp/bt_peer_connection.cpp
Executable file
File diff suppressed because it is too large
Load Diff
344
cpp/entry.cpp
Executable file
344
cpp/entry.cpp
Executable file
@ -0,0 +1,344 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2003, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include "libtorrent/entry.hpp"
|
||||
#include "libtorrent/config.hpp"
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/next_prior.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
namespace std
|
||||
{
|
||||
using ::isprint;
|
||||
}
|
||||
#define for if (false) {} else for
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
template <class T>
|
||||
void call_destructor(T* o)
|
||||
{
|
||||
assert(o);
|
||||
o->~T();
|
||||
}
|
||||
|
||||
struct compare_string
|
||||
{
|
||||
compare_string(char const* s): m_str(s) {}
|
||||
|
||||
bool operator()(
|
||||
std::pair<std::string
|
||||
, libtorrent::entry> const& e) const
|
||||
{
|
||||
return m_str && e.first == m_str;
|
||||
}
|
||||
char const* m_str;
|
||||
};
|
||||
}
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
TORRENT_EXPORT char const* integer_to_str(char* buf, int size, entry::integer_type val)
|
||||
{
|
||||
int sign = 0;
|
||||
if (val < 0)
|
||||
{
|
||||
sign = 1;
|
||||
val = -val;
|
||||
}
|
||||
buf[--size] = '\0';
|
||||
if (val == 0) buf[--size] = '0';
|
||||
for (; size > sign && val != 0;)
|
||||
{
|
||||
buf[--size] = '0' + char(val % 10);
|
||||
val /= 10;
|
||||
}
|
||||
if (sign) buf[--size] = '-';
|
||||
return buf + size;
|
||||
}
|
||||
}
|
||||
|
||||
entry& entry::operator[](char const* key)
|
||||
{
|
||||
dictionary_type::iterator i = dict().find(key);
|
||||
if (i != dict().end()) return i->second;
|
||||
dictionary_type::iterator ret = dict().insert(
|
||||
dict().begin()
|
||||
, std::make_pair(std::string(key), entry()));
|
||||
return ret->second;
|
||||
}
|
||||
|
||||
|
||||
entry& entry::operator[](std::string const& key)
|
||||
{
|
||||
return (*this)[key.c_str()];
|
||||
}
|
||||
|
||||
entry* entry::find_key(char const* key)
|
||||
{
|
||||
dictionary_type::iterator i = std::find_if(
|
||||
dict().begin()
|
||||
, dict().end()
|
||||
, compare_string(key));
|
||||
if (i == dict().end()) return 0;
|
||||
return &i->second;
|
||||
|
||||
}
|
||||
|
||||
entry const* entry::find_key(char const* key) const
|
||||
{
|
||||
dictionary_type::const_iterator i = dict().find(key);
|
||||
if (i == dict().end()) return 0;
|
||||
return &i->second;
|
||||
}
|
||||
|
||||
const entry& entry::operator[](char const* key) const
|
||||
{
|
||||
dictionary_type::const_iterator i = dict().find(key);
|
||||
if (i == dict().end()) throw type_error(
|
||||
(std::string("key not found: ") + key).c_str());
|
||||
return i->second;
|
||||
}
|
||||
|
||||
const entry& entry::operator[](std::string const& key) const
|
||||
{
|
||||
return (*this)[key.c_str()];
|
||||
}
|
||||
|
||||
entry::entry(const dictionary_type& v)
|
||||
{
|
||||
new(data) dictionary_type(v);
|
||||
m_type = dictionary_t;
|
||||
}
|
||||
|
||||
entry::entry(const string_type& v)
|
||||
{
|
||||
new(data) string_type(v);
|
||||
m_type = string_t;
|
||||
}
|
||||
|
||||
entry::entry(const list_type& v)
|
||||
{
|
||||
new(data) list_type(v);
|
||||
m_type = list_t;
|
||||
}
|
||||
|
||||
entry::entry(const integer_type& v)
|
||||
{
|
||||
new(data) integer_type(v);
|
||||
m_type = int_t;
|
||||
}
|
||||
|
||||
void entry::operator=(const dictionary_type& v)
|
||||
{
|
||||
destruct();
|
||||
new(data) dictionary_type(v);
|
||||
m_type = dictionary_t;
|
||||
}
|
||||
|
||||
void entry::operator=(const string_type& v)
|
||||
{
|
||||
destruct();
|
||||
new(data) string_type(v);
|
||||
m_type = string_t;
|
||||
}
|
||||
|
||||
void entry::operator=(const list_type& v)
|
||||
{
|
||||
destruct();
|
||||
new(data) list_type(v);
|
||||
m_type = list_t;
|
||||
}
|
||||
|
||||
void entry::operator=(const integer_type& v)
|
||||
{
|
||||
destruct();
|
||||
new(data) integer_type(v);
|
||||
m_type = int_t;
|
||||
}
|
||||
|
||||
bool entry::operator==(entry const& e) const
|
||||
{
|
||||
if (m_type != e.m_type) return false;
|
||||
|
||||
switch(m_type)
|
||||
{
|
||||
case int_t:
|
||||
return integer() == e.integer();
|
||||
case string_t:
|
||||
return string() == e.string();
|
||||
case list_t:
|
||||
return list() == e.list();
|
||||
case dictionary_t:
|
||||
return dict() == e.dict();
|
||||
default:
|
||||
assert(m_type == undefined_t);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void entry::construct(data_type t)
|
||||
{
|
||||
m_type = t;
|
||||
switch(m_type)
|
||||
{
|
||||
case int_t:
|
||||
new(data) integer_type;
|
||||
break;
|
||||
case string_t:
|
||||
new(data) string_type;
|
||||
break;
|
||||
case list_t:
|
||||
new(data) list_type;
|
||||
break;
|
||||
case dictionary_t:
|
||||
new (data) dictionary_type;
|
||||
break;
|
||||
default:
|
||||
assert(m_type == undefined_t);
|
||||
m_type = undefined_t;
|
||||
}
|
||||
}
|
||||
|
||||
void entry::copy(const entry& e)
|
||||
{
|
||||
m_type = e.m_type;
|
||||
switch(m_type)
|
||||
{
|
||||
case int_t:
|
||||
new(data) integer_type(e.integer());
|
||||
break;
|
||||
case string_t:
|
||||
new(data) string_type(e.string());
|
||||
break;
|
||||
case list_t:
|
||||
new(data) list_type(e.list());
|
||||
break;
|
||||
case dictionary_t:
|
||||
new (data) dictionary_type(e.dict());
|
||||
break;
|
||||
default:
|
||||
m_type = undefined_t;
|
||||
}
|
||||
}
|
||||
|
||||
void entry::destruct()
|
||||
{
|
||||
switch(m_type)
|
||||
{
|
||||
case int_t:
|
||||
call_destructor(reinterpret_cast<integer_type*>(data));
|
||||
break;
|
||||
case string_t:
|
||||
call_destructor(reinterpret_cast<string_type*>(data));
|
||||
break;
|
||||
case list_t:
|
||||
call_destructor(reinterpret_cast<list_type*>(data));
|
||||
break;
|
||||
case dictionary_t:
|
||||
call_destructor(reinterpret_cast<dictionary_type*>(data));
|
||||
break;
|
||||
default:
|
||||
assert(m_type == undefined_t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void entry::print(std::ostream& os, int indent) const
|
||||
{
|
||||
assert(indent >= 0);
|
||||
for (int i = 0; i < indent; ++i) os << " ";
|
||||
switch (m_type)
|
||||
{
|
||||
case int_t:
|
||||
os << integer() << "\n";
|
||||
break;
|
||||
case string_t:
|
||||
{
|
||||
bool binary_string = false;
|
||||
for (std::string::const_iterator i = string().begin(); i != string().end(); ++i)
|
||||
{
|
||||
if (!std::isprint(static_cast<unsigned char>(*i)))
|
||||
{
|
||||
binary_string = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (binary_string)
|
||||
{
|
||||
os.unsetf(std::ios_base::dec);
|
||||
os.setf(std::ios_base::hex);
|
||||
for (std::string::const_iterator i = string().begin(); i != string().end(); ++i)
|
||||
os << std::setfill('0') << std::setw(2)
|
||||
<< static_cast<unsigned int>((unsigned char)*i);
|
||||
os.unsetf(std::ios_base::hex);
|
||||
os.setf(std::ios_base::dec);
|
||||
os << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
os << string() << "\n";
|
||||
}
|
||||
} break;
|
||||
case list_t:
|
||||
{
|
||||
os << "list\n";
|
||||
for (list_type::const_iterator i = list().begin(); i != list().end(); ++i)
|
||||
{
|
||||
i->print(os, indent+1);
|
||||
}
|
||||
} break;
|
||||
case dictionary_t:
|
||||
{
|
||||
os << "dictionary\n";
|
||||
for (dictionary_type::const_iterator i = dict().begin(); i != dict().end(); ++i)
|
||||
{
|
||||
for (int j = 0; j < indent+1; ++j) os << " ";
|
||||
os << "[" << i->first << "]";
|
||||
if (i->second.type() != entry::string_t
|
||||
&& i->second.type() != entry::int_t)
|
||||
os << "\n";
|
||||
else os << " ";
|
||||
i->second.print(os, indent+2);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
os << "<uninitialized>\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
148
cpp/escape_string.cpp
Executable file
148
cpp/escape_string.cpp
Executable file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2003, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
std::string unescape_string(std::string const& s)
|
||||
{
|
||||
std::string ret;
|
||||
for (std::string::const_iterator i = s.begin(); i != s.end(); ++i)
|
||||
{
|
||||
if(*i == '+')
|
||||
{
|
||||
ret += ' ';
|
||||
}
|
||||
else if (*i != '%')
|
||||
{
|
||||
ret += *i;
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
if (i == s.end())
|
||||
throw std::runtime_error("invalid escaped string");
|
||||
|
||||
int high;
|
||||
if(*i >= '0' && *i <= '9') high = *i - '0';
|
||||
else if(*i >= 'A' && *i <= 'F') high = *i + 10 - 'A';
|
||||
else if(*i >= 'a' && *i <= 'f') high = *i + 10 - 'a';
|
||||
else throw std::runtime_error("invalid escaped string");
|
||||
|
||||
++i;
|
||||
if (i == s.end())
|
||||
throw std::runtime_error("invalid escaped string");
|
||||
|
||||
int low;
|
||||
if(*i >= '0' && *i <= '9') low = *i - '0';
|
||||
else if(*i >= 'A' && *i <= 'F') low = *i + 10 - 'A';
|
||||
else if(*i >= 'a' && *i <= 'f') low = *i + 10 - 'a';
|
||||
else throw std::runtime_error("invalid escaped string");
|
||||
|
||||
ret += char(high * 16 + low);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
std::string escape_string(const char* str, int len)
|
||||
{
|
||||
assert(str != 0);
|
||||
assert(len >= 0);
|
||||
// http://www.ietf.org/rfc/rfc2396.txt
|
||||
// section 2.3
|
||||
// some trackers seems to require that ' is escaped
|
||||
// static const char unreserved_chars[] = "-_.!~*'()";
|
||||
static const char unreserved_chars[] = "-_.!~*()"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789";
|
||||
|
||||
std::stringstream ret;
|
||||
ret << std::hex << std::setfill('0');
|
||||
for (int i = 0; i < len; ++i)
|
||||
{
|
||||
if (std::count(
|
||||
unreserved_chars
|
||||
, unreserved_chars+sizeof(unreserved_chars)-1
|
||||
, *str))
|
||||
{
|
||||
ret << *str;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret << '%'
|
||||
<< std::setw(2)
|
||||
<< (int)static_cast<unsigned char>(*str);
|
||||
}
|
||||
++str;
|
||||
}
|
||||
return ret.str();
|
||||
}
|
||||
|
||||
std::string escape_path(const char* str, int len)
|
||||
{
|
||||
assert(str != 0);
|
||||
assert(len >= 0);
|
||||
static const char unreserved_chars[] = "/-_.!~*()"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789";
|
||||
|
||||
std::stringstream ret;
|
||||
ret << std::hex << std::setfill('0');
|
||||
for (int i = 0; i < len; ++i)
|
||||
{
|
||||
if (std::count(
|
||||
unreserved_chars
|
||||
, unreserved_chars+sizeof(unreserved_chars)-1
|
||||
, *str))
|
||||
{
|
||||
ret << *str;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret << '%'
|
||||
<< std::setw(2)
|
||||
<< (int)static_cast<unsigned char>(*str);
|
||||
}
|
||||
++str;
|
||||
}
|
||||
return ret.str();
|
||||
}
|
||||
}
|
||||
313
cpp/file.cpp
Executable file
313
cpp/file.cpp
Executable file
@ -0,0 +1,313 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2003, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
// windows part
|
||||
#include "libtorrent/utf8.hpp"
|
||||
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef _MODE_T_
|
||||
typedef int mode_t;
|
||||
#endif
|
||||
|
||||
#ifdef UNICODE
|
||||
#include "libtorrent/storage.hpp"
|
||||
#endif
|
||||
|
||||
#else
|
||||
// unix part
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <boost/static_assert.hpp>
|
||||
// make sure the _FILE_OFFSET_BITS define worked
|
||||
// on this platform
|
||||
BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8);
|
||||
|
||||
#endif
|
||||
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include "libtorrent/file.hpp"
|
||||
#include <sstream>
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
#ifndef O_RANDOM
|
||||
#define O_RANDOM 0
|
||||
#endif
|
||||
|
||||
#ifdef UNICODE
|
||||
#include "libtorrent/storage.hpp"
|
||||
#endif
|
||||
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum { mode_in = 1, mode_out = 2 };
|
||||
|
||||
mode_t map_open_mode(int m)
|
||||
{
|
||||
if (m == (mode_in | mode_out)) return O_RDWR | O_CREAT | O_BINARY | O_RANDOM;
|
||||
if (m == mode_out) return O_WRONLY | O_CREAT | O_BINARY | O_RANDOM;
|
||||
if (m == mode_in) return O_RDONLY | O_BINARY | O_RANDOM;
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
std::string utf8_native(std::string const& s)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::wstring ws;
|
||||
libtorrent::utf8_wchar(s, ws);
|
||||
std::size_t size = wcstombs(0, ws.c_str(), 0);
|
||||
if (size == std::size_t(-1)) return s;
|
||||
std::string ret;
|
||||
ret.resize(size);
|
||||
size = wcstombs(&ret[0], ws.c_str(), size + 1);
|
||||
if (size == wchar_t(-1)) return s;
|
||||
ret.resize(size);
|
||||
return ret;
|
||||
}
|
||||
catch(std::exception)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
}
|
||||
#else
|
||||
std::string utf8_native(std::string const& s)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
const file::open_mode file::in(mode_in);
|
||||
const file::open_mode file::out(mode_out);
|
||||
|
||||
const file::seek_mode file::begin(1);
|
||||
const file::seek_mode file::end(2);
|
||||
|
||||
struct file::impl
|
||||
{
|
||||
impl()
|
||||
: m_fd(-1)
|
||||
, m_open_mode(0)
|
||||
{}
|
||||
|
||||
impl(fs::path const& path, int mode)
|
||||
: m_fd(-1)
|
||||
, m_open_mode(0)
|
||||
{
|
||||
open(path, mode);
|
||||
}
|
||||
|
||||
~impl()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void open(fs::path const& path, int mode)
|
||||
{
|
||||
assert(path.is_complete());
|
||||
close();
|
||||
#if defined(_WIN32) && defined(UNICODE)
|
||||
std::wstring wpath(safe_convert(path.native_file_string()));
|
||||
m_fd = ::_wopen(
|
||||
wpath.c_str()
|
||||
, map_open_mode(mode)
|
||||
, S_IREAD | S_IWRITE);
|
||||
#else
|
||||
m_fd = ::open(
|
||||
utf8_native(path.native_file_string()).c_str()
|
||||
, map_open_mode(mode)
|
||||
#ifdef _WIN32
|
||||
, S_IREAD | S_IWRITE);
|
||||
#else
|
||||
, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
#endif
|
||||
#endif
|
||||
if (m_fd == -1)
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "open failed: '" << path.native_file_string() << "'. "
|
||||
<< strerror(errno);
|
||||
throw file_error(msg.str());
|
||||
}
|
||||
m_open_mode = mode;
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if (m_fd == -1) return;
|
||||
|
||||
::close(m_fd);
|
||||
m_fd = -1;
|
||||
m_open_mode = 0;
|
||||
}
|
||||
|
||||
size_type read(char* buf, size_type num_bytes)
|
||||
{
|
||||
assert(m_open_mode & mode_in);
|
||||
assert(m_fd != -1);
|
||||
|
||||
size_type ret = ::read(m_fd, buf, num_bytes);
|
||||
if (ret == -1)
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "read failed: " << strerror(errno);
|
||||
throw file_error(msg.str());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_type write(const char* buf, size_type num_bytes)
|
||||
{
|
||||
assert(m_open_mode & mode_out);
|
||||
assert(m_fd != -1);
|
||||
|
||||
// TODO: Test this a bit more, what happens with random failures in
|
||||
// the files?
|
||||
// if ((rand() % 100) > 80)
|
||||
// throw file_error("debug");
|
||||
|
||||
size_type ret = ::write(m_fd, buf, num_bytes);
|
||||
if (ret == -1)
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "write failed: " << strerror(errno);
|
||||
throw file_error(msg.str());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_type seek(size_type offset, int m)
|
||||
{
|
||||
assert(m_open_mode);
|
||||
assert(m_fd != -1);
|
||||
|
||||
int seekdir = (m == 1)?SEEK_SET:SEEK_END;
|
||||
#ifdef _WIN32
|
||||
size_type ret = _lseeki64(m_fd, offset, seekdir);
|
||||
#else
|
||||
size_type ret = lseek(m_fd, offset, seekdir);
|
||||
#endif
|
||||
|
||||
// For some strange reason this fails
|
||||
// on win32. Use windows specific file
|
||||
// wrapper instead.
|
||||
if (ret == -1)
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "seek failed: '" << strerror(errno)
|
||||
<< "' fd: " << m_fd
|
||||
<< " offset: " << offset
|
||||
<< " seekdir: " << seekdir;
|
||||
throw file_error(msg.str());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_type tell()
|
||||
{
|
||||
assert(m_open_mode);
|
||||
assert(m_fd != -1);
|
||||
|
||||
#ifdef _WIN32
|
||||
return _telli64(m_fd);
|
||||
#else
|
||||
return lseek(m_fd, 0, SEEK_CUR);
|
||||
#endif
|
||||
}
|
||||
|
||||
int m_fd;
|
||||
int m_open_mode;
|
||||
};
|
||||
|
||||
// pimpl forwardings
|
||||
|
||||
file::file() : m_impl(new impl()) {}
|
||||
|
||||
file::file(boost::filesystem::path const& p, file::open_mode m)
|
||||
: m_impl(new impl(p, m.m_mask))
|
||||
{}
|
||||
|
||||
file::~file() {}
|
||||
|
||||
void file::open(boost::filesystem::path const& p, file::open_mode m)
|
||||
{
|
||||
m_impl->open(p, m.m_mask);
|
||||
}
|
||||
|
||||
void file::close()
|
||||
{
|
||||
m_impl->close();
|
||||
}
|
||||
|
||||
size_type file::write(const char* buf, size_type num_bytes)
|
||||
{
|
||||
return m_impl->write(buf, num_bytes);
|
||||
}
|
||||
|
||||
size_type file::read(char* buf, size_type num_bytes)
|
||||
{
|
||||
return m_impl->read(buf, num_bytes);
|
||||
}
|
||||
|
||||
size_type file::seek(size_type pos, file::seek_mode m)
|
||||
{
|
||||
return m_impl->seek(pos, m.m_val);
|
||||
}
|
||||
|
||||
size_type file::tell()
|
||||
{
|
||||
return m_impl->tell();
|
||||
}
|
||||
|
||||
}
|
||||
1211
cpp/flood_core.cpp
Executable file
1211
cpp/flood_core.cpp
Executable file
File diff suppressed because it is too large
Load Diff
845
cpp/http_tracker_connection.cpp
Executable file
845
cpp/http_tracker_connection.cpp
Executable file
@ -0,0 +1,845 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2003, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <cctype>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "zlib.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 1)
|
||||
#endif
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "libtorrent/tracker_manager.hpp"
|
||||
#include "libtorrent/http_tracker_connection.hpp"
|
||||
#include "libtorrent/entry.hpp"
|
||||
#include "libtorrent/bencode.hpp"
|
||||
#include "libtorrent/torrent.hpp"
|
||||
#include "libtorrent/io.hpp"
|
||||
|
||||
using namespace libtorrent;
|
||||
using boost::bind;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum
|
||||
{
|
||||
minimum_tracker_response_length = 3,
|
||||
http_buffer_size = 2048
|
||||
};
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
FTEXT = 0x01,
|
||||
FHCRC = 0x02,
|
||||
FEXTRA = 0x04,
|
||||
FNAME = 0x08,
|
||||
FCOMMENT = 0x10,
|
||||
FRESERVED = 0xe0,
|
||||
|
||||
GZIP_MAGIC0 = 0x1f,
|
||||
GZIP_MAGIC1 = 0x8b
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using namespace boost::posix_time;
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
http_parser::http_parser()
|
||||
: m_recv_pos(0)
|
||||
, m_status_code(-1)
|
||||
, m_content_length(-1)
|
||||
, m_content_encoding(plain)
|
||||
, m_state(read_status)
|
||||
, m_recv_buffer(0, 0)
|
||||
, m_body_start_pos(0)
|
||||
, m_finished(false)
|
||||
{}
|
||||
|
||||
boost::tuple<int, int> http_parser::incoming(buffer::const_interval recv_buffer)
|
||||
{
|
||||
m_recv_buffer = recv_buffer;
|
||||
boost::tuple<int, int> ret(0, 0);
|
||||
|
||||
char const* pos = recv_buffer.begin + m_recv_pos;
|
||||
if (m_state == read_status)
|
||||
{
|
||||
assert(!m_finished);
|
||||
char const* newline = std::find(pos, recv_buffer.end, '\n');
|
||||
// if we don't have a full line yet, wait.
|
||||
if (newline == recv_buffer.end) return ret;
|
||||
|
||||
if (newline == pos)
|
||||
throw std::runtime_error("unexpected newline in HTTP response");
|
||||
|
||||
std::istringstream line(std::string(pos, newline - 1));
|
||||
++newline;
|
||||
int incoming = (int)std::distance(pos, newline);
|
||||
m_recv_pos += incoming;
|
||||
boost::get<1>(ret) += incoming;
|
||||
pos = newline;
|
||||
|
||||
line >> m_protocol;
|
||||
if (m_protocol.substr(0, 5) != "HTTP/")
|
||||
{
|
||||
throw std::runtime_error("unknown protocol in HTTP response: "
|
||||
+ m_protocol);
|
||||
}
|
||||
line >> m_status_code;
|
||||
std::getline(line, m_server_message);
|
||||
m_state = read_header;
|
||||
}
|
||||
|
||||
if (m_state == read_header)
|
||||
{
|
||||
assert(!m_finished);
|
||||
char const* newline = std::find(pos, recv_buffer.end, '\n');
|
||||
std::string line;
|
||||
|
||||
while (newline != recv_buffer.end && m_state == read_header)
|
||||
{
|
||||
if (newline == pos)
|
||||
throw std::runtime_error("unexpected newline in HTTP response");
|
||||
|
||||
line.assign(pos, newline - 1);
|
||||
m_recv_pos += newline - pos;
|
||||
boost::get<1>(ret) += newline - pos;
|
||||
pos = newline;
|
||||
|
||||
std::string::size_type separator = line.find(": ");
|
||||
if (separator == std::string::npos)
|
||||
{
|
||||
++pos;
|
||||
++m_recv_pos;
|
||||
boost::get<1>(ret) += 1;
|
||||
|
||||
m_state = read_body;
|
||||
m_body_start_pos = m_recv_pos;
|
||||
break;
|
||||
}
|
||||
|
||||
std::string name = line.substr(0, separator);
|
||||
std::string value = line.substr(separator + 2, std::string::npos);
|
||||
m_header.insert(std::make_pair(name, value));
|
||||
|
||||
if (name == "Content-Length")
|
||||
{
|
||||
try
|
||||
{
|
||||
m_content_length = boost::lexical_cast<int>(value);
|
||||
}
|
||||
catch(boost::bad_lexical_cast&) {}
|
||||
}
|
||||
else if (name == "Content-Encoding")
|
||||
{
|
||||
if (value == "gzip" || value == "x-gzip")
|
||||
{
|
||||
m_content_encoding = gzip;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string error_str = "unknown content encoding in response: \"";
|
||||
error_str += value;
|
||||
error_str += "\"";
|
||||
throw std::runtime_error(error_str);
|
||||
}
|
||||
}
|
||||
// TODO: make sure we don't step outside of the buffer
|
||||
++pos;
|
||||
++m_recv_pos;
|
||||
assert(m_recv_pos <= (int)recv_buffer.left());
|
||||
newline = std::find(pos, recv_buffer.end, '\n');
|
||||
}
|
||||
}
|
||||
|
||||
if (m_state == read_body)
|
||||
{
|
||||
int incoming = recv_buffer.end - pos;
|
||||
if (m_recv_pos - m_body_start_pos + incoming > m_content_length
|
||||
&& m_content_length >= 0)
|
||||
incoming = m_content_length - m_recv_pos + m_body_start_pos;
|
||||
|
||||
assert(incoming >= 0);
|
||||
m_recv_pos += incoming;
|
||||
boost::get<0>(ret) += incoming;
|
||||
|
||||
if (m_content_length >= 0
|
||||
&& m_recv_pos - m_body_start_pos >= m_content_length)
|
||||
{
|
||||
m_finished = true;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
buffer::const_interval http_parser::get_body()
|
||||
{
|
||||
char const* body_begin = m_recv_buffer.begin + m_body_start_pos;
|
||||
char const* body_end = m_recv_buffer.begin + m_recv_pos;
|
||||
|
||||
m_recv_pos = 0;
|
||||
m_body_start_pos = 0;
|
||||
m_status_code = -1;
|
||||
m_content_length = -1;
|
||||
m_finished = false;
|
||||
m_state = read_status;
|
||||
m_header.clear();
|
||||
|
||||
return buffer::const_interval(body_begin, body_end);
|
||||
}
|
||||
|
||||
http_tracker_connection::http_tracker_connection(
|
||||
demuxer& d
|
||||
, tracker_manager& man
|
||||
, tracker_request const& req
|
||||
, std::string const& hostname
|
||||
, unsigned short port
|
||||
, std::string request
|
||||
, boost::weak_ptr<request_callback> c
|
||||
, session_settings const& stn
|
||||
, std::string const& auth)
|
||||
: tracker_connection(man, req, d, c)
|
||||
, m_man(man)
|
||||
, m_state(read_status)
|
||||
, m_content_encoding(plain)
|
||||
, m_content_length(0)
|
||||
, m_name_lookup(d)
|
||||
, m_port(port)
|
||||
, m_recv_pos(0)
|
||||
, m_buffer(http_buffer_size)
|
||||
, m_settings(stn)
|
||||
, m_password(auth)
|
||||
, m_code(0)
|
||||
, m_timed_out(false)
|
||||
{
|
||||
const std::string* connect_to_host;
|
||||
bool using_proxy = false;
|
||||
|
||||
m_send_buffer.assign("GET ");
|
||||
|
||||
// should we use the proxy?
|
||||
if (!m_settings.proxy_ip.empty())
|
||||
{
|
||||
connect_to_host = &m_settings.proxy_ip;
|
||||
using_proxy = true;
|
||||
m_send_buffer += "http://";
|
||||
m_send_buffer += hostname;
|
||||
if (port != 80)
|
||||
{
|
||||
m_send_buffer += ":";
|
||||
m_send_buffer += boost::lexical_cast<std::string>(port);
|
||||
}
|
||||
m_port = m_settings.proxy_port != 0
|
||||
? m_settings.proxy_port : 80 ;
|
||||
}
|
||||
else
|
||||
{
|
||||
connect_to_host = &hostname;
|
||||
}
|
||||
|
||||
if (tracker_req().kind == tracker_request::scrape_request)
|
||||
{
|
||||
// find and replace "announce" with "scrape"
|
||||
// in request
|
||||
|
||||
std::size_t pos = request.find("announce");
|
||||
if (pos == std::string::npos)
|
||||
throw std::runtime_error("scrape is not available on url: '"
|
||||
+ tracker_req().url +"'");
|
||||
request.replace(pos, 8, "scrape");
|
||||
}
|
||||
|
||||
m_send_buffer += request;
|
||||
|
||||
// if request-string already contains
|
||||
// some parameters, append an ampersand instead
|
||||
// of a question mark
|
||||
if (request.find('?') != std::string::npos)
|
||||
m_send_buffer += "&";
|
||||
else
|
||||
m_send_buffer += "?";
|
||||
|
||||
m_send_buffer += "info_hash=";
|
||||
m_send_buffer += escape_string(
|
||||
reinterpret_cast<const char*>(req.info_hash.begin()), 20);
|
||||
|
||||
if (tracker_req().kind == tracker_request::announce_request)
|
||||
{
|
||||
m_send_buffer += "&peer_id=";
|
||||
m_send_buffer += escape_string(
|
||||
reinterpret_cast<const char*>(req.pid.begin()), 20);
|
||||
|
||||
m_send_buffer += "&port=";
|
||||
m_send_buffer += boost::lexical_cast<std::string>(req.listen_port);
|
||||
|
||||
m_send_buffer += "&uploaded=";
|
||||
m_send_buffer += boost::lexical_cast<std::string>(req.uploaded);
|
||||
|
||||
m_send_buffer += "&downloaded=";
|
||||
m_send_buffer += boost::lexical_cast<std::string>(req.downloaded);
|
||||
|
||||
m_send_buffer += "&left=";
|
||||
m_send_buffer += boost::lexical_cast<std::string>(req.left);
|
||||
|
||||
if (req.event != tracker_request::none)
|
||||
{
|
||||
const char* event_string[] = {"completed", "started", "stopped"};
|
||||
m_send_buffer += "&event=";
|
||||
m_send_buffer += event_string[req.event - 1];
|
||||
}
|
||||
m_send_buffer += "&key=";
|
||||
std::stringstream key_string;
|
||||
key_string << std::hex << req.key;
|
||||
m_send_buffer += key_string.str();
|
||||
m_send_buffer += "&compact=1";
|
||||
m_send_buffer += "&numwant=";
|
||||
m_send_buffer += boost::lexical_cast<std::string>(
|
||||
std::min(req.num_want, 999));
|
||||
|
||||
// extension that tells the tracker that
|
||||
// we don't need any peer_id's in the response
|
||||
m_send_buffer += "&no_peer_id=1";
|
||||
}
|
||||
|
||||
m_send_buffer += " HTTP/1.0\r\nAccept-Encoding: gzip\r\n"
|
||||
"User-Agent: ";
|
||||
m_send_buffer += m_settings.user_agent;
|
||||
m_send_buffer += "\r\n"
|
||||
"Host: ";
|
||||
m_send_buffer += hostname;
|
||||
if (port != 80)
|
||||
{
|
||||
m_send_buffer += ':';
|
||||
m_send_buffer += boost::lexical_cast<std::string>(port);
|
||||
}
|
||||
if (using_proxy && !m_settings.proxy_login.empty())
|
||||
{
|
||||
m_send_buffer += "\r\nProxy-Authorization: Basic ";
|
||||
m_send_buffer += base64encode(m_settings.proxy_login + ":" + m_settings.proxy_password);
|
||||
}
|
||||
if (auth != "")
|
||||
{
|
||||
m_send_buffer += "\r\nAuthorization: Basic ";
|
||||
m_send_buffer += base64encode(auth);
|
||||
}
|
||||
m_send_buffer += "\r\n\r\n";
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (has_requester())
|
||||
{
|
||||
requester().debug_log("==> TRACKER_REQUEST [ str: " + m_send_buffer + " ]");
|
||||
std::stringstream info_hash_str;
|
||||
info_hash_str << req.info_hash;
|
||||
requester().debug_log("info_hash: " + info_hash_str.str() + "\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
tcp::resolver::query q(*connect_to_host, "0");
|
||||
m_name_lookup.async_resolve(q
|
||||
, boost::bind(&http_tracker_connection::name_lookup, self(), _1, _2));
|
||||
set_timeout(m_settings.tracker_completion_timeout
|
||||
, m_settings.tracker_receive_timeout);
|
||||
}
|
||||
|
||||
void http_tracker_connection::on_timeout()
|
||||
{
|
||||
m_timed_out = true;
|
||||
m_socket.reset();
|
||||
m_name_lookup.cancel();
|
||||
fail_timeout();
|
||||
}
|
||||
|
||||
void http_tracker_connection::name_lookup(asio::error const& error
|
||||
, tcp::resolver::iterator i) try
|
||||
{
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
if (m_timed_out) return;
|
||||
|
||||
if (error || i == tcp::resolver::iterator())
|
||||
{
|
||||
fail(-1, error.what());
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (has_requester()) requester().debug_log("tracker name lookup successful");
|
||||
#endif
|
||||
restart_read_timeout();
|
||||
m_socket.reset(new stream_socket(m_name_lookup.io_service()));
|
||||
tcp::endpoint a(i->endpoint().address(), m_port);
|
||||
if (has_requester()) requester().m_tracker_address = a;
|
||||
m_socket->async_connect(a, bind(&http_tracker_connection::connected, self(), _1));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
assert(false);
|
||||
fail(-1, e.what());
|
||||
};
|
||||
|
||||
void http_tracker_connection::connected(asio::error const& error) try
|
||||
{
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
if (m_timed_out) return;
|
||||
if (error)
|
||||
{
|
||||
fail(-1, error.what());
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (has_requester()) requester().debug_log("tracker connection successful");
|
||||
#endif
|
||||
|
||||
restart_read_timeout();
|
||||
async_write(*m_socket, asio::buffer(m_send_buffer.c_str()
|
||||
, m_send_buffer.size()), bind(&http_tracker_connection::sent
|
||||
, self(), _1));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
assert(false);
|
||||
fail(-1, e.what());
|
||||
}
|
||||
|
||||
void http_tracker_connection::sent(asio::error const& error) try
|
||||
{
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
if (m_timed_out) return;
|
||||
if (error)
|
||||
{
|
||||
fail(-1, error.what());
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (has_requester()) requester().debug_log("tracker send data completed");
|
||||
#endif
|
||||
restart_read_timeout();
|
||||
assert(m_buffer.size() - m_recv_pos > 0);
|
||||
m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos]
|
||||
, m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive
|
||||
, self(), _1, _2));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
assert(false);
|
||||
fail(-1, e.what());
|
||||
}; // msvc 7.1 seems to require this semi-colon
|
||||
|
||||
|
||||
void http_tracker_connection::receive(asio::error const& error
|
||||
, std::size_t bytes_transferred) try
|
||||
{
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
if (m_timed_out) return;
|
||||
|
||||
if (error)
|
||||
{
|
||||
if (error == asio::error::eof)
|
||||
{
|
||||
on_response();
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
fail(-1, error.what());
|
||||
return;
|
||||
}
|
||||
|
||||
restart_read_timeout();
|
||||
assert(bytes_transferred > 0);
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (has_requester()) requester().debug_log("tracker connection reading "
|
||||
+ boost::lexical_cast<std::string>(bytes_transferred));
|
||||
#endif
|
||||
|
||||
m_recv_pos += bytes_transferred;
|
||||
|
||||
// if the receive buffer is full, expand it with http_buffer_size
|
||||
if ((int)m_buffer.size() == m_recv_pos)
|
||||
{
|
||||
if ((int)m_buffer.size() >= m_settings.tracker_maximum_response_length)
|
||||
{
|
||||
fail(200, "too large tracker response");
|
||||
return;
|
||||
}
|
||||
assert(http_buffer_size > 0);
|
||||
if ((int)m_buffer.size() + http_buffer_size
|
||||
> m_settings.tracker_maximum_response_length)
|
||||
m_buffer.resize(m_settings.tracker_maximum_response_length);
|
||||
else
|
||||
m_buffer.resize(m_buffer.size() + http_buffer_size);
|
||||
}
|
||||
|
||||
if (m_state == read_status)
|
||||
{
|
||||
std::vector<char>::iterator end = m_buffer.begin()+m_recv_pos;
|
||||
std::vector<char>::iterator newline = std::find(m_buffer.begin(), end, '\n');
|
||||
// if we don't have a full line yet, wait.
|
||||
if (newline != end)
|
||||
{
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (has_requester()) requester().debug_log(std::string(m_buffer.begin(), newline));
|
||||
#endif
|
||||
|
||||
std::istringstream line(std::string(m_buffer.begin(), newline));
|
||||
++newline;
|
||||
m_recv_pos -= (int)std::distance(m_buffer.begin(), newline);
|
||||
m_buffer.erase(m_buffer.begin(), newline);
|
||||
|
||||
std::string protocol;
|
||||
line >> m_server_protocol;
|
||||
if (m_server_protocol.substr(0, 5) != "HTTP/")
|
||||
{
|
||||
std::string error_msg = "unknown protocol in response: " + m_server_protocol;
|
||||
fail(-1, error_msg.c_str());
|
||||
return;
|
||||
}
|
||||
line >> m_code;
|
||||
std::getline(line, m_server_message);
|
||||
m_state = read_header;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_state == read_header)
|
||||
{
|
||||
std::vector<char>::iterator end = m_buffer.begin() + m_recv_pos;
|
||||
std::vector<char>::iterator newline
|
||||
= std::find(m_buffer.begin(), end, '\n');
|
||||
std::string line;
|
||||
|
||||
while (newline != end && m_state == read_header)
|
||||
{
|
||||
line.assign(m_buffer.begin(), newline);
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (has_requester()) requester().debug_log(line);
|
||||
#endif
|
||||
|
||||
if (line.substr(0, 16) == "Content-Length: ")
|
||||
{
|
||||
try
|
||||
{
|
||||
m_content_length = boost::lexical_cast<int>(
|
||||
line.substr(16, line.length() - 17));
|
||||
}
|
||||
catch(boost::bad_lexical_cast&)
|
||||
{
|
||||
fail(-1, "invalid content-length in tracker response");
|
||||
return;
|
||||
}
|
||||
if (m_content_length > m_settings.tracker_maximum_response_length)
|
||||
{
|
||||
fail(-1, "content-length is greater than maximum response length");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_content_length < minimum_tracker_response_length && m_code == 200)
|
||||
{
|
||||
fail(-1, "content-length is smaller than minimum response length");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (line.substr(0, 18) == "Content-Encoding: ")
|
||||
{
|
||||
if (line.substr(18, 4) == "gzip" || line.substr(18, 6) == "x-gzip")
|
||||
{
|
||||
m_content_encoding = gzip;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string error_str = "unknown content encoding in response: \"";
|
||||
error_str += line.substr(18, line.length() - 18 - 2);
|
||||
error_str += "\"";
|
||||
fail(-1, error_str.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (line.substr(0, 10) == "Location: ")
|
||||
{
|
||||
m_location.assign(line.begin() + 10, line.end());
|
||||
}
|
||||
else if (line.substr(0, 7) == "Server: ")
|
||||
{
|
||||
m_server.assign(line.begin() + 7, line.end());
|
||||
}
|
||||
else if (line.size() < 3)
|
||||
{
|
||||
m_state = read_body;
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (has_requester()) requester().debug_log("end of http header");
|
||||
#endif
|
||||
if (m_code >= 300 && m_code < 400)
|
||||
{
|
||||
if (m_location.empty())
|
||||
{
|
||||
std::string error_str = "got redirection response (";
|
||||
error_str += boost::lexical_cast<std::string>(m_code);
|
||||
error_str += ") without 'Location' header";
|
||||
fail(-1, error_str.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (has_requester()) requester().debug_log("Redirecting to \"" + m_location + "\"");
|
||||
#endif
|
||||
tracker_request req = tracker_req();
|
||||
std::string::size_type i = m_location.find('?');
|
||||
if (i == std::string::npos)
|
||||
req.url = m_location;
|
||||
else
|
||||
req.url.assign(m_location.begin(), m_location.begin() + i);
|
||||
|
||||
m_man.queue_request(m_socket->io_service(), req
|
||||
, m_password, m_requester);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
++newline;
|
||||
assert(m_recv_pos <= (int)m_buffer.size());
|
||||
m_recv_pos -= (int)std::distance(m_buffer.begin(), newline);
|
||||
m_buffer.erase(m_buffer.begin(), newline);
|
||||
assert(m_recv_pos <= (int)m_buffer.size());
|
||||
end = m_buffer.begin() + m_recv_pos;
|
||||
newline = std::find(m_buffer.begin(), end, '\n');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (m_state == read_body)
|
||||
{
|
||||
if (m_recv_pos == m_content_length)
|
||||
{
|
||||
on_response();
|
||||
close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (m_recv_pos > m_content_length && m_content_length > 0)
|
||||
{
|
||||
fail(-1, "invalid tracker response (body > content_length)");
|
||||
return;
|
||||
}
|
||||
|
||||
assert(m_buffer.size() - m_recv_pos > 0);
|
||||
m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos]
|
||||
, m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive
|
||||
, self(), _1, _2));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
assert(false);
|
||||
fail(-1, e.what());
|
||||
};
|
||||
|
||||
void http_tracker_connection::on_response()
|
||||
{
|
||||
// GZIP
|
||||
if (m_content_encoding == gzip)
|
||||
{
|
||||
boost::shared_ptr<request_callback> r = m_requester.lock();
|
||||
|
||||
if (!r)
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
if (inflate_gzip(m_buffer, tracker_request(), r.get(),
|
||||
m_settings.tracker_maximum_response_length))
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// handle tracker response
|
||||
try
|
||||
{
|
||||
entry e = bdecode(m_buffer.begin(), m_buffer.end());
|
||||
parse(e);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::string error_str(e.what());
|
||||
error_str += ": ";
|
||||
error_str.append(m_buffer.begin(), m_buffer.end());
|
||||
fail(m_code, error_str.c_str());
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
catch (...)
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
peer_entry http_tracker_connection::extract_peer_info(const entry& info)
|
||||
{
|
||||
peer_entry ret;
|
||||
|
||||
// extract peer id (if any)
|
||||
entry const* i = info.find_key("peer id");
|
||||
if (i != 0)
|
||||
{
|
||||
if (i->string().length() != 20)
|
||||
throw std::runtime_error("invalid response from tracker");
|
||||
std::copy(i->string().begin(), i->string().end(), ret.pid.begin());
|
||||
}
|
||||
else
|
||||
{
|
||||
// if there's no peer_id, just initialize it to a bunch of zeroes
|
||||
std::fill_n(ret.pid.begin(), 20, 0);
|
||||
}
|
||||
|
||||
// extract ip
|
||||
i = info.find_key("ip");
|
||||
if (i == 0) throw std::runtime_error("invalid response from tracker");
|
||||
ret.ip = i->string();
|
||||
|
||||
// extract port
|
||||
i = info.find_key("port");
|
||||
if (i == 0) throw std::runtime_error("invalid response from tracker");
|
||||
ret.port = (unsigned short)i->integer();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void http_tracker_connection::parse(entry const& e)
|
||||
{
|
||||
if (!has_requester()) return;
|
||||
|
||||
try
|
||||
{
|
||||
// parse the response
|
||||
try
|
||||
{
|
||||
entry const& failure = e["failure reason"];
|
||||
|
||||
fail(m_code, failure.string().c_str());
|
||||
return;
|
||||
}
|
||||
catch (type_error const&) {}
|
||||
|
||||
try
|
||||
{
|
||||
entry const& warning = e["warning message"];
|
||||
if (has_requester())
|
||||
requester().tracker_warning(warning.string());
|
||||
}
|
||||
catch(type_error const&) {}
|
||||
|
||||
std::vector<peer_entry> peer_list;
|
||||
|
||||
if (tracker_req().kind == tracker_request::scrape_request)
|
||||
{
|
||||
std::string ih;
|
||||
std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end()
|
||||
, std::back_inserter(ih));
|
||||
entry scrape_data = e["files"][ih];
|
||||
int complete = scrape_data["complete"].integer();
|
||||
int incomplete = scrape_data["incomplete"].integer();
|
||||
requester().tracker_response(tracker_request(), peer_list, 0, complete
|
||||
, incomplete);
|
||||
return;
|
||||
}
|
||||
|
||||
int interval = (int)e["interval"].integer();
|
||||
|
||||
if (e["peers"].type() == entry::string_t)
|
||||
{
|
||||
std::string const& peers = e["peers"].string();
|
||||
for (std::string::const_iterator i = peers.begin();
|
||||
i != peers.end();)
|
||||
{
|
||||
if (std::distance(i, peers.end()) < 6) break;
|
||||
|
||||
peer_entry p;
|
||||
p.pid.clear();
|
||||
std::stringstream ip_str;
|
||||
ip_str << (int)detail::read_uint8(i) << ".";
|
||||
ip_str << (int)detail::read_uint8(i) << ".";
|
||||
ip_str << (int)detail::read_uint8(i) << ".";
|
||||
ip_str << (int)detail::read_uint8(i);
|
||||
p.ip = ip_str.str();
|
||||
p.port = detail::read_uint16(i);
|
||||
peer_list.push_back(p);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entry::list_type const& l = e["peers"].list();
|
||||
for(entry::list_type::const_iterator i = l.begin(); i != l.end(); ++i)
|
||||
{
|
||||
peer_entry p = extract_peer_info(*i);
|
||||
peer_list.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
// look for optional scrape info
|
||||
int complete = -1;
|
||||
int incomplete = -1;
|
||||
|
||||
try { complete = e["complete"].integer(); }
|
||||
catch(type_error&) {}
|
||||
|
||||
try { incomplete = e["incomplete"].integer(); }
|
||||
catch(type_error&) {}
|
||||
|
||||
requester().tracker_response(tracker_request(), peer_list, interval, complete
|
||||
, incomplete);
|
||||
}
|
||||
catch(type_error& e)
|
||||
{
|
||||
requester().tracker_request_error(tracker_request(), m_code, e.what());
|
||||
}
|
||||
catch(std::runtime_error& e)
|
||||
{
|
||||
requester().tracker_request_error(tracker_request(), m_code, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
326
cpp/identify_client.cpp
Executable file
326
cpp/identify_client.cpp
Executable file
@ -0,0 +1,326 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2003, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 1)
|
||||
#endif
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "libtorrent/identify_client.hpp"
|
||||
#include "libtorrent/fingerprint.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
using namespace libtorrent;
|
||||
|
||||
int decode_digit(char c)
|
||||
{
|
||||
if (std::isdigit(c)) return c - '0';
|
||||
return unsigned(c) - 'A' + 10;
|
||||
}
|
||||
|
||||
// takes a peer id and returns a valid boost::optional
|
||||
// object if the peer id matched the azureus style encoding
|
||||
// the returned fingerprint contains information about the
|
||||
// client's id
|
||||
boost::optional<fingerprint> parse_az_style(const peer_id& id)
|
||||
{
|
||||
fingerprint ret("..", 0, 0, 0, 0);
|
||||
|
||||
if (id[0] != '-' || !std::isprint(id[1]) || (id[2] < '0')
|
||||
|| (id[3] < '0') || (id[4] < '0')
|
||||
|| (id[5] < '0') || (id[6] < '0')
|
||||
|| id[7] != '-')
|
||||
return boost::optional<fingerprint>();
|
||||
|
||||
ret.name[0] = id[1];
|
||||
ret.name[1] = id[2];
|
||||
ret.major_version = decode_digit(id[3]);
|
||||
ret.minor_version = decode_digit(id[4]);
|
||||
ret.revision_version = decode_digit(id[5]);
|
||||
ret.tag_version = decode_digit(id[6]);
|
||||
|
||||
return boost::optional<fingerprint>(ret);
|
||||
}
|
||||
|
||||
// checks if a peer id can possibly contain a shadow-style
|
||||
// identification
|
||||
boost::optional<fingerprint> parse_shadow_style(const peer_id& id)
|
||||
{
|
||||
fingerprint ret("..", 0, 0, 0, 0);
|
||||
|
||||
if (!std::isalnum(id[0]))
|
||||
return boost::optional<fingerprint>();
|
||||
|
||||
if (std::equal(id.begin()+4, id.begin()+6, "--"))
|
||||
{
|
||||
if ((id[1] < '0') || (id[2] < '0')
|
||||
|| (id[3] < '0'))
|
||||
return boost::optional<fingerprint>();
|
||||
ret.major_version = decode_digit(id[1]);
|
||||
ret.minor_version = decode_digit(id[2]);
|
||||
ret.revision_version = decode_digit(id[3]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (id[8] != 0 || id[1] > 127 || id[2] > 127 || id[3] > 127)
|
||||
return boost::optional<fingerprint>();
|
||||
ret.major_version = id[1];
|
||||
ret.minor_version = id[2];
|
||||
ret.revision_version = id[3];
|
||||
}
|
||||
|
||||
ret.name[0] = id[0];
|
||||
ret.name[1] = 0;
|
||||
|
||||
ret.tag_version = 0;
|
||||
return boost::optional<fingerprint>(ret);
|
||||
}
|
||||
|
||||
// checks if a peer id can possibly contain a mainline-style
|
||||
// identification
|
||||
boost::optional<fingerprint> parse_mainline_style(const peer_id& id)
|
||||
{
|
||||
char ids[21];
|
||||
std::copy(id.begin(), id.end(), ids);
|
||||
ids[20] = 0;
|
||||
fingerprint ret("..", 0, 0, 0, 0);
|
||||
ret.name[1] = 0;
|
||||
ret.tag_version = 0;
|
||||
if (sscanf(ids, "%c%d-%d-%d--", &ret.name[0], &ret.major_version, &ret.minor_version
|
||||
, &ret.revision_version) != 4
|
||||
|| !std::isprint(ret.name[0]))
|
||||
return boost::optional<fingerprint>();
|
||||
|
||||
return boost::optional<fingerprint>(ret);
|
||||
}
|
||||
|
||||
typedef std::pair<char const*, char const*> map_entry;
|
||||
|
||||
// only support BitTorrentSpecification
|
||||
// must be ordered alphabetically
|
||||
map_entry name_map[] =
|
||||
{
|
||||
map_entry("A", "ABC")
|
||||
, map_entry("AR", "Arctic Torrent")
|
||||
, map_entry("AX", "BitPump")
|
||||
, map_entry("AZ", "Azureus")
|
||||
, map_entry("BB", "BitBuddy")
|
||||
, map_entry("BC", "BitComet")
|
||||
, map_entry("BS", "BTSlave")
|
||||
, map_entry("BX", "BittorrentX")
|
||||
, map_entry("CD", "Enhanced CTorrent")
|
||||
, map_entry("CT", "CTorrent")
|
||||
, map_entry("DE", "Deluge")
|
||||
, map_entry("ES", "electric sheep")
|
||||
, map_entry("KT", "KTorrent")
|
||||
, map_entry("LP", "lphant")
|
||||
, map_entry("LT", "libtorrent")
|
||||
, map_entry("M", "Mainline")
|
||||
, map_entry("MP", "MooPolice")
|
||||
, map_entry("MT", "Moonlight Torrent")
|
||||
, map_entry("O", "Osprey Permaseed")
|
||||
, map_entry("R", "Tribler")
|
||||
, map_entry("S", "Shadow")
|
||||
, map_entry("SB", "Swiftbit")
|
||||
, map_entry("SN", "ShareNet")
|
||||
, map_entry("SS", "SwarmScope")
|
||||
, map_entry("SZ", "Shareaza")
|
||||
, map_entry("T", "BitTornado")
|
||||
, map_entry("TN", "Torrent.NET")
|
||||
, map_entry("TR", "Transmission")
|
||||
, map_entry("TS", "TorrentStorm")
|
||||
, map_entry("U", "UPnP")
|
||||
, map_entry("UL", "uLeecher")
|
||||
, map_entry("UT", "MicroTorrent")
|
||||
, map_entry("XT", "XanTorrent")
|
||||
, map_entry("ZT", "ZipTorrent")
|
||||
, map_entry("lt", "libTorrent (libtorrent.rakshasa.no/)")
|
||||
, map_entry("pX", "pHoeniX")
|
||||
, map_entry("qB", "qBittorrent")
|
||||
};
|
||||
|
||||
bool compare_first_string(map_entry const& lhs, map_entry const& rhs)
|
||||
{
|
||||
return lhs.first[0] < rhs.first[0]
|
||||
|| ((lhs.first[0] == rhs.first[0]) && (lhs.first[1] < rhs.first[1]));
|
||||
}
|
||||
|
||||
std::string lookup(fingerprint const& f)
|
||||
{
|
||||
std::stringstream identity;
|
||||
|
||||
const int size = sizeof(name_map)/sizeof(name_map[0]);
|
||||
map_entry* i =
|
||||
std::lower_bound(name_map, name_map + size
|
||||
, map_entry(f.name, ""), &compare_first_string);
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (int i = 1; i < size; ++i)
|
||||
{
|
||||
assert(compare_first_string(name_map[i-1]
|
||||
, name_map[i]));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (i < name_map + size && std::equal(f.name, f.name + 2, i->first))
|
||||
identity << i->second;
|
||||
else
|
||||
{
|
||||
identity << f.name[0];
|
||||
if (f.name[1] != 0) identity << f.name[1];
|
||||
}
|
||||
|
||||
identity << " " << (int)f.major_version
|
||||
<< "." << (int)f.minor_version
|
||||
<< "." << (int)f.revision_version;
|
||||
|
||||
if (f.name[1] != 0)
|
||||
identity << "." << (int)f.tag_version;
|
||||
|
||||
return identity.str();
|
||||
}
|
||||
|
||||
bool find_string(unsigned char const* id, char const* search)
|
||||
{
|
||||
return std::equal(search, search + std::strlen(search), id);
|
||||
}
|
||||
}
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
boost::optional<fingerprint> client_fingerprint(peer_id const& p)
|
||||
{
|
||||
// look for azureus style id
|
||||
boost::optional<fingerprint> f;
|
||||
f = parse_az_style(p);
|
||||
if (f) return f;
|
||||
|
||||
// look for shadow style id
|
||||
f = parse_shadow_style(p);
|
||||
if (f) return f;
|
||||
|
||||
// look for mainline style id
|
||||
f = parse_mainline_style(p);
|
||||
if (f) return f;
|
||||
return f;
|
||||
}
|
||||
|
||||
std::string identify_client(peer_id const& p)
|
||||
{
|
||||
peer_id::const_iterator PID = p.begin();
|
||||
boost::optional<fingerprint> f;
|
||||
|
||||
if (p.is_all_zeros()) return "Unknown";
|
||||
|
||||
// ----------------------
|
||||
// non standard encodings
|
||||
// ----------------------
|
||||
|
||||
if (find_string(PID, "Deadman Walking-")) return "Deadman";
|
||||
if (find_string(PID + 5, "Azureus")) return "Azureus 2.0.3.2";
|
||||
if (find_string(PID, "DansClient")) return "XanTorrent";
|
||||
if (find_string(PID + 4, "btfans")) return "SimpleBT";
|
||||
if (find_string(PID, "PRC.P---")) return "Bittorrent Plus! II";
|
||||
if (find_string(PID, "P87.P---")) return "Bittorrent Plus!";
|
||||
if (find_string(PID, "S587Plus")) return "Bittorrent Plus!";
|
||||
if (find_string(PID, "martini")) return "Martini Man";
|
||||
if (find_string(PID, "Plus---")) return "Bittorrent Plus";
|
||||
if (find_string(PID, "turbobt")) return "TurboBT";
|
||||
if (find_string(PID, "a00---0")) return "Swarmy";
|
||||
if (find_string(PID, "a02---0")) return "Swarmy";
|
||||
if (find_string(PID, "T00---0")) return "Teeweety";
|
||||
if (find_string(PID, "BTDWV-")) return "Deadman Walking";
|
||||
if (find_string(PID + 2, "BS")) return "BitSpirit";
|
||||
if (find_string(PID, "btuga")) return "BTugaXP";
|
||||
if (find_string(PID, "oernu")) return "BTugaXP";
|
||||
if (find_string(PID, "Mbrst")) return "Burst!";
|
||||
if (find_string(PID, "Plus")) return "Plus!";
|
||||
if (find_string(PID, "-Qt-")) return "Qt";
|
||||
if (find_string(PID, "exbc")) return "BitComet";
|
||||
if (find_string(PID, "-G3")) return "G3 Torrent";
|
||||
if (find_string(PID, "XBT")) return "XBT";
|
||||
if (find_string(PID, "OP")) return "Opera";
|
||||
|
||||
if (find_string(PID, "-BOW") && PID[7] == '-')
|
||||
return "Bits on Wheels " + std::string(PID + 4, PID + 7);
|
||||
|
||||
|
||||
if (find_string(PID, "eX"))
|
||||
{
|
||||
std::string user(PID + 2, PID + 14);
|
||||
return std::string("eXeem ('") + user.c_str() + "')";
|
||||
}
|
||||
|
||||
if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\x97"))
|
||||
return "Experimental 3.2.1b2";
|
||||
|
||||
if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0"))
|
||||
return "Experimental 3.1";
|
||||
|
||||
|
||||
// look for azureus style id
|
||||
f = parse_az_style(p);
|
||||
if (f) return lookup(*f);
|
||||
|
||||
// look for shadow style id
|
||||
f = parse_shadow_style(p);
|
||||
if (f) return lookup(*f);
|
||||
|
||||
// look for mainline style id
|
||||
f = parse_mainline_style(p);
|
||||
if (f) return lookup(*f);
|
||||
|
||||
|
||||
if (std::equal(PID, PID + 12, "\0\0\0\0\0\0\0\0\0\0\0\0"))
|
||||
return "Generic";
|
||||
|
||||
std::string unknown("Unknown [");
|
||||
for (peer_id::const_iterator i = p.begin(); i != p.end(); ++i)
|
||||
{
|
||||
unknown += std::isprint(*i)?*i:'.';
|
||||
}
|
||||
unknown += "]";
|
||||
return unknown;
|
||||
}
|
||||
|
||||
}
|
||||
80
cpp/ip_filter.cpp
Normal file
80
cpp/ip_filter.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2005, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "libtorrent/ip_filter.hpp"
|
||||
#include <boost/utility.hpp>
|
||||
//#include <iostream>
|
||||
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
void ip_filter::add_rule(address first, address last, int flags)
|
||||
{
|
||||
if (first.is_v4())
|
||||
{
|
||||
assert(last.is_v4());
|
||||
m_filter4.add_rule(first.to_v4(), last.to_v4(), flags);
|
||||
}
|
||||
else if (first.is_v6())
|
||||
{
|
||||
assert(last.is_v6());
|
||||
m_filter6.add_rule(first.to_v6(), last.to_v6(), flags);
|
||||
}
|
||||
else
|
||||
assert(false);
|
||||
}
|
||||
|
||||
int ip_filter::access(address const& addr) const
|
||||
{
|
||||
if (addr.is_v4())
|
||||
return m_filter4.access(addr.to_v4());
|
||||
assert(addr.is_v6());
|
||||
return m_filter6.access(addr.to_v6());
|
||||
}
|
||||
|
||||
ip_filter::filter_tuple_t ip_filter::export_filter() const
|
||||
{
|
||||
return boost::make_tuple(m_filter4.export_filter()
|
||||
, m_filter6.export_filter());
|
||||
}
|
||||
|
||||
/*
|
||||
void ip_filter::print() const
|
||||
{
|
||||
for (range_t::iterator i = m_access_list.begin(); i != m_access_list.end(); ++i)
|
||||
{
|
||||
std::cout << i->start.as_string() << " " << i->access << "\n";
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
145
cpp/kademlia/closest_nodes.cpp
Normal file
145
cpp/kademlia/closest_nodes.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <libtorrent/kademlia/closest_nodes.hpp>
|
||||
#include <libtorrent/kademlia/routing_table.hpp>
|
||||
#include <libtorrent/kademlia/rpc_manager.hpp>
|
||||
|
||||
namespace libtorrent { namespace dht
|
||||
{
|
||||
|
||||
using asio::ip::udp;
|
||||
|
||||
typedef boost::shared_ptr<observer> observer_ptr;
|
||||
|
||||
class closest_nodes_observer : public observer
|
||||
{
|
||||
public:
|
||||
closest_nodes_observer(
|
||||
boost::intrusive_ptr<traversal_algorithm> const& algorithm
|
||||
, node_id self
|
||||
, node_id target
|
||||
)
|
||||
: m_algorithm(algorithm)
|
||||
, m_target(target)
|
||||
, m_self(self)
|
||||
{}
|
||||
|
||||
void send(msg& p)
|
||||
{
|
||||
p.info_hash = m_target;
|
||||
}
|
||||
|
||||
void timeout();
|
||||
void reply(msg const&);
|
||||
|
||||
private:
|
||||
boost::intrusive_ptr<traversal_algorithm> m_algorithm;
|
||||
node_id const m_target;
|
||||
node_id const m_self;
|
||||
};
|
||||
|
||||
void closest_nodes_observer::reply(msg const& in)
|
||||
{
|
||||
if (!in.nodes.empty())
|
||||
{
|
||||
for (msg::nodes_t::const_iterator i = in.nodes.begin()
|
||||
, end(in.nodes.end()); i != end; ++i)
|
||||
{
|
||||
m_algorithm->traverse(i->id, i->addr);
|
||||
}
|
||||
}
|
||||
m_algorithm->finished(m_self);
|
||||
}
|
||||
|
||||
void closest_nodes_observer::timeout()
|
||||
{
|
||||
m_algorithm->failed(m_self);
|
||||
}
|
||||
|
||||
|
||||
closest_nodes::closest_nodes(
|
||||
node_id target
|
||||
, int branch_factor
|
||||
, int max_results
|
||||
, routing_table& table
|
||||
, rpc_manager& rpc
|
||||
, done_callback const& callback
|
||||
)
|
||||
: traversal_algorithm(
|
||||
target
|
||||
, branch_factor
|
||||
, max_results
|
||||
, table
|
||||
, rpc
|
||||
, table.begin()
|
||||
, table.end()
|
||||
)
|
||||
, m_done_callback(callback)
|
||||
{
|
||||
boost::intrusive_ptr<closest_nodes> self(this);
|
||||
add_requests();
|
||||
}
|
||||
|
||||
void closest_nodes::invoke(node_id const& id, udp::endpoint addr)
|
||||
{
|
||||
observer_ptr p(new closest_nodes_observer(this, id, m_target));
|
||||
m_rpc.invoke(messages::find_node, addr, p);
|
||||
}
|
||||
|
||||
void closest_nodes::done()
|
||||
{
|
||||
std::vector<node_entry> results;
|
||||
int result_size = m_table.bucket_size();
|
||||
if (result_size > (int)m_results.size()) result_size = (int)m_results.size();
|
||||
for (std::vector<result>::iterator i = m_results.begin()
|
||||
, end(m_results.begin() + result_size); i != end; ++i)
|
||||
{
|
||||
results.push_back(node_entry(i->id, i->addr));
|
||||
}
|
||||
m_done_callback(results);
|
||||
}
|
||||
|
||||
void closest_nodes::initiate(
|
||||
node_id target
|
||||
, int branch_factor
|
||||
, int max_results
|
||||
, routing_table& table
|
||||
, rpc_manager& rpc
|
||||
, done_callback const& callback
|
||||
)
|
||||
{
|
||||
new closest_nodes(target, branch_factor, max_results, table, rpc, callback);
|
||||
}
|
||||
|
||||
} } // namespace libtorrent::dht
|
||||
|
||||
905
cpp/kademlia/dht_tracker.cpp
Normal file
905
cpp/kademlia/dht_tracker.cpp
Normal file
@ -0,0 +1,905 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2006, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <set>
|
||||
#include <numeric>
|
||||
#include <stdexcept>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include <boost/date_time/posix_time/ptime.hpp>
|
||||
#include <boost/ref.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
|
||||
#include "libtorrent/kademlia/node.hpp"
|
||||
#include "libtorrent/kademlia/node_id.hpp"
|
||||
#include "libtorrent/kademlia/traversal_algorithm.hpp"
|
||||
#include "libtorrent/kademlia/dht_tracker.hpp"
|
||||
|
||||
#include "libtorrent/socket.hpp"
|
||||
#include "libtorrent/bencode.hpp"
|
||||
#include "libtorrent/io.hpp"
|
||||
#include "libtorrent/version.hpp"
|
||||
|
||||
using boost::posix_time::ptime;
|
||||
using boost::posix_time::time_duration;
|
||||
using boost::posix_time::second_clock;
|
||||
using boost::posix_time::microsec_clock;
|
||||
using boost::posix_time::seconds;
|
||||
using boost::posix_time::minutes;
|
||||
using boost::posix_time::hours;
|
||||
using boost::posix_time::milliseconds;
|
||||
using boost::ref;
|
||||
using boost::lexical_cast;
|
||||
using libtorrent::dht::node_impl;
|
||||
using libtorrent::dht::node_id;
|
||||
using libtorrent::dht::packet_t;
|
||||
using libtorrent::dht::msg;
|
||||
using libtorrent::dht::packet_iterator;
|
||||
namespace messages = libtorrent::dht::messages;
|
||||
using namespace libtorrent::detail;
|
||||
|
||||
using asio::ip::udp;
|
||||
typedef asio::ip::address_v4 address;
|
||||
|
||||
namespace
|
||||
{
|
||||
const int tick_period = 1; // minutes
|
||||
|
||||
struct count_peers
|
||||
{
|
||||
int& count;
|
||||
count_peers(int& c): count(c) {}
|
||||
void operator()(std::pair<libtorrent::dht::node_id
|
||||
, libtorrent::dht::torrent_entry> const& t)
|
||||
{
|
||||
count += std::distance(t.second.peers.begin()
|
||||
, t.second.peers.end());
|
||||
}
|
||||
};
|
||||
|
||||
boost::optional<node_id> read_id(libtorrent::entry const& d)
|
||||
{
|
||||
using namespace libtorrent;
|
||||
using libtorrent::dht::node_id;
|
||||
|
||||
if (d.type() != entry::dictionary_t) return boost::optional<node_id>();
|
||||
entry const* nid = d.find_key("node-id");
|
||||
if (!nid
|
||||
|| nid->type() != entry::string_t
|
||||
|| nid->string().length() != 40)
|
||||
return boost::optional<node_id>();
|
||||
return boost::optional<node_id>(
|
||||
boost::lexical_cast<node_id>(nid->string()));
|
||||
}
|
||||
|
||||
template <class EndpointType>
|
||||
void read_endpoint_list(libtorrent::entry const* n, std::vector<EndpointType>& epl)
|
||||
{
|
||||
using namespace libtorrent;
|
||||
entry::list_type const& contacts = n->list();
|
||||
for (entry::list_type::const_iterator i = contacts.begin()
|
||||
, end(contacts.end()); i != end; ++i)
|
||||
{
|
||||
std::string const& p = i->string();
|
||||
if (p.size() < 6) continue;
|
||||
std::string::const_iterator in = p.begin();
|
||||
if (p.size() == 6)
|
||||
epl.push_back(read_v4_endpoint<EndpointType>(in));
|
||||
else if (p.size() == 18)
|
||||
epl.push_back(read_v6_endpoint<EndpointType>(in));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace libtorrent { namespace dht
|
||||
{
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_DEFINE_LOG(dht_tracker)
|
||||
#endif
|
||||
|
||||
// class that puts the networking and the kademlia node in a single
|
||||
// unit and connecting them together.
|
||||
dht_tracker::dht_tracker(asio::io_service& d, dht_settings const& settings
|
||||
, asio::ip::address listen_interface, entry const& bootstrap)
|
||||
: m_demuxer(d)
|
||||
, m_socket(m_demuxer, udp::endpoint(listen_interface, settings.service_port))
|
||||
, m_dht(bind(&dht_tracker::send_packet, this, _1), settings
|
||||
, read_id(bootstrap))
|
||||
, m_buffer(0)
|
||||
, m_last_refresh(second_clock::universal_time() - hours(1))
|
||||
, m_timer(m_demuxer)
|
||||
, m_connection_timer(m_demuxer)
|
||||
, m_refresh_timer(m_demuxer)
|
||||
, m_settings(settings)
|
||||
, m_refresh_bucket(160)
|
||||
, m_host_resolver(d)
|
||||
{
|
||||
using boost::bind;
|
||||
|
||||
m_in_buf[0].resize(1000);
|
||||
m_in_buf[1].resize(1000);
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
m_counter = 0;
|
||||
std::fill_n(m_replies_bytes_sent, 5, 0);
|
||||
std::fill_n(m_queries_bytes_received, 5, 0);
|
||||
std::fill_n(m_replies_sent, 5, 0);
|
||||
std::fill_n(m_queries_received, 5, 0);
|
||||
m_announces = 0;
|
||||
m_failed_announces = 0;
|
||||
m_total_message_input = 0;
|
||||
m_ut_message_input = 0;
|
||||
m_lt_message_input = 0;
|
||||
m_mp_message_input = 0;
|
||||
m_gr_message_input = 0;
|
||||
m_total_in_bytes = 0;
|
||||
m_total_out_bytes = 0;
|
||||
m_queries_out_bytes = 0;
|
||||
|
||||
// turns on and off individual components' logging
|
||||
|
||||
// rpc_log().enable(false);
|
||||
// node_log().enable(false);
|
||||
// traversal_log().enable(false);
|
||||
// dht_tracker_log.enable(false);
|
||||
|
||||
#endif
|
||||
std::vector<udp::endpoint> initial_nodes;
|
||||
|
||||
if (bootstrap.type() == entry::dictionary_t)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (entry const* nodes = bootstrap.find_key("nodes"))
|
||||
read_endpoint_list<udp::endpoint>(nodes, initial_nodes);
|
||||
} catch (std::exception&) {}
|
||||
}
|
||||
|
||||
m_dht.bootstrap(initial_nodes, bind(&dht_tracker::on_bootstrap, this));
|
||||
|
||||
m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0]
|
||||
, m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer]
|
||||
, bind(&dht_tracker::on_receive, this, _1, _2));
|
||||
m_timer.expires_from_now(seconds(1));
|
||||
m_timer.async_wait(bind(&dht_tracker::tick, this, _1));
|
||||
|
||||
m_connection_timer.expires_from_now(seconds(10));
|
||||
m_connection_timer.async_wait(bind(&dht_tracker::connection_timeout, this, _1));
|
||||
|
||||
m_refresh_timer.expires_from_now(minutes(15));
|
||||
m_refresh_timer.async_wait(bind(&dht_tracker::refresh_timeout, this, _1));
|
||||
}
|
||||
|
||||
void dht_tracker::dht_status(session_status& s)
|
||||
{
|
||||
boost::tie(s.m_dht_nodes, s.m_dht_node_cache) = m_dht.size();
|
||||
s.m_dht_torrents = m_dht.data_size();
|
||||
}
|
||||
|
||||
void dht_tracker::connection_timeout(asio::error const& e)
|
||||
try
|
||||
{
|
||||
if (e) return;
|
||||
time_duration d = m_dht.connection_timeout();
|
||||
m_connection_timer.expires_from_now(d);
|
||||
m_connection_timer.async_wait(bind(&dht_tracker::connection_timeout, this, _1));
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
assert(false);
|
||||
};
|
||||
|
||||
void dht_tracker::refresh_timeout(asio::error const& e)
|
||||
try
|
||||
{
|
||||
if (e) return;
|
||||
time_duration d = m_dht.refresh_timeout();
|
||||
m_refresh_timer.expires_from_now(d);
|
||||
m_refresh_timer.async_wait(bind(&dht_tracker::refresh_timeout, this, _1));
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
assert(false);
|
||||
};
|
||||
|
||||
void dht_tracker::rebind(asio::ip::address listen_interface, int listen_port)
|
||||
{
|
||||
m_socket.close();
|
||||
m_socket.open(asio::ip::udp::v4());
|
||||
m_socket.bind(udp::endpoint(listen_interface, listen_port));
|
||||
}
|
||||
|
||||
void dht_tracker::tick(asio::error const& e)
|
||||
try
|
||||
{
|
||||
if (e) return;
|
||||
m_timer.expires_from_now(minutes(tick_period));
|
||||
m_timer.async_wait(bind(&dht_tracker::tick, this, _1));
|
||||
|
||||
m_dht.new_write_key();
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
static bool first = true;
|
||||
if (first)
|
||||
{
|
||||
boost::filesystem::create_directory("libtorrent_logs");
|
||||
}
|
||||
|
||||
std::ofstream st("libtorrent_logs/routing_table_state.txt", std::ios_base::trunc);
|
||||
m_dht.print_state(st);
|
||||
|
||||
// count torrents
|
||||
int torrents = std::distance(m_dht.begin_data(), m_dht.end_data());
|
||||
|
||||
// count peers
|
||||
int peers = 0;
|
||||
std::for_each(m_dht.begin_data(), m_dht.end_data(), count_peers(peers));
|
||||
|
||||
std::ofstream pc("libtorrent_logs/dht_stats.log", std::ios_base::app);
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
using boost::posix_time::to_simple_string;
|
||||
pc << "\n\n ***** starting log at " << to_simple_string(
|
||||
second_clock::universal_time()) << " *****\n\n"
|
||||
<< "minute:active nodes:passive nodes"
|
||||
":ping replies sent:ping queries recvd:ping"
|
||||
":ping replies sent:ping queries recvd:ping"
|
||||
":find_node replies bytes sent:find_node queries bytes recv"
|
||||
":find_node replies bytes sent:find_node queries bytes recv"
|
||||
":get_peers replies sent:get_peers queries recvd:get_peers"
|
||||
":get_peers replies bytes sent:get_peers queries bytes recv"
|
||||
":announce_peer replies sent:announce_peer queries recvd:announce_peer"
|
||||
":announce_peer replies bytes sent:announce_peer queries bytes recv"
|
||||
":error replies sent:error queries recvd:error"
|
||||
":error replies bytes sent:error queries bytes recv"
|
||||
":num torrents:num peers:announces per min"
|
||||
":failed announces per min:total msgs per min"
|
||||
":ut msgs per min:lt msgs per min:mp msgs per min"
|
||||
":gr msgs per min:bytes in per sec:bytes out per sec"
|
||||
":queries out bytes per sec\n\n";
|
||||
}
|
||||
|
||||
int active;
|
||||
int passive;
|
||||
boost::tie(active, passive) = m_dht.size();
|
||||
pc << (m_counter * tick_period)
|
||||
<< "\t" << active
|
||||
<< "\t" << passive;
|
||||
for (int i = 0; i < 5; ++i)
|
||||
pc << "\t" << (m_replies_sent[i] / float(tick_period))
|
||||
<< "\t" << (m_queries_received[i] / float(tick_period))
|
||||
<< "\t" << (m_replies_bytes_sent[i] / float(tick_period*60))
|
||||
<< "\t" << (m_queries_bytes_received[i] / float(tick_period*60));
|
||||
|
||||
pc << "\t" << torrents
|
||||
<< "\t" << peers
|
||||
<< "\t" << m_announces / float(tick_period)
|
||||
<< "\t" << m_failed_announces / float(tick_period)
|
||||
<< "\t" << (m_total_message_input / float(tick_period))
|
||||
<< "\t" << (m_ut_message_input / float(tick_period))
|
||||
<< "\t" << (m_lt_message_input / float(tick_period))
|
||||
<< "\t" << (m_mp_message_input / float(tick_period))
|
||||
<< "\t" << (m_gr_message_input / float(tick_period))
|
||||
<< "\t" << (m_total_in_bytes / float(tick_period*60))
|
||||
<< "\t" << (m_total_out_bytes / float(tick_period*60))
|
||||
<< "\t" << (m_queries_out_bytes / float(tick_period*60))
|
||||
<< std::endl;
|
||||
++m_counter;
|
||||
std::fill_n(m_replies_bytes_sent, 5, 0);
|
||||
std::fill_n(m_queries_bytes_received, 5, 0);
|
||||
std::fill_n(m_replies_sent, 5, 0);
|
||||
std::fill_n(m_queries_received, 5, 0);
|
||||
m_announces = 0;
|
||||
m_failed_announces = 0;
|
||||
m_total_message_input = 0;
|
||||
m_ut_message_input = 0;
|
||||
m_lt_message_input = 0;
|
||||
m_total_in_bytes = 0;
|
||||
m_total_out_bytes = 0;
|
||||
m_queries_out_bytes = 0;
|
||||
#endif
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
assert(false);
|
||||
};
|
||||
|
||||
void dht_tracker::announce(sha1_hash const& ih, int listen_port
|
||||
, boost::function<void(std::vector<tcp::endpoint> const&
|
||||
, sha1_hash const&)> f)
|
||||
{
|
||||
m_dht.announce(ih, listen_port, f);
|
||||
}
|
||||
|
||||
// translate bittorrent kademlia message into the generice kademlia message
|
||||
// used by the library
|
||||
void dht_tracker::on_receive(asio::error const& error, size_t bytes_transferred)
|
||||
try
|
||||
{
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
|
||||
int current_buffer = m_buffer;
|
||||
m_buffer = (m_buffer + 1) & 1;
|
||||
m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0]
|
||||
, m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer]
|
||||
, bind(&dht_tracker::on_receive, this, _1, _2));
|
||||
|
||||
if (error) return;
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
++m_total_message_input;
|
||||
m_total_in_bytes += bytes_transferred;
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
using libtorrent::entry;
|
||||
using libtorrent::bdecode;
|
||||
|
||||
assert(bytes_transferred > 0);
|
||||
|
||||
entry e = bdecode(m_in_buf[current_buffer].begin()
|
||||
, m_in_buf[current_buffer].end());
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << microsec_clock::universal_time()
|
||||
<< " RECEIVED [" << m_remote_endpoint[current_buffer]
|
||||
<< "]:";
|
||||
#endif
|
||||
|
||||
libtorrent::dht::msg m;
|
||||
m.message_id = 0;
|
||||
m.addr = m_remote_endpoint[current_buffer];
|
||||
m.transaction_id = e["t"].string();
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
try
|
||||
{
|
||||
entry const* ver = e.find_key("v");
|
||||
if (!ver) throw std::exception();
|
||||
|
||||
std::string const& client = ver->string();
|
||||
if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "UT"))
|
||||
{
|
||||
++m_ut_message_input;
|
||||
TORRENT_LOG(dht_tracker) << " client: uTorrent";
|
||||
}
|
||||
else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "LT"))
|
||||
{
|
||||
++m_lt_message_input;
|
||||
TORRENT_LOG(dht_tracker) << " client: libtorrent";
|
||||
}
|
||||
else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "MP"))
|
||||
{
|
||||
++m_mp_message_input;
|
||||
TORRENT_LOG(dht_tracker) << " client: MooPolice";
|
||||
}
|
||||
else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "GR"))
|
||||
{
|
||||
++m_gr_message_input;
|
||||
TORRENT_LOG(dht_tracker) << " client: GetRight";
|
||||
}
|
||||
else
|
||||
{
|
||||
TORRENT_LOG(dht_tracker) << " client: generic";
|
||||
}
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
TORRENT_LOG(dht_tracker) << " client: generic";
|
||||
};
|
||||
#endif
|
||||
|
||||
std::string const& msg_type = e["y"].string();
|
||||
|
||||
if (msg_type == "r")
|
||||
{
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " reply: transaction: "
|
||||
<< m.transaction_id;
|
||||
#endif
|
||||
|
||||
m.reply = true;
|
||||
entry const& r = e["r"];
|
||||
std::string const& id = r["id"].string();
|
||||
if (id.size() != 20) throw std::runtime_error("invalid size of id");
|
||||
std::copy(id.begin(), id.end(), m.id.begin());
|
||||
|
||||
if (entry const* n = r.find_key("values"))
|
||||
{
|
||||
m.peers.clear();
|
||||
read_endpoint_list<tcp::endpoint>(n, m.peers);
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size();
|
||||
#endif
|
||||
}
|
||||
|
||||
m.nodes.clear();
|
||||
if (entry const* n = r.find_key("nodes"))
|
||||
{
|
||||
std::string const& nodes = n->string();
|
||||
std::string::const_iterator i = nodes.begin();
|
||||
std::string::const_iterator end = nodes.end();
|
||||
|
||||
while (std::distance(i, end) >= 26)
|
||||
{
|
||||
node_id id;
|
||||
std::copy(i, i + 20, id.begin());
|
||||
i += 20;
|
||||
m.nodes.push_back(libtorrent::dht::node_entry(
|
||||
id, read_v4_endpoint<udp::endpoint>(i)));
|
||||
}
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (entry const* n = r.find_key("nodes2"))
|
||||
{
|
||||
entry::list_type const& contacts = n->list();
|
||||
for (entry::list_type::const_iterator i = contacts.begin()
|
||||
, end(contacts.end()); i != end; ++i)
|
||||
{
|
||||
std::string const& p = i->string();
|
||||
if (p.size() < 6 + 20) continue;
|
||||
std::string::const_iterator in = p.begin();
|
||||
|
||||
node_id id;
|
||||
std::copy(in, in + 20, id.begin());
|
||||
in += 20;
|
||||
if (p.size() == 6 + 20)
|
||||
m.nodes.push_back(libtorrent::dht::node_entry(
|
||||
id, read_v4_endpoint<udp::endpoint>(in)));
|
||||
else if (p.size() == 18 + 20)
|
||||
m.nodes.push_back(libtorrent::dht::node_entry(
|
||||
id, read_v6_endpoint<udp::endpoint>(in)));
|
||||
}
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " nodes2 + nodes: " << m.nodes.size();
|
||||
#endif
|
||||
}
|
||||
|
||||
entry const* token = r.find_key("token");
|
||||
if (token) m.write_token = *token;
|
||||
}
|
||||
else if (msg_type == "q")
|
||||
{
|
||||
m.reply = false;
|
||||
entry const& a = e["a"];
|
||||
std::string const& id = a["id"].string();
|
||||
if (id.size() != 20) throw std::runtime_error("invalid size of id");
|
||||
std::copy(id.begin(), id.end(), m.id.begin());
|
||||
|
||||
std::string request_kind(e["q"].string());
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " query: " << request_kind;
|
||||
#endif
|
||||
|
||||
if (request_kind == "ping")
|
||||
{
|
||||
m.message_id = libtorrent::dht::messages::ping;
|
||||
}
|
||||
else if (request_kind == "find_node")
|
||||
{
|
||||
std::string const& target = a["target"].string();
|
||||
if (target.size() != 20) throw std::runtime_error("invalid size of target id");
|
||||
std::copy(target.begin(), target.end(), m.info_hash.begin());
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " target: "
|
||||
<< boost::lexical_cast<std::string>(m.info_hash);
|
||||
#endif
|
||||
|
||||
m.message_id = libtorrent::dht::messages::find_node;
|
||||
}
|
||||
else if (request_kind == "get_peers")
|
||||
{
|
||||
std::string const& info_hash = a["info_hash"].string();
|
||||
if (info_hash.size() != 20) throw std::runtime_error("invalid size of info-hash");
|
||||
std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin());
|
||||
m.message_id = libtorrent::dht::messages::get_peers;
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " info_hash: "
|
||||
<< boost::lexical_cast<std::string>(m.info_hash);
|
||||
#endif
|
||||
}
|
||||
else if (request_kind == "announce_peer")
|
||||
{
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
++m_announces;
|
||||
#endif
|
||||
std::string const& info_hash = a["info_hash"].string();
|
||||
if (info_hash.size() != 20)
|
||||
throw std::runtime_error("invalid size of info-hash");
|
||||
std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin());
|
||||
m.port = a["port"].integer();
|
||||
m.write_token = a["token"];
|
||||
m.message_id = libtorrent::dht::messages::announce_peer;
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " info_hash: "
|
||||
<< boost::lexical_cast<std::string>(m.info_hash);
|
||||
TORRENT_LOG(dht_tracker) << " port: " << m.port;
|
||||
|
||||
if (!m_dht.verify_token(m))
|
||||
++m_failed_announces;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED REQUEST *** : "
|
||||
<< request_kind;
|
||||
#endif
|
||||
throw std::runtime_error("unsupported request: " + request_kind);
|
||||
}
|
||||
}
|
||||
else if (msg_type == "e")
|
||||
{
|
||||
entry::list_type const& list = e["e"].list();
|
||||
m.message_id = messages::error;
|
||||
m.error_msg = list.back().string();
|
||||
m.error_code = list.front().integer();
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " error: " << m.error_code << " "
|
||||
<< m.error_msg;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED MESSAGE TYPE *** : "
|
||||
<< msg_type;
|
||||
#endif
|
||||
throw std::runtime_error("unsupported message type: " + msg_type);
|
||||
}
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
if (!m.reply)
|
||||
{
|
||||
++m_queries_received[m.message_id];
|
||||
m_queries_bytes_received[m.message_id] += int(bytes_transferred);
|
||||
}
|
||||
TORRENT_LOG(dht_tracker) << e;
|
||||
#endif
|
||||
|
||||
m_dht.incoming(m);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << "invalid incoming packet: "
|
||||
<< e.what();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
assert(false);
|
||||
};
|
||||
|
||||
entry dht_tracker::state() const
|
||||
{
|
||||
entry ret(entry::dictionary_t);
|
||||
{
|
||||
entry nodes(entry::list_t);
|
||||
for (node_impl::iterator i(m_dht.begin())
|
||||
, end(m_dht.end()); i != end; ++i)
|
||||
{
|
||||
std::string node;
|
||||
std::back_insert_iterator<std::string> out(node);
|
||||
write_endpoint(i->addr, out);
|
||||
nodes.list().push_back(entry(node));
|
||||
}
|
||||
bucket_t cache;
|
||||
m_dht.replacement_cache(cache);
|
||||
for (bucket_t::iterator i(cache.begin())
|
||||
, end(cache.end()); i != end; ++i)
|
||||
{
|
||||
std::string node;
|
||||
std::back_insert_iterator<std::string> out(node);
|
||||
write_endpoint(i->addr, out);
|
||||
nodes.list().push_back(entry(node));
|
||||
}
|
||||
if (!nodes.list().empty())
|
||||
ret["nodes"] = nodes;
|
||||
}
|
||||
|
||||
ret["node-id"] = boost::lexical_cast<std::string>(m_dht.nid());
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dht_tracker::add_node(udp::endpoint node)
|
||||
{
|
||||
m_dht.add_node(node);
|
||||
}
|
||||
|
||||
void dht_tracker::add_node(std::pair<std::string, int> const& node)
|
||||
{
|
||||
udp::resolver::query q(node.first, lexical_cast<std::string>(node.second));
|
||||
m_host_resolver.async_resolve(q, bind(&dht_tracker::on_name_lookup
|
||||
, this, _1, _2));
|
||||
}
|
||||
|
||||
void dht_tracker::on_name_lookup(asio::error const& e
|
||||
, udp::resolver::iterator host) try
|
||||
{
|
||||
if (e || host == udp::resolver::iterator()) return;
|
||||
add_node(host->endpoint());
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
assert(false);
|
||||
};
|
||||
|
||||
void dht_tracker::add_router_node(std::pair<std::string, int> const& node)
|
||||
{
|
||||
udp::resolver::query q(node.first, lexical_cast<std::string>(node.second));
|
||||
m_host_resolver.async_resolve(q, bind(&dht_tracker::on_router_name_lookup
|
||||
, this, _1, _2));
|
||||
}
|
||||
|
||||
void dht_tracker::on_router_name_lookup(asio::error const& e
|
||||
, udp::resolver::iterator host) try
|
||||
{
|
||||
if (e || host == udp::resolver::iterator()) return;
|
||||
m_dht.add_router_node(host->endpoint());
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
assert(false);
|
||||
};
|
||||
|
||||
void dht_tracker::on_bootstrap()
|
||||
{}
|
||||
|
||||
void dht_tracker::send_packet(msg const& m)
|
||||
{
|
||||
using libtorrent::bencode;
|
||||
using libtorrent::entry;
|
||||
entry e(entry::dictionary_t);
|
||||
e["t"] = m.transaction_id;
|
||||
std::string version_str("LT ");
|
||||
std::string::iterator i = version_str.begin() + 2;
|
||||
detail::write_uint8(LIBTORRENT_VERSION_MAJOR, i);
|
||||
detail::write_uint8(LIBTORRENT_VERSION_MINOR, i);
|
||||
e["v"] = version_str;
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << microsec_clock::universal_time()
|
||||
<< " SENDING [" << m.addr << "]:";
|
||||
TORRENT_LOG(dht_tracker) << " transaction: " << m.transaction_id;
|
||||
// e.print(std::cerr);
|
||||
#endif
|
||||
|
||||
if (m.message_id == messages::error)
|
||||
{
|
||||
assert(m.reply);
|
||||
e["y"] = "e";
|
||||
entry error_list(entry::list_t);
|
||||
error_list.list().push_back(entry(m.error_code));
|
||||
error_list.list().push_back(entry(m.error_msg));
|
||||
e["e"] = error_list;
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " error: " << m.error_code << " "
|
||||
<< m.error_msg;
|
||||
#endif
|
||||
}
|
||||
else if (m.reply)
|
||||
{
|
||||
e["y"] = "r";
|
||||
e["r"] = entry(entry::dictionary_t);
|
||||
entry& r = e["r"];
|
||||
r["id"] = std::string(m.id.begin(), m.id.end());
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " reply: "
|
||||
<< messages::ids[m.message_id];
|
||||
#endif
|
||||
|
||||
if (m.write_token.type() != entry::undefined_t)
|
||||
r["token"] = m.write_token;
|
||||
|
||||
switch (m.message_id)
|
||||
{
|
||||
case messages::ping:
|
||||
break;
|
||||
case messages::find_node:
|
||||
{
|
||||
bool ipv6_nodes = false;
|
||||
r["nodes"] = entry(entry::string_t);
|
||||
entry& n = r["nodes"];
|
||||
std::back_insert_iterator<std::string> out(n.string());
|
||||
for (msg::nodes_t::const_iterator i = m.nodes.begin()
|
||||
, end(m.nodes.end()); i != end; ++i)
|
||||
{
|
||||
if (!i->addr.address().is_v4())
|
||||
{
|
||||
ipv6_nodes = true;
|
||||
continue;
|
||||
}
|
||||
std::copy(i->id.begin(), i->id.end(), out);
|
||||
write_endpoint(i->addr, out);
|
||||
}
|
||||
|
||||
if (ipv6_nodes)
|
||||
{
|
||||
r["nodes2"] = entry(entry::list_t);
|
||||
entry& p = r["nodes2"];
|
||||
std::string endpoint;
|
||||
endpoint.resize(6);
|
||||
for (msg::nodes_t::const_iterator i = m.nodes.begin()
|
||||
, end(m.nodes.end()); i != end; ++i)
|
||||
{
|
||||
std::string::iterator out = endpoint.begin();
|
||||
std::copy(i->id.begin(), i->id.end(), out);
|
||||
write_endpoint(i->addr, out);
|
||||
p.list().push_back(entry(endpoint));
|
||||
}
|
||||
}
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case messages::get_peers:
|
||||
{
|
||||
if (m.peers.empty())
|
||||
{
|
||||
r["nodes"] = entry(entry::string_t);
|
||||
entry& n = r["nodes"];
|
||||
std::back_insert_iterator<std::string> out(n.string());
|
||||
for (msg::nodes_t::const_iterator i = m.nodes.begin()
|
||||
, end(m.nodes.end()); i != end; ++i)
|
||||
{
|
||||
if (!i->addr.address().is_v4()) continue;
|
||||
std::copy(i->id.begin(), i->id.end(), out);
|
||||
write_endpoint(i->addr, out);
|
||||
}
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
r["values"] = entry(entry::list_t);
|
||||
entry& p = r["values"];
|
||||
std::string endpoint;
|
||||
endpoint.resize(6);
|
||||
for (msg::peers_t::const_iterator i = m.peers.begin()
|
||||
, end(m.peers.end()); i != end; ++i)
|
||||
{
|
||||
std::string::iterator out = endpoint.begin();
|
||||
write_endpoint(*i, out);
|
||||
p.list().push_back(entry(endpoint));
|
||||
}
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size();
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case messages::announce_peer:
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
e["y"] = "q";
|
||||
e["a"] = entry(entry::dictionary_t);
|
||||
entry& a = e["a"];
|
||||
a["id"] = std::string(m.id.begin(), m.id.end());
|
||||
|
||||
if (m.write_token.type() != entry::undefined_t)
|
||||
a["token"] = m.write_token;
|
||||
assert(m.message_id <= messages::error);
|
||||
e["q"] = messages::ids[m.message_id];
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " query: "
|
||||
<< messages::ids[m.message_id];
|
||||
#endif
|
||||
|
||||
switch (m.message_id)
|
||||
{
|
||||
case messages::find_node:
|
||||
{
|
||||
a["target"] = std::string(m.info_hash.begin(), m.info_hash.end());
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " target: "
|
||||
<< boost::lexical_cast<std::string>(m.info_hash);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case messages::get_peers:
|
||||
{
|
||||
a["info_hash"] = std::string(m.info_hash.begin(), m.info_hash.end());
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " info_hash: "
|
||||
<< boost::lexical_cast<std::string>(m.info_hash);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case messages::announce_peer:
|
||||
a["port"] = m_settings.service_port;
|
||||
a["info_hash"] = boost::lexical_cast<std::string>(m.info_hash);
|
||||
a["token"] = m.write_token;
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << " port: "
|
||||
<< m_settings.service_port
|
||||
<< " info_hash: " << boost::lexical_cast<std::string>(m.info_hash);
|
||||
#endif
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_send_buf.clear();
|
||||
bencode(std::back_inserter(m_send_buf), e);
|
||||
m_socket.send_to(asio::buffer(&m_send_buf[0]
|
||||
, (int)m_send_buf.size()), m.addr);
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
m_total_out_bytes += m_send_buf.size();
|
||||
|
||||
if (m.reply)
|
||||
{
|
||||
++m_replies_sent[m.message_id];
|
||||
m_replies_bytes_sent[m.message_id] += int(m_send_buf.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_queries_out_bytes += m_send_buf.size();
|
||||
}
|
||||
TORRENT_LOG(dht_tracker) << e;
|
||||
#endif
|
||||
|
||||
if (!m.piggy_backed_ping) return;
|
||||
|
||||
msg pm;
|
||||
pm.reply = false;
|
||||
pm.piggy_backed_ping = false;
|
||||
pm.message_id = messages::ping;
|
||||
pm.transaction_id = m.ping_transaction_id;
|
||||
pm.id = m.id;
|
||||
pm.addr = m.addr;
|
||||
|
||||
send_packet(pm);
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
156
cpp/kademlia/find_data.cpp
Normal file
156
cpp/kademlia/find_data.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <libtorrent/kademlia/find_data.hpp>
|
||||
#include <libtorrent/kademlia/routing_table.hpp>
|
||||
#include <libtorrent/kademlia/rpc_manager.hpp>
|
||||
#include <libtorrent/io.hpp>
|
||||
|
||||
namespace libtorrent { namespace dht
|
||||
{
|
||||
|
||||
typedef boost::shared_ptr<observer> observer_ptr;
|
||||
|
||||
class find_data_observer : public observer
|
||||
{
|
||||
public:
|
||||
find_data_observer(
|
||||
boost::intrusive_ptr<find_data> const& algorithm
|
||||
, node_id self
|
||||
, node_id target)
|
||||
: m_algorithm(algorithm)
|
||||
, m_target(target)
|
||||
, m_self(self)
|
||||
{}
|
||||
|
||||
void send(msg& m)
|
||||
{
|
||||
m.reply = false;
|
||||
m.message_id = messages::get_peers;
|
||||
m.info_hash = m_target;
|
||||
}
|
||||
|
||||
void timeout();
|
||||
void reply(msg const&);
|
||||
|
||||
private:
|
||||
boost::intrusive_ptr<find_data> m_algorithm;
|
||||
node_id const m_target;
|
||||
node_id const m_self;
|
||||
};
|
||||
|
||||
void find_data_observer::reply(msg const& m)
|
||||
{
|
||||
if (!m.peers.empty())
|
||||
{
|
||||
m_algorithm->got_data(&m);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (msg::nodes_t::const_iterator i = m.nodes.begin()
|
||||
, end(m.nodes.end()); i != end; ++i)
|
||||
{
|
||||
m_algorithm->traverse(i->id, i->addr);
|
||||
}
|
||||
}
|
||||
m_algorithm->finished(m_self);
|
||||
}
|
||||
|
||||
void find_data_observer::timeout()
|
||||
{
|
||||
m_algorithm->failed(m_self);
|
||||
}
|
||||
|
||||
|
||||
find_data::find_data(
|
||||
node_id target
|
||||
, int branch_factor
|
||||
, int max_results
|
||||
, routing_table& table
|
||||
, rpc_manager& rpc
|
||||
, done_callback const& callback
|
||||
)
|
||||
: traversal_algorithm(
|
||||
target
|
||||
, branch_factor
|
||||
, max_results
|
||||
, table
|
||||
, rpc
|
||||
, table.begin()
|
||||
, table.end()
|
||||
)
|
||||
, m_done_callback(callback)
|
||||
, m_done(false)
|
||||
{
|
||||
boost::intrusive_ptr<find_data> self(this);
|
||||
add_requests();
|
||||
}
|
||||
|
||||
void find_data::invoke(node_id const& id, asio::ip::udp::endpoint addr)
|
||||
{
|
||||
if (m_done)
|
||||
{
|
||||
m_invoke_count = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
observer_ptr p(new find_data_observer(this, id, m_target));
|
||||
m_rpc.invoke(messages::get_peers, addr, p);
|
||||
}
|
||||
|
||||
void find_data::got_data(msg const* m)
|
||||
{
|
||||
m_done = true;
|
||||
m_done_callback(m);
|
||||
}
|
||||
|
||||
void find_data::done()
|
||||
{
|
||||
if (m_invoke_count != 0) return;
|
||||
if (!m_done) m_done_callback(0);
|
||||
}
|
||||
|
||||
void find_data::initiate(
|
||||
node_id target
|
||||
, int branch_factor
|
||||
, int max_results
|
||||
, routing_table& table
|
||||
, rpc_manager& rpc
|
||||
, done_callback const& callback
|
||||
)
|
||||
{
|
||||
std::cerr << "find_data::initiate, key: " << target << "\n";
|
||||
new find_data(target, branch_factor, max_results, table, rpc, callback);
|
||||
}
|
||||
|
||||
} } // namespace libtorrent::dht
|
||||
|
||||
539
cpp/kademlia/node.cpp
Normal file
539
cpp/kademlia/node.cpp
Normal file
@ -0,0 +1,539 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2006, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <utility>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/iterator_adaptors.hpp>
|
||||
|
||||
#include "libtorrent/io.hpp"
|
||||
#include "libtorrent/hasher.hpp"
|
||||
#include "libtorrent/random_sample.hpp"
|
||||
#include "libtorrent/kademlia/node_id.hpp"
|
||||
#include "libtorrent/kademlia/rpc_manager.hpp"
|
||||
#include "libtorrent/kademlia/packet_iterator.hpp"
|
||||
#include "libtorrent/kademlia/routing_table.hpp"
|
||||
#include "libtorrent/kademlia/node.hpp"
|
||||
|
||||
#include "libtorrent/kademlia/refresh.hpp"
|
||||
#include "libtorrent/kademlia/closest_nodes.hpp"
|
||||
#include "libtorrent/kademlia/find_data.hpp"
|
||||
|
||||
using boost::bind;
|
||||
using boost::posix_time::second_clock;
|
||||
using boost::posix_time::seconds;
|
||||
using boost::posix_time::minutes;
|
||||
using boost::posix_time::ptime;
|
||||
using boost::posix_time::time_duration;
|
||||
|
||||
namespace libtorrent { namespace dht
|
||||
{
|
||||
|
||||
#ifdef _MSC_VER
|
||||
namespace
|
||||
{
|
||||
char rand() { return (char)std::rand(); }
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef boost::shared_ptr<observer> observer_ptr;
|
||||
|
||||
// TODO: configurable?
|
||||
enum { announce_interval = 30 };
|
||||
|
||||
using asio::ip::udp;
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_DEFINE_LOG(node)
|
||||
#endif
|
||||
|
||||
node_id generate_id()
|
||||
{
|
||||
char random[20];
|
||||
std::srand(std::time(0));
|
||||
#ifdef _MSC_VER
|
||||
std::generate(random, random + 20, &rand);
|
||||
#else
|
||||
std::generate(random, random + 20, &std::rand);
|
||||
#endif
|
||||
|
||||
hasher h;
|
||||
h.update(random, 20);
|
||||
return h.final();
|
||||
}
|
||||
|
||||
// remove peers that have timed out
|
||||
void purge_peers(std::set<peer_entry>& peers)
|
||||
{
|
||||
for (std::set<peer_entry>::iterator i = peers.begin()
|
||||
, end(peers.end()); i != end;)
|
||||
{
|
||||
// the peer has timed out
|
||||
if (i->added + minutes(int(announce_interval * 1.5f)) < second_clock::universal_time())
|
||||
{
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(node) << "peer timed out at: " << i->addr.address();
|
||||
#endif
|
||||
peers.erase(i++);
|
||||
}
|
||||
else
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void nop() {}
|
||||
|
||||
node_impl::node_impl(boost::function<void(msg const&)> const& f
|
||||
, dht_settings const& settings, boost::optional<node_id> node_id)
|
||||
: m_settings(settings)
|
||||
, m_id(node_id ? *node_id : generate_id())
|
||||
, m_table(m_id, 8, settings)
|
||||
, m_rpc(bind(&node_impl::incoming_request, this, _1)
|
||||
, m_id, m_table, f)
|
||||
, m_last_tracker_tick(boost::posix_time::second_clock::universal_time())
|
||||
{
|
||||
m_secret[0] = std::rand();
|
||||
m_secret[1] = std::rand();
|
||||
}
|
||||
|
||||
bool node_impl::verify_token(msg const& m)
|
||||
{
|
||||
if (m.write_token.type() != entry::string_t)
|
||||
return false;
|
||||
std::string const& token = m.write_token.string();
|
||||
if (token.length() != 4) return false;
|
||||
|
||||
hasher h1;
|
||||
std::string address = m.addr.address().to_string();
|
||||
h1.update(&address[0], address.length());
|
||||
h1.update((char*)&m_secret[0], sizeof(m_secret[0]));
|
||||
h1.update((char*)&m.info_hash[0], sha1_hash::size);
|
||||
|
||||
sha1_hash h = h1.final();
|
||||
if (std::equal(token.begin(), token.end(), (signed char*)&h[0]))
|
||||
return true;
|
||||
|
||||
hasher h2;
|
||||
h2.update(&address[0], address.length());
|
||||
h2.update((char*)&m_secret[1], sizeof(m_secret[1]));
|
||||
h = h2.final();
|
||||
if (std::equal(token.begin(), token.end(), (signed char*)&h[0]))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
entry node_impl::generate_token(msg const& m)
|
||||
{
|
||||
std::string token;
|
||||
token.resize(4);
|
||||
hasher h;
|
||||
std::string address = m.addr.address().to_string();
|
||||
h.update(&address[0], address.length());
|
||||
h.update((char*)&m_secret[0], sizeof(m_secret[0]));
|
||||
h.update((char*)&m.info_hash[0], sha1_hash::size);
|
||||
|
||||
sha1_hash hash = h.final();
|
||||
std::copy(hash.begin(), hash.begin() + 4, (signed char*)&token[0]);
|
||||
return entry(token);
|
||||
}
|
||||
|
||||
void node_impl::refresh(node_id const& id
|
||||
, boost::function0<void> f)
|
||||
{
|
||||
// use the 'bucket size' closest nodes
|
||||
// to start the refresh with
|
||||
std::vector<node_entry> start;
|
||||
start.reserve(m_table.bucket_size());
|
||||
m_table.find_node(id, start, false);
|
||||
refresh::initiate(id, m_settings.search_branching, 10, m_table.bucket_size()
|
||||
, m_table, start.begin(), start.end(), m_rpc, f);
|
||||
}
|
||||
|
||||
void node_impl::bootstrap(std::vector<udp::endpoint> const& nodes
|
||||
, boost::function0<void> f)
|
||||
{
|
||||
std::vector<node_entry> start;
|
||||
start.reserve(nodes.size());
|
||||
std::copy(nodes.begin(), nodes.end(), std::back_inserter(start));
|
||||
refresh::initiate(m_id, m_settings.search_branching, 10, m_table.bucket_size()
|
||||
, m_table, start.begin(), start.end(), m_rpc, f);
|
||||
}
|
||||
|
||||
void node_impl::refresh()
|
||||
{
|
||||
std::vector<node_entry> start;
|
||||
start.reserve(m_table.size().get<0>());
|
||||
std::copy(m_table.begin(), m_table.end(), std::back_inserter(start));
|
||||
|
||||
refresh::initiate(m_id, m_settings.search_branching, 10, m_table.bucket_size()
|
||||
, m_table, start.begin(), start.end(), m_rpc, bind(&nop));
|
||||
}
|
||||
|
||||
int node_impl::bucket_size(int bucket)
|
||||
{
|
||||
return m_table.bucket_size(bucket);
|
||||
}
|
||||
|
||||
void node_impl::new_write_key()
|
||||
{
|
||||
m_secret[1] = m_secret[0];
|
||||
m_secret[0] = std::rand();
|
||||
}
|
||||
|
||||
void node_impl::refresh_bucket(int bucket)
|
||||
{
|
||||
assert(bucket >= 0 && bucket < 160);
|
||||
|
||||
// generate a random node_id within the given bucket
|
||||
node_id target = generate_id();
|
||||
int num_bits = 160 - bucket;
|
||||
node_id mask(0);
|
||||
for (int i = 0; i < num_bits; ++i)
|
||||
{
|
||||
int byte = i / 8;
|
||||
mask[byte] |= 0x80 >> (i % 8);
|
||||
}
|
||||
|
||||
node_id root = m_id;
|
||||
root &= mask;
|
||||
target &= ~mask;
|
||||
target |= root;
|
||||
|
||||
// make sure this is in another subtree than m_id
|
||||
// clear the (num_bits - 1) bit and then set it to the
|
||||
// inverse of m_id's corresponding bit.
|
||||
target[(num_bits - 1) / 8] &= ~(0x80 >> ((num_bits - 1) % 8));
|
||||
target[(num_bits - 1) / 8] |=
|
||||
(~(m_id[(num_bits - 1) / 8])) & (0x80 >> ((num_bits - 1) % 8));
|
||||
|
||||
assert(distance_exp(m_id, target) == bucket);
|
||||
|
||||
std::vector<node_entry> start;
|
||||
start.reserve(m_table.bucket_size());
|
||||
m_table.find_node(target, start, false, m_table.bucket_size());
|
||||
|
||||
refresh::initiate(target, m_settings.search_branching, 10, m_table.bucket_size()
|
||||
, m_table, start.begin(), start.end(), m_rpc, bind(&nop));
|
||||
m_table.touch_bucket(bucket);
|
||||
}
|
||||
|
||||
|
||||
void node_impl::incoming(msg const& m)
|
||||
{
|
||||
if (m_rpc.incoming(m))
|
||||
{
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class announce_observer : public observer
|
||||
{
|
||||
public:
|
||||
announce_observer(sha1_hash const& info_hash, int listen_port
|
||||
, entry const& write_token)
|
||||
: m_info_hash(info_hash)
|
||||
, m_listen_port(listen_port)
|
||||
, m_token(write_token)
|
||||
{}
|
||||
|
||||
void send(msg& m)
|
||||
{
|
||||
m.port = m_listen_port;
|
||||
m.info_hash = m_info_hash;
|
||||
m.write_token = m_token;
|
||||
}
|
||||
|
||||
void timeout() {}
|
||||
void reply(msg const&) {}
|
||||
|
||||
private:
|
||||
sha1_hash m_info_hash;
|
||||
int m_listen_port;
|
||||
entry m_token;
|
||||
};
|
||||
|
||||
class get_peers_observer : public observer
|
||||
{
|
||||
public:
|
||||
get_peers_observer(sha1_hash const& info_hash, int listen_port
|
||||
, rpc_manager& rpc
|
||||
, boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> f)
|
||||
: m_info_hash(info_hash)
|
||||
, m_listen_port(listen_port)
|
||||
, m_rpc(rpc)
|
||||
, m_fun(f)
|
||||
{}
|
||||
|
||||
void send(msg& m)
|
||||
{
|
||||
m.port = m_listen_port;
|
||||
m.info_hash = m_info_hash;
|
||||
}
|
||||
|
||||
void timeout() {}
|
||||
void reply(msg const& r)
|
||||
{
|
||||
m_rpc.invoke(messages::announce_peer, r.addr
|
||||
, boost::shared_ptr<observer>(
|
||||
new announce_observer(m_info_hash, m_listen_port, r.write_token)));
|
||||
m_fun(r.peers, m_info_hash);
|
||||
}
|
||||
|
||||
private:
|
||||
sha1_hash m_info_hash;
|
||||
int m_listen_port;
|
||||
rpc_manager& m_rpc;
|
||||
boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> m_fun;
|
||||
};
|
||||
|
||||
|
||||
void announce_fun(std::vector<node_entry> const& v, rpc_manager& rpc
|
||||
, int listen_port, sha1_hash const& ih
|
||||
, boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> f)
|
||||
{
|
||||
bool nodes = false;
|
||||
// only store on the first k nodes
|
||||
for (std::vector<node_entry>::const_iterator i = v.begin()
|
||||
, end(v.end()); i != end; ++i)
|
||||
{
|
||||
rpc.invoke(messages::get_peers, i->addr, boost::shared_ptr<observer>(
|
||||
new get_peers_observer(ih, listen_port, rpc, f)));
|
||||
nodes = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct dummy_observer : observer
|
||||
{
|
||||
virtual void reply(msg const&) {}
|
||||
virtual void timeout() {}
|
||||
virtual void send(msg&) {}
|
||||
};
|
||||
}
|
||||
|
||||
void node_impl::add_router_node(udp::endpoint router)
|
||||
{
|
||||
m_table.add_router_node(router);
|
||||
}
|
||||
|
||||
void node_impl::add_node(udp::endpoint node)
|
||||
{
|
||||
// ping the node, and if we get a reply, it
|
||||
// will be added to the routing table
|
||||
observer_ptr p(new dummy_observer());
|
||||
m_rpc.invoke(messages::ping, node, p);
|
||||
}
|
||||
|
||||
void node_impl::announce(sha1_hash const& info_hash, int listen_port
|
||||
, boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> f)
|
||||
{
|
||||
// search for nodes with ids close to id, and then invoke the
|
||||
// get_peers and then announce_peer rpc on them.
|
||||
closest_nodes::initiate(info_hash, m_settings.search_branching
|
||||
, m_table.bucket_size(), m_table, m_rpc
|
||||
, boost::bind(&announce_fun, _1, boost::ref(m_rpc), listen_port
|
||||
, info_hash, f));
|
||||
}
|
||||
|
||||
time_duration node_impl::refresh_timeout()
|
||||
{
|
||||
int refresh = -1;
|
||||
ptime now = second_clock::universal_time();
|
||||
ptime next = now + minutes(15);
|
||||
for (int i = 0; i < 160; ++i)
|
||||
{
|
||||
ptime r = m_table.next_refresh(i);
|
||||
if (r <= now)
|
||||
{
|
||||
if (refresh == -1) refresh = i;
|
||||
}
|
||||
else if (r < next)
|
||||
{
|
||||
next = r;
|
||||
}
|
||||
}
|
||||
if (refresh != -1)
|
||||
{
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(node) << "refreshing bucket: " << refresh;
|
||||
#endif
|
||||
refresh_bucket(refresh);
|
||||
}
|
||||
if (next < now + seconds(5)) return seconds(5);
|
||||
return next - now;
|
||||
}
|
||||
|
||||
time_duration node_impl::connection_timeout()
|
||||
{
|
||||
time_duration d = m_rpc.tick();
|
||||
|
||||
ptime now(second_clock::universal_time());
|
||||
if (now - m_last_tracker_tick < minutes(10)) return d;
|
||||
m_last_tracker_tick = now;
|
||||
|
||||
// look through all peers and see if any have timed out
|
||||
for (data_iterator i = begin_data(), end(end_data()); i != end;)
|
||||
{
|
||||
torrent_entry& t = i->second;
|
||||
node_id const& key = i->first;
|
||||
++i;
|
||||
purge_peers(t.peers);
|
||||
|
||||
// if there are no more peers, remove the entry altogether
|
||||
if (t.peers.empty())
|
||||
{
|
||||
table_t::iterator i = m_map.find(key);
|
||||
if (i != m_map.end()) m_map.erase(i);
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
void node_impl::on_announce(msg const& m, msg& reply)
|
||||
{
|
||||
if (!verify_token(m))
|
||||
{
|
||||
reply.message_id = messages::error;
|
||||
reply.error_code = 203;
|
||||
reply.error_msg = "Incorrect write token in announce_peer message";
|
||||
return;
|
||||
}
|
||||
|
||||
// the token was correct. That means this
|
||||
// node is not spoofing its address. So, let
|
||||
// the table get a chance to add it.
|
||||
m_table.node_seen(m.id, m.addr);
|
||||
|
||||
torrent_entry& v = m_map[m.info_hash];
|
||||
peer_entry e;
|
||||
e.addr = tcp::endpoint(m.addr.address(), m.addr.port());
|
||||
e.added = second_clock::universal_time();
|
||||
std::set<peer_entry>::iterator i = v.peers.find(e);
|
||||
if (i != v.peers.end()) v.peers.erase(i++);
|
||||
v.peers.insert(i, e);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
tcp::endpoint get_endpoint(peer_entry const& p)
|
||||
{
|
||||
return p.addr;
|
||||
}
|
||||
}
|
||||
|
||||
bool node_impl::on_find(msg const& m, std::vector<tcp::endpoint>& peers) const
|
||||
{
|
||||
table_t::const_iterator i = m_map.find(m.info_hash);
|
||||
if (i == m_map.end()) return false;
|
||||
|
||||
torrent_entry const& v = i->second;
|
||||
|
||||
int num = (std::min)((int)v.peers.size(), m_settings.max_peers_reply);
|
||||
peers.clear();
|
||||
peers.reserve(num);
|
||||
random_sample_n(boost::make_transform_iterator(v.peers.begin(), &get_endpoint)
|
||||
, boost::make_transform_iterator(v.peers.end(), &get_endpoint)
|
||||
, std::back_inserter(peers), num);
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
for (std::vector<tcp::endpoint>::iterator i = peers.begin()
|
||||
, end(peers.end()); i != end; ++i)
|
||||
{
|
||||
TORRENT_LOG(node) << " " << *i;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void node_impl::incoming_request(msg const& m)
|
||||
{
|
||||
msg reply;
|
||||
switch (m.message_id)
|
||||
{
|
||||
case messages::ping:
|
||||
break;
|
||||
case messages::get_peers:
|
||||
{
|
||||
reply.info_hash = m.info_hash;
|
||||
reply.write_token = generate_token(m);
|
||||
|
||||
if (!on_find(m, reply.peers))
|
||||
{
|
||||
// we don't have any peers for this info_hash,
|
||||
// return nodes instead
|
||||
m_table.find_node(m.info_hash, reply.nodes, false);
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
for (std::vector<node_entry>::iterator i = reply.nodes.begin()
|
||||
, end(reply.nodes.end()); i != end; ++i)
|
||||
{
|
||||
TORRENT_LOG(node) << " " << i->id << " " << i->addr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
break;
|
||||
case messages::find_node:
|
||||
{
|
||||
reply.info_hash = m.info_hash;
|
||||
|
||||
m_table.find_node(m.info_hash, reply.nodes, false);
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
for (std::vector<node_entry>::iterator i = reply.nodes.begin()
|
||||
, end(reply.nodes.end()); i != end; ++i)
|
||||
{
|
||||
TORRENT_LOG(node) << " " << i->id << " " << i->addr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case messages::announce_peer:
|
||||
{
|
||||
on_announce(m, reply);
|
||||
}
|
||||
break;
|
||||
};
|
||||
|
||||
if (m_table.need_node(m.id))
|
||||
m_rpc.reply_with_ping(reply, m);
|
||||
else
|
||||
m_rpc.reply(reply, m);
|
||||
}
|
||||
|
||||
|
||||
} } // namespace libtorrent::dht
|
||||
97
cpp/kademlia/node_id.cpp
Normal file
97
cpp/kademlia/node_id.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2006, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <cassert>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include "libtorrent/kademlia/node_id.hpp"
|
||||
|
||||
using boost::bind;
|
||||
|
||||
namespace libtorrent { namespace dht
|
||||
{
|
||||
|
||||
// returns the distance between the two nodes
|
||||
// using the kademlia XOR-metric
|
||||
node_id distance(node_id const& n1, node_id const& n2)
|
||||
{
|
||||
node_id ret;
|
||||
node_id::iterator k = ret.begin();
|
||||
for (node_id::const_iterator i = n1.begin(), j = n2.begin()
|
||||
, end(n1.end()); i != end; ++i, ++j, ++k)
|
||||
{
|
||||
*k = *i ^ *j;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// returns true if: distance(n1, ref) < distance(n2, ref)
|
||||
bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref)
|
||||
{
|
||||
for (node_id::const_iterator i = n1.begin(), j = n2.begin()
|
||||
, k = ref.begin(), end(n1.end()); i != end; ++i, ++j, ++k)
|
||||
{
|
||||
boost::uint8_t lhs = (*i ^ *k);
|
||||
boost::uint8_t rhs = (*j ^ *k);
|
||||
if (lhs < rhs) return true;
|
||||
if (lhs > rhs) return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// returns n in: 2^n <= distance(n1, n2) < 2^(n+1)
|
||||
// useful for finding out which bucket a node belongs to
|
||||
int distance_exp(node_id const& n1, node_id const& n2)
|
||||
{
|
||||
int byte = node_id::size - 1;
|
||||
for (node_id::const_iterator i = n1.begin(), j = n2.begin()
|
||||
, end(n1.end()); i != end; ++i, ++j, --byte)
|
||||
{
|
||||
assert(byte >= 0);
|
||||
boost::uint8_t t = *i ^ *j;
|
||||
if (t == 0) continue;
|
||||
// we have found the first non-zero byte
|
||||
// return the bit-number of the first bit
|
||||
// that differs
|
||||
int bit = byte * 8;
|
||||
for (int b = 7; b > 0; --b)
|
||||
if (t >= (1 << b)) return bit + b;
|
||||
return bit;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} } // namespace libtorrent::dht
|
||||
|
||||
190
cpp/kademlia/refresh.cpp
Normal file
190
cpp/kademlia/refresh.cpp
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <libtorrent/kademlia/refresh.hpp>
|
||||
#include <libtorrent/kademlia/routing_table.hpp>
|
||||
#include <libtorrent/kademlia/rpc_manager.hpp>
|
||||
#include <libtorrent/kademlia/logging.hpp>
|
||||
|
||||
#include <libtorrent/io.hpp>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
using boost::bind;
|
||||
|
||||
namespace libtorrent { namespace dht
|
||||
{
|
||||
|
||||
using asio::ip::udp;
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_DEFINE_LOG(refresh)
|
||||
#endif
|
||||
|
||||
typedef boost::shared_ptr<observer> observer_ptr;
|
||||
|
||||
class refresh_observer : public observer
|
||||
{
|
||||
public:
|
||||
refresh_observer(
|
||||
boost::intrusive_ptr<refresh> const& algorithm
|
||||
, node_id self
|
||||
, node_id target
|
||||
)
|
||||
: m_target(target)
|
||||
, m_self(self)
|
||||
, m_algorithm(algorithm)
|
||||
{}
|
||||
|
||||
void send(msg& m)
|
||||
{
|
||||
m.info_hash = m_target;
|
||||
}
|
||||
|
||||
void timeout();
|
||||
void reply(msg const& m);
|
||||
|
||||
private:
|
||||
node_id const m_target;
|
||||
node_id const m_self;
|
||||
boost::intrusive_ptr<refresh> m_algorithm;
|
||||
};
|
||||
|
||||
void refresh_observer::reply(msg const& in)
|
||||
{
|
||||
if (!in.nodes.empty())
|
||||
{
|
||||
for (msg::nodes_t::const_iterator i = in.nodes.begin()
|
||||
, end(in.nodes.end()); i != end; ++i)
|
||||
{
|
||||
m_algorithm->traverse(i->id, i->addr);
|
||||
}
|
||||
}
|
||||
m_algorithm->finished(m_self);
|
||||
}
|
||||
|
||||
void refresh_observer::timeout()
|
||||
{
|
||||
m_algorithm->failed(m_self);
|
||||
}
|
||||
|
||||
class ping_observer : public observer
|
||||
{
|
||||
public:
|
||||
ping_observer(
|
||||
boost::intrusive_ptr<refresh> const& algorithm
|
||||
, node_id self
|
||||
)
|
||||
: m_self(self)
|
||||
, m_algorithm(algorithm)
|
||||
{}
|
||||
|
||||
void send(msg& p) {}
|
||||
void timeout();
|
||||
void reply(msg const& m);
|
||||
|
||||
private:
|
||||
node_id const m_self;
|
||||
boost::intrusive_ptr<refresh> m_algorithm;
|
||||
};
|
||||
|
||||
void ping_observer::reply(msg const& m)
|
||||
{
|
||||
m_algorithm->ping_reply(m_self);
|
||||
}
|
||||
|
||||
void ping_observer::timeout()
|
||||
{
|
||||
m_algorithm->ping_timeout(m_self);
|
||||
}
|
||||
|
||||
void refresh::invoke(node_id const& nid, udp::endpoint addr)
|
||||
{
|
||||
observer_ptr p(new refresh_observer(
|
||||
this
|
||||
, nid
|
||||
, m_target
|
||||
));
|
||||
|
||||
m_rpc.invoke(messages::find_node, addr, p);
|
||||
}
|
||||
|
||||
void refresh::done()
|
||||
{
|
||||
m_leftover_nodes_iterator = (int)m_results.size() > m_max_results ?
|
||||
m_results.begin() + m_max_results : m_results.end();
|
||||
|
||||
invoke_pings_or_finish();
|
||||
}
|
||||
|
||||
void refresh::ping_reply(node_id nid)
|
||||
{
|
||||
m_active_pings--;
|
||||
invoke_pings_or_finish();
|
||||
}
|
||||
|
||||
void refresh::ping_timeout(node_id nid)
|
||||
{
|
||||
m_active_pings--;
|
||||
invoke_pings_or_finish();
|
||||
}
|
||||
|
||||
void refresh::invoke_pings_or_finish()
|
||||
{
|
||||
while (m_active_pings < m_max_active_pings)
|
||||
{
|
||||
if (m_leftover_nodes_iterator == m_results.end()) break;
|
||||
|
||||
result const& node = *m_leftover_nodes_iterator;
|
||||
|
||||
// Skip initial nodes
|
||||
if (node.flags & result::initial)
|
||||
{
|
||||
++m_leftover_nodes_iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
observer_ptr p(new ping_observer(this, node.id));
|
||||
|
||||
m_rpc.invoke(messages::ping, node.addr, p);
|
||||
++m_active_pings;
|
||||
++m_leftover_nodes_iterator;
|
||||
}
|
||||
|
||||
if (m_active_pings == 0)
|
||||
{
|
||||
m_done_callback();
|
||||
}
|
||||
}
|
||||
|
||||
} } // namespace libtorrent::dht
|
||||
|
||||
434
cpp/kademlia/routing_table.cpp
Normal file
434
cpp/kademlia/routing_table.cpp
Normal file
@ -0,0 +1,434 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2006, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <numeric>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
|
||||
#include "libtorrent/kademlia/routing_table.hpp"
|
||||
#include "libtorrent/kademlia/node_id.hpp"
|
||||
#include "libtorrent/session_settings.hpp"
|
||||
|
||||
using boost::bind;
|
||||
using boost::uint8_t;
|
||||
|
||||
using boost::posix_time::second_clock;
|
||||
using boost::posix_time::minutes;
|
||||
using boost::posix_time::seconds;
|
||||
using boost::posix_time::hours;
|
||||
|
||||
namespace pt = boost::posix_time;
|
||||
|
||||
namespace libtorrent { namespace dht
|
||||
{
|
||||
|
||||
using asio::ip::udp;
|
||||
typedef asio::ip::address_v4 address;
|
||||
|
||||
routing_table::routing_table(node_id const& id, int bucket_size
|
||||
, dht_settings const& settings)
|
||||
: m_bucket_size(bucket_size)
|
||||
, m_settings(settings)
|
||||
, m_id(id)
|
||||
, m_lowest_active_bucket(160)
|
||||
{
|
||||
// distribute the refresh times for the buckets in an
|
||||
// attempt do even out the network load
|
||||
for (int i = 0; i < 160; ++i)
|
||||
m_bucket_activity[i] = second_clock::universal_time() - seconds(15*60 - i*5);
|
||||
}
|
||||
|
||||
boost::tuple<int, int> routing_table::size() const
|
||||
{
|
||||
int nodes = 0;
|
||||
int replacements = 0;
|
||||
for (table_t::const_iterator i = m_buckets.begin()
|
||||
, end(m_buckets.end()); i != end; ++i)
|
||||
{
|
||||
nodes += i->first.size();
|
||||
replacements += i->second.size();
|
||||
}
|
||||
return boost::make_tuple(nodes, replacements);
|
||||
}
|
||||
|
||||
void routing_table::print_state(std::ostream& os) const
|
||||
{
|
||||
os << "kademlia routing table state\n"
|
||||
<< "bucket_size: " << m_bucket_size << "\n"
|
||||
<< "node_id: " << m_id << "\n\n";
|
||||
|
||||
os << "number of nodes per bucket:\n"
|
||||
"live\n";
|
||||
for (int k = 0; k < 8; ++k)
|
||||
{
|
||||
for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
|
||||
i != end; ++i)
|
||||
{
|
||||
os << (int(i->first.size()) > (7 - k) ? "|" : " ");
|
||||
}
|
||||
os << "\n";
|
||||
}
|
||||
for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
|
||||
i != end; ++i)
|
||||
{
|
||||
os << "+";
|
||||
}
|
||||
os << "\n";
|
||||
for (int k = 0; k < 8; ++k)
|
||||
{
|
||||
for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
|
||||
i != end; ++i)
|
||||
{
|
||||
os << (int(i->second.size()) > k ? "|" : " ");
|
||||
}
|
||||
os << "\n";
|
||||
}
|
||||
os << "cached\n-----------\n";
|
||||
|
||||
os << "nodes:\n";
|
||||
for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
|
||||
i != end; ++i)
|
||||
{
|
||||
int bucket_index = int(i - m_buckets.begin());
|
||||
os << "bucket " << bucket_index << " "
|
||||
<< to_simple_string(m_bucket_activity[bucket_index])
|
||||
<< " " << (bucket_index >= m_lowest_active_bucket?"active":"inactive")
|
||||
<< "\n";
|
||||
for (bucket_t::const_iterator j = i->first.begin()
|
||||
, end(i->first.end()); j != end; ++j)
|
||||
{
|
||||
os << "ip: " << j->addr << " fails: " << j->fail_count
|
||||
<< " id: " << j->id << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void routing_table::touch_bucket(int bucket)
|
||||
{
|
||||
m_bucket_activity[bucket] = second_clock::universal_time();
|
||||
}
|
||||
|
||||
boost::posix_time::ptime routing_table::next_refresh(int bucket)
|
||||
{
|
||||
assert(bucket < 160);
|
||||
assert(bucket >= 0);
|
||||
// lower than or equal to since a refresh of bucket 0 will
|
||||
// effectively refresh the lowest active bucket as well
|
||||
if (bucket <= m_lowest_active_bucket && bucket > 0)
|
||||
return second_clock::universal_time() + minutes(15);
|
||||
return m_bucket_activity[bucket] + minutes(15);
|
||||
}
|
||||
|
||||
void routing_table::replacement_cache(bucket_t& nodes) const
|
||||
{
|
||||
for (table_t::const_iterator i = m_buckets.begin()
|
||||
, end(m_buckets.end()); i != end; ++i)
|
||||
{
|
||||
std::copy(i->second.begin(), i->second.end()
|
||||
, std::back_inserter(nodes));
|
||||
}
|
||||
}
|
||||
|
||||
bool routing_table::need_node(node_id const& id)
|
||||
{
|
||||
int bucket_index = distance_exp(m_id, id);
|
||||
assert(bucket_index < (int)m_buckets.size());
|
||||
assert(bucket_index >= 0);
|
||||
bucket_t& b = m_buckets[bucket_index].first;
|
||||
bucket_t& rb = m_buckets[bucket_index].second;
|
||||
|
||||
// if the replacement cache is full, we don't
|
||||
// need another node. The table is fine the
|
||||
// way it is.
|
||||
if ((int)rb.size() >= m_bucket_size) return false;
|
||||
|
||||
// if the node already exists, we don't need it
|
||||
if (std::find_if(b.begin(), b.end(), bind(std::equal_to<node_id>()
|
||||
, bind(&node_entry::id, _1), id)) != b.end()) return false;
|
||||
|
||||
if (std::find_if(rb.begin(), rb.end(), bind(std::equal_to<node_id>()
|
||||
, bind(&node_entry::id, _1), id)) != rb.end()) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void routing_table::node_failed(node_id const& id)
|
||||
{
|
||||
int bucket_index = distance_exp(m_id, id);
|
||||
assert(bucket_index < (int)m_buckets.size());
|
||||
assert(bucket_index >= 0);
|
||||
bucket_t& b = m_buckets[bucket_index].first;
|
||||
bucket_t& rb = m_buckets[bucket_index].second;
|
||||
|
||||
bucket_t::iterator i = std::find_if(b.begin(), b.end()
|
||||
, bind(std::equal_to<node_id>()
|
||||
, bind(&node_entry::id, _1), id));
|
||||
|
||||
if (i == b.end()) return;
|
||||
|
||||
// if messages to ourself fails, ignore it
|
||||
if (bucket_index == 0) return;
|
||||
|
||||
if (rb.empty())
|
||||
{
|
||||
++i->fail_count;
|
||||
if (i->fail_count >= m_settings.max_fail_count)
|
||||
{
|
||||
b.erase(i);
|
||||
assert(m_lowest_active_bucket <= bucket_index);
|
||||
while (m_buckets[m_lowest_active_bucket].first.empty()
|
||||
&& m_lowest_active_bucket < 160)
|
||||
{
|
||||
++m_lowest_active_bucket;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
b.erase(i);
|
||||
b.push_back(rb.back());
|
||||
rb.erase(rb.end() - 1);
|
||||
}
|
||||
|
||||
void routing_table::add_router_node(udp::endpoint router)
|
||||
{
|
||||
m_router_nodes.insert(router);
|
||||
}
|
||||
|
||||
// this function is called every time the node sees
|
||||
// a sign of a node being alive. This node will either
|
||||
// be inserted in the k-buckets or be moved to the top
|
||||
// of its bucket.
|
||||
// the return value indicates if the table needs a refresh.
|
||||
// if true, the node should refresh the table (i.e. do a find_node
|
||||
// on its own id)
|
||||
bool routing_table::node_seen(node_id const& id, udp::endpoint addr)
|
||||
{
|
||||
if (m_router_nodes.find(addr) != m_router_nodes.end()) return false;
|
||||
int bucket_index = distance_exp(m_id, id);
|
||||
assert(bucket_index < (int)m_buckets.size());
|
||||
assert(bucket_index >= 0);
|
||||
bucket_t& b = m_buckets[bucket_index].first;
|
||||
|
||||
bucket_t::iterator i = std::find_if(b.begin(), b.end()
|
||||
, bind(std::equal_to<node_id>()
|
||||
, bind(&node_entry::id, _1), id));
|
||||
|
||||
bool ret = need_bootstrap();
|
||||
|
||||
m_bucket_activity[bucket_index] = second_clock::universal_time();
|
||||
|
||||
if (i != b.end())
|
||||
{
|
||||
// TODO: what do we do if we see a node with
|
||||
// the same id as a node at a different address?
|
||||
// assert(i->addr == addr);
|
||||
|
||||
// we already have the node in our bucket
|
||||
// just move it to the back since it was
|
||||
// the last node we had any contact with
|
||||
// in this bucket
|
||||
b.erase(i);
|
||||
b.push_back(node_entry(id, addr));
|
||||
// TORRENT_LOG(table) << "replacing node: " << id << " " << addr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// if the node was not present in our list
|
||||
// we will only insert it if there is room
|
||||
// for it, or if some of our nodes have gone
|
||||
// offline
|
||||
if ((int)b.size() < m_bucket_size)
|
||||
{
|
||||
b.push_back(node_entry(id, addr));
|
||||
// if bucket index is 0, the node is ourselves
|
||||
// don't updated m_lowest_active_bucket
|
||||
if (bucket_index < m_lowest_active_bucket
|
||||
&& bucket_index > 0)
|
||||
m_lowest_active_bucket = bucket_index;
|
||||
// TORRENT_LOG(table) << "inserting node: " << id << " " << addr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// if there is no room, we look for nodes marked as stale
|
||||
// in the k-bucket. If we find one, we can replace it.
|
||||
// A node is considered stale if it has failed at least one
|
||||
// time. Here we choose the node that has failed most times.
|
||||
// If we don't find one, place this node in the replacement-
|
||||
// cache and replace any nodes that will fail in the future
|
||||
// with nodes from that cache.
|
||||
|
||||
i = std::max_element(b.begin(), b.end()
|
||||
, bind(std::less<int>()
|
||||
, bind(&node_entry::fail_count, _1)
|
||||
, bind(&node_entry::fail_count, _2)));
|
||||
|
||||
if (i != b.end() && i->fail_count > 0)
|
||||
{
|
||||
// i points to a node that has been marked
|
||||
// as stale. Replace it with this new one
|
||||
b.erase(i);
|
||||
b.push_back(node_entry(id, addr));
|
||||
// TORRENT_LOG(table) << "replacing stale node: " << id << " " << addr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// if we don't have any identified stale nodes in
|
||||
// the bucket, and the bucket is full, we have to
|
||||
// cache this node and wait until some node fails
|
||||
// and then replace it.
|
||||
|
||||
bucket_t& rb = m_buckets[bucket_index].second;
|
||||
|
||||
i = std::find_if(rb.begin(), rb.end()
|
||||
, bind(std::equal_to<node_id>()
|
||||
, bind(&node_entry::id, _1), id));
|
||||
|
||||
// if the node is already in the replacement bucket
|
||||
// just return.
|
||||
if (i != rb.end()) return ret;
|
||||
|
||||
if ((int)rb.size() > m_bucket_size) rb.erase(rb.begin());
|
||||
rb.push_back(node_entry(id, addr));
|
||||
// TORRENT_LOG(table) << "inserting node in replacement cache: " << id << " " << addr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool routing_table::need_bootstrap() const
|
||||
{
|
||||
for (const_iterator i = begin(); i != end(); ++i)
|
||||
{
|
||||
if (i->fail_count == 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// fills the vector with the k nodes from our buckets that
|
||||
// are nearest to the given id.
|
||||
void routing_table::find_node(node_id const& target
|
||||
, std::vector<node_entry>& l, bool include_self, int count)
|
||||
{
|
||||
l.clear();
|
||||
if (count == 0) count = m_bucket_size;
|
||||
l.reserve(count);
|
||||
|
||||
int bucket_index = distance_exp(m_id, target);
|
||||
bucket_t& b = m_buckets[bucket_index].first;
|
||||
|
||||
// copy all nodes that hasn't failed into the target
|
||||
// vector.
|
||||
std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l)
|
||||
, bind(&node_entry::fail_count, _1));
|
||||
assert((int)l.size() <= count);
|
||||
|
||||
if ((int)l.size() == count)
|
||||
{
|
||||
assert(std::count_if(l.begin(), l.end()
|
||||
, boost::bind(std::not_equal_to<int>()
|
||||
, boost::bind(&node_entry::fail_count, _1), 0)) == 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// if we didn't have enough nodes in that bucket
|
||||
// we have to reply with nodes from buckets closer
|
||||
// to us. i.e. all the buckets in the range
|
||||
// [0, bucket_index) if we are to include ourself
|
||||
// or [1, bucket_index) if not.
|
||||
bucket_t tmpb;
|
||||
for (int i = include_self?0:1; i < count; ++i)
|
||||
{
|
||||
bucket_t& b = m_buckets[i].first;
|
||||
std::remove_copy_if(b.begin(), b.end(), std::back_inserter(tmpb)
|
||||
, bind(&node_entry::fail_count, _1));
|
||||
}
|
||||
|
||||
std::random_shuffle(tmpb.begin(), tmpb.end());
|
||||
size_t to_copy = (std::min)(m_bucket_size - l.size()
|
||||
, tmpb.size());
|
||||
std::copy(tmpb.begin(), tmpb.begin() + to_copy
|
||||
, std::back_inserter(l));
|
||||
|
||||
assert((int)l.size() <= m_bucket_size);
|
||||
|
||||
// return if we have enough nodes or if the bucket index
|
||||
// is the biggest index available (there are no more buckets)
|
||||
// to look in.
|
||||
if ((int)l.size() == count
|
||||
|| bucket_index == (int)m_buckets.size() - 1)
|
||||
{
|
||||
assert(std::count_if(l.begin(), l.end()
|
||||
, boost::bind(std::not_equal_to<int>()
|
||||
, boost::bind(&node_entry::fail_count, _1), 0)) == 0);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = bucket_index + 1; i < m_buckets.size(); ++i)
|
||||
{
|
||||
bucket_t& b = m_buckets[i].first;
|
||||
|
||||
std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l)
|
||||
, bind(&node_entry::fail_count, _1));
|
||||
if ((int)l.size() >= count)
|
||||
{
|
||||
l.erase(l.begin() + count, l.end());
|
||||
assert(std::count_if(l.begin(), l.end()
|
||||
, boost::bind(std::not_equal_to<int>()
|
||||
, boost::bind(&node_entry::fail_count, _1), 0)) == 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
assert((int)l.size() == count
|
||||
|| std::distance(l.begin(), l.end()) < m_bucket_size);
|
||||
assert((int)l.size() <= count);
|
||||
|
||||
assert(std::count_if(l.begin(), l.end()
|
||||
, boost::bind(std::not_equal_to<int>()
|
||||
, boost::bind(&node_entry::fail_count, _1), 0)) == 0);
|
||||
}
|
||||
|
||||
routing_table::iterator routing_table::begin() const
|
||||
{
|
||||
return iterator(m_buckets.begin(), m_buckets.end());
|
||||
}
|
||||
|
||||
routing_table::iterator routing_table::end() const
|
||||
{
|
||||
return iterator(m_buckets.end(), m_buckets.end());
|
||||
}
|
||||
|
||||
} } // namespace libtorrent::dht
|
||||
|
||||
356
cpp/kademlia/rpc_manager.cpp
Normal file
356
cpp/kademlia/rpc_manager.cpp
Normal file
@ -0,0 +1,356 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2006, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include <boost/date_time/posix_time/ptime.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include <libtorrent/io.hpp>
|
||||
#include <libtorrent/invariant_check.hpp>
|
||||
#include <libtorrent/kademlia/rpc_manager.hpp>
|
||||
#include <libtorrent/kademlia/logging.hpp>
|
||||
#include <libtorrent/kademlia/routing_table.hpp>
|
||||
#include <libtorrent/hasher.hpp>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
using boost::posix_time::ptime;
|
||||
using boost::posix_time::time_duration;
|
||||
using boost::posix_time::microsec_clock;
|
||||
using boost::posix_time::seconds;
|
||||
using boost::posix_time::milliseconds;
|
||||
using boost::shared_ptr;
|
||||
using boost::bind;
|
||||
|
||||
namespace libtorrent { namespace dht
|
||||
{
|
||||
|
||||
namespace io = libtorrent::detail;
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_DEFINE_LOG(rpc)
|
||||
#endif
|
||||
|
||||
node_id generate_id();
|
||||
|
||||
rpc_manager::rpc_manager(fun const& f, node_id const& our_id
|
||||
, routing_table& table, send_fun const& sf)
|
||||
: m_next_transaction_id(rand() % max_transactions)
|
||||
, m_oldest_transaction_id(m_next_transaction_id)
|
||||
, m_incoming(f)
|
||||
, m_send(sf)
|
||||
, m_our_id(our_id)
|
||||
, m_table(table)
|
||||
, m_timer(boost::posix_time::microsec_clock::universal_time())
|
||||
, m_random_number(generate_id())
|
||||
{
|
||||
std::srand(time(0));
|
||||
}
|
||||
|
||||
rpc_manager::~rpc_manager()
|
||||
{
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(rpc) << "Destructing";
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
void rpc_manager::check_invariant() const
|
||||
{
|
||||
assert(m_oldest_transaction_id >= 0);
|
||||
assert(m_oldest_transaction_id < max_transactions);
|
||||
assert(m_next_transaction_id >= 0);
|
||||
assert(m_next_transaction_id < max_transactions);
|
||||
assert(!m_transactions[m_next_transaction_id]);
|
||||
|
||||
for (int i = (m_next_transaction_id + 1) % max_transactions;
|
||||
i != m_oldest_transaction_id; i = (i + 1) % max_transactions)
|
||||
{
|
||||
assert(!m_transactions[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool rpc_manager::incoming(msg const& m)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (m.reply)
|
||||
{
|
||||
// if we don't have the transaction id in our
|
||||
// request list, ignore the packet
|
||||
|
||||
if (m.transaction_id.size() != 2)
|
||||
{
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(rpc) << "Reply with invalid transaction id size: "
|
||||
<< m.transaction_id.size() << " from " << m.addr;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string::const_iterator i = m.transaction_id.begin();
|
||||
int tid = io::read_uint16(i);
|
||||
|
||||
if (tid >= (int)m_transactions.size()
|
||||
|| tid < 0)
|
||||
{
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(rpc) << "Reply with unknown transaction id: "
|
||||
<< tid << " from " << m.addr;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
boost::shared_ptr<observer> o = m_transactions[tid];
|
||||
|
||||
if (!o)
|
||||
{
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(rpc) << "Reply with unknown transaction id: "
|
||||
<< tid << " from " << m.addr;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m.addr != o->target_addr)
|
||||
{
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(rpc) << "Reply with incorrect address and valid transaction id: "
|
||||
<< tid << " from " << m.addr;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
std::ofstream reply_stats("libtorrent_logs/round_trip_ms.log", std::ios::app);
|
||||
reply_stats << m.addr << "\t" << (microsec_clock::universal_time()
|
||||
- o->sent).total_milliseconds() << std::endl;
|
||||
#endif
|
||||
o->reply(m);
|
||||
m_transactions[tid].reset();
|
||||
|
||||
if (m.piggy_backed_ping)
|
||||
{
|
||||
// there is a ping request piggy
|
||||
// backed in this reply
|
||||
msg ph;
|
||||
ph.message_id = messages::ping;
|
||||
ph.transaction_id = m.ping_transaction_id;
|
||||
ph.id = m_our_id;
|
||||
ph.addr = m.addr;
|
||||
|
||||
msg empty;
|
||||
|
||||
reply(empty, ph);
|
||||
}
|
||||
return m_table.node_seen(m.id, m.addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// this is an incoming request
|
||||
m_incoming(m);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
time_duration rpc_manager::tick()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
using boost::posix_time::microsec_clock;
|
||||
|
||||
const int timeout_ms = 20 * 1000;
|
||||
|
||||
// look for observers that has timed out
|
||||
|
||||
if (m_next_transaction_id == m_oldest_transaction_id) return milliseconds(timeout_ms);
|
||||
|
||||
for (;m_next_transaction_id != m_oldest_transaction_id;
|
||||
m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions)
|
||||
{
|
||||
assert(m_oldest_transaction_id >= 0);
|
||||
assert(m_oldest_transaction_id < max_transactions);
|
||||
|
||||
boost::shared_ptr<observer> o = m_transactions[m_oldest_transaction_id];
|
||||
if (!o) continue;
|
||||
|
||||
time_duration diff = o->sent + milliseconds(timeout_ms)
|
||||
- microsec_clock::universal_time();
|
||||
if (diff > seconds(0))
|
||||
{
|
||||
if (diff < seconds(1)) return seconds(1);
|
||||
return diff;
|
||||
}
|
||||
|
||||
m_transactions[m_oldest_transaction_id].reset();
|
||||
o->timeout();
|
||||
}
|
||||
return milliseconds(timeout_ms);
|
||||
}
|
||||
|
||||
unsigned int rpc_manager::new_transaction_id()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
unsigned int tid = m_next_transaction_id;
|
||||
m_next_transaction_id = (m_next_transaction_id + 1) % max_transactions;
|
||||
// boost::shared_ptr<observer> o = m_transactions[m_next_transaction_id];
|
||||
if (m_transactions[m_next_transaction_id])
|
||||
{
|
||||
m_transactions[m_next_transaction_id].reset();
|
||||
assert(m_oldest_transaction_id == m_next_transaction_id);
|
||||
}
|
||||
if (m_oldest_transaction_id == m_next_transaction_id)
|
||||
{
|
||||
m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions;
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(rpc) << "WARNING: transaction limit reached! Too many concurrent"
|
||||
" messages! limit: " << (int)max_transactions;
|
||||
#endif
|
||||
update_oldest_transaction_id();
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
assert(!m_transactions[m_next_transaction_id]);
|
||||
for (int i = (m_next_transaction_id + 1) % max_transactions;
|
||||
i != m_oldest_transaction_id; i = (i + 1) % max_transactions)
|
||||
{
|
||||
assert(!m_transactions[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
// hopefully this wouldn't happen, but unfortunately, the
|
||||
// traversal algorithm will simply fail in case its connections
|
||||
// are overwritten. If timeout() is called, it will likely spawn
|
||||
// another connection, which in turn will close the next one
|
||||
// and so on.
|
||||
// if (o) o->timeout();
|
||||
return tid;
|
||||
}
|
||||
|
||||
void rpc_manager::update_oldest_transaction_id()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert(m_oldest_transaction_id != m_next_transaction_id);
|
||||
while (!m_transactions[m_oldest_transaction_id])
|
||||
{
|
||||
m_oldest_transaction_id = (m_oldest_transaction_id + 1)
|
||||
% max_transactions;
|
||||
if (m_oldest_transaction_id == m_next_transaction_id)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void rpc_manager::invoke(int message_id, udp::endpoint target_addr
|
||||
, shared_ptr<observer> o)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
msg m;
|
||||
m.message_id = message_id;
|
||||
m.reply = false;
|
||||
m.id = m_our_id;
|
||||
m.addr = target_addr;
|
||||
int tid = new_transaction_id();
|
||||
m.transaction_id.clear();
|
||||
std::back_insert_iterator<std::string> out(m.transaction_id);
|
||||
io::write_uint16(tid, out);
|
||||
|
||||
o->send(m);
|
||||
|
||||
m_transactions[tid] = o;
|
||||
o->sent = boost::posix_time::microsec_clock::universal_time();
|
||||
o->target_addr = target_addr;
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(rpc) << "Invoking " << messages::ids[message_id]
|
||||
<< " -> " << target_addr;
|
||||
#endif
|
||||
m_send(m);
|
||||
}
|
||||
|
||||
void rpc_manager::reply(msg& m, msg const& reply_to)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (m.message_id != messages::error)
|
||||
m.message_id = reply_to.message_id;
|
||||
m.addr = reply_to.addr;
|
||||
m.reply = true;
|
||||
m.piggy_backed_ping = false;
|
||||
m.id = m_our_id;
|
||||
m.transaction_id = reply_to.transaction_id;
|
||||
|
||||
m_send(m);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct dummy_observer : observer
|
||||
{
|
||||
virtual void reply(msg const&) {}
|
||||
virtual void timeout() {}
|
||||
virtual void send(msg&) {}
|
||||
};
|
||||
}
|
||||
|
||||
void rpc_manager::reply_with_ping(msg& m, msg const& reply_to)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (m.message_id != messages::error)
|
||||
m.message_id = reply_to.message_id;
|
||||
m.addr = reply_to.addr;
|
||||
m.reply = true;
|
||||
m.piggy_backed_ping = true;
|
||||
m.id = m_our_id;
|
||||
m.transaction_id = reply_to.transaction_id;
|
||||
|
||||
int ptid = new_transaction_id();
|
||||
m.ping_transaction_id.clear();
|
||||
std::back_insert_iterator<std::string> out(m.ping_transaction_id);
|
||||
io::write_uint16(ptid, out);
|
||||
|
||||
boost::shared_ptr<observer> o(new dummy_observer);
|
||||
m_transactions[ptid] = o;
|
||||
o->sent = boost::posix_time::microsec_clock::universal_time();
|
||||
o->target_addr = m.addr;
|
||||
|
||||
m_send(m);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} } // namespace libtorrent::dht
|
||||
|
||||
162
cpp/kademlia/traversal_algorithm.cpp
Normal file
162
cpp/kademlia/traversal_algorithm.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <libtorrent/kademlia/traversal_algorithm.hpp>
|
||||
#include <libtorrent/kademlia/routing_table.hpp>
|
||||
#include <libtorrent/kademlia/rpc_manager.hpp>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
using boost::bind;
|
||||
using asio::ip::udp;
|
||||
|
||||
namespace libtorrent { namespace dht
|
||||
{
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_DEFINE_LOG(traversal)
|
||||
#endif
|
||||
|
||||
void traversal_algorithm::add_entry(node_id const& id, udp::endpoint addr, unsigned char flags)
|
||||
{
|
||||
if (m_failed.find(addr) != m_failed.end()) return;
|
||||
|
||||
result const entry(id, addr, flags);
|
||||
|
||||
std::vector<result>::iterator i = std::lower_bound(
|
||||
m_results.begin()
|
||||
, m_results.end()
|
||||
, entry
|
||||
, bind(
|
||||
compare_ref
|
||||
, bind(&result::id, _1)
|
||||
, bind(&result::id, _2)
|
||||
, m_target
|
||||
)
|
||||
);
|
||||
|
||||
if (i == m_results.end() || i->id != id)
|
||||
{
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(traversal) << "adding result: " << id << " " << addr;
|
||||
#endif
|
||||
m_results.insert(i, entry);
|
||||
}
|
||||
}
|
||||
|
||||
void traversal_algorithm::traverse(node_id const& id, udp::endpoint addr)
|
||||
{
|
||||
add_entry(id, addr, 0);
|
||||
}
|
||||
|
||||
void traversal_algorithm::finished(node_id const& id)
|
||||
{
|
||||
--m_invoke_count;
|
||||
add_requests();
|
||||
if (m_invoke_count == 0) done();
|
||||
}
|
||||
|
||||
void traversal_algorithm::failed(node_id const& id)
|
||||
{
|
||||
m_invoke_count--;
|
||||
|
||||
std::vector<result>::iterator i = std::find_if(
|
||||
m_results.begin()
|
||||
, m_results.end()
|
||||
, bind(
|
||||
std::equal_to<node_id>()
|
||||
, bind(&result::id, _1)
|
||||
, id
|
||||
)
|
||||
);
|
||||
|
||||
assert(i != m_results.end());
|
||||
|
||||
assert(i->flags & result::queried);
|
||||
m_failed.insert(i->addr);
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(traversal) << "failed: " << i->id << " " << i->addr;
|
||||
#endif
|
||||
m_results.erase(i);
|
||||
m_table.node_failed(id);
|
||||
add_requests();
|
||||
if (m_invoke_count == 0) done();
|
||||
}
|
||||
|
||||
void traversal_algorithm::add_request(node_id const& id, udp::endpoint addr)
|
||||
{
|
||||
invoke(id, addr);
|
||||
++m_invoke_count;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool bitwise_nand(unsigned char lhs, unsigned char rhs)
|
||||
{
|
||||
return (lhs & rhs) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
void traversal_algorithm::add_requests()
|
||||
{
|
||||
while (m_invoke_count < m_branch_factor)
|
||||
{
|
||||
// Find the first node that hasn't already been queried.
|
||||
// TODO: Better heuristic
|
||||
std::vector<result>::iterator i = std::find_if(
|
||||
m_results.begin()
|
||||
, last_iterator()
|
||||
, bind(
|
||||
&bitwise_nand
|
||||
, bind(&result::flags, _1)
|
||||
, (unsigned char)result::queried
|
||||
)
|
||||
);
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(traversal) << "nodes left (" << this << "): " << (last_iterator() - i);
|
||||
#endif
|
||||
|
||||
if (i == last_iterator()) break;
|
||||
|
||||
add_request(i->id, i->addr);
|
||||
i->flags |= result::queried;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<traversal_algorithm::result>::iterator traversal_algorithm::last_iterator()
|
||||
{
|
||||
return (int)m_results.size() >= m_max_results ?
|
||||
m_results.begin() + m_max_results
|
||||
: m_results.end();
|
||||
}
|
||||
|
||||
} } // namespace libtorrent::dht
|
||||
|
||||
2050
cpp/peer_connection.cpp
Executable file
2050
cpp/peer_connection.cpp
Executable file
File diff suppressed because it is too large
Load Diff
1198
cpp/piece_picker.cpp
Executable file
1198
cpp/piece_picker.cpp
Executable file
File diff suppressed because it is too large
Load Diff
1419
cpp/policy.cpp
Executable file
1419
cpp/policy.cpp
Executable file
File diff suppressed because it is too large
Load Diff
287
cpp/session.cpp
Executable file
287
cpp/session.cpp
Executable file
@ -0,0 +1,287 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2006, Arvid Norberg, Magnus Jonsson
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 1)
|
||||
#endif
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/filesystem/convenience.hpp>
|
||||
#include <boost/filesystem/exception.hpp>
|
||||
#include <boost/limits.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "libtorrent/peer_id.hpp"
|
||||
#include "libtorrent/torrent_info.hpp"
|
||||
#include "libtorrent/tracker_manager.hpp"
|
||||
#include "libtorrent/bencode.hpp"
|
||||
#include "libtorrent/hasher.hpp"
|
||||
#include "libtorrent/entry.hpp"
|
||||
#include "libtorrent/session.hpp"
|
||||
#include "libtorrent/fingerprint.hpp"
|
||||
#include "libtorrent/entry.hpp"
|
||||
#include "libtorrent/alert_types.hpp"
|
||||
#include "libtorrent/invariant_check.hpp"
|
||||
#include "libtorrent/file.hpp"
|
||||
#include "libtorrent/allocate_resources.hpp"
|
||||
#include "libtorrent/bt_peer_connection.hpp"
|
||||
#include "libtorrent/ip_filter.hpp"
|
||||
#include "libtorrent/socket.hpp"
|
||||
#include "libtorrent/aux_/session_impl.hpp"
|
||||
#include "libtorrent/kademlia/dht_tracker.hpp"
|
||||
|
||||
using namespace boost::posix_time;
|
||||
using boost::shared_ptr;
|
||||
using boost::weak_ptr;
|
||||
using boost::bind;
|
||||
using boost::mutex;
|
||||
using libtorrent::aux::session_impl;
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
session::session(
|
||||
fingerprint const& id
|
||||
, std::pair<int, int> listen_port_range
|
||||
, char const* listen_interface)
|
||||
: m_impl(new session_impl(listen_port_range, id, listen_interface))
|
||||
{
|
||||
// turn off the filename checking in boost.filesystem
|
||||
using namespace boost::filesystem;
|
||||
if (path::default_name_check_writable())
|
||||
path::default_name_check(no_check);
|
||||
assert(listen_port_range.first > 0);
|
||||
assert(listen_port_range.first < listen_port_range.second);
|
||||
#ifndef NDEBUG
|
||||
// this test was added after it came to my attention
|
||||
// that devstudios managed c++ failed to generate
|
||||
// correct code for boost.function
|
||||
boost::function0<void> test = boost::ref(*m_impl);
|
||||
assert(!test.empty());
|
||||
#endif
|
||||
}
|
||||
|
||||
session::session(fingerprint const& id)
|
||||
: m_impl(new session_impl(std::make_pair(0, 0), id))
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
boost::function0<void> test = boost::ref(*m_impl);
|
||||
assert(!test.empty());
|
||||
#endif
|
||||
}
|
||||
|
||||
session::~session()
|
||||
{
|
||||
assert(m_impl);
|
||||
// if there is at least one destruction-proxy
|
||||
// abort the session and let the destructor
|
||||
// of the proxy to syncronize
|
||||
if (!m_impl.unique())
|
||||
m_impl->abort();
|
||||
}
|
||||
|
||||
void session::disable_extensions()
|
||||
{
|
||||
m_impl->disable_extensions();
|
||||
}
|
||||
|
||||
void session::set_ip_filter(ip_filter const& f)
|
||||
{
|
||||
m_impl->set_ip_filter(f);
|
||||
}
|
||||
|
||||
void session::set_peer_id(peer_id const& id)
|
||||
{
|
||||
m_impl->set_peer_id(id);
|
||||
}
|
||||
|
||||
void session::set_key(int key)
|
||||
{
|
||||
m_impl->set_key(key);
|
||||
}
|
||||
|
||||
void session::enable_extension(extension_index i)
|
||||
{
|
||||
m_impl->enable_extension(i);
|
||||
}
|
||||
|
||||
std::vector<torrent_handle> session::get_torrents() const
|
||||
{
|
||||
return m_impl->get_torrents();
|
||||
}
|
||||
|
||||
// if the torrent already exists, this will throw duplicate_torrent
|
||||
torrent_handle session::add_torrent(
|
||||
torrent_info const& ti
|
||||
, boost::filesystem::path const& save_path
|
||||
, entry const& resume_data
|
||||
, bool compact_mode
|
||||
, int block_size)
|
||||
{
|
||||
return m_impl->add_torrent(ti, save_path, resume_data
|
||||
, compact_mode, block_size);
|
||||
}
|
||||
|
||||
torrent_handle session::add_torrent(
|
||||
char const* tracker_url
|
||||
, sha1_hash const& info_hash
|
||||
, boost::filesystem::path const& save_path
|
||||
, entry const& e
|
||||
, bool compact_mode
|
||||
, int block_size)
|
||||
{
|
||||
return m_impl->add_torrent(tracker_url, info_hash, save_path, e
|
||||
, compact_mode, block_size);
|
||||
}
|
||||
|
||||
void session::remove_torrent(const torrent_handle& h)
|
||||
{
|
||||
m_impl->remove_torrent(h);
|
||||
}
|
||||
|
||||
bool session::listen_on(
|
||||
std::pair<int, int> const& port_range
|
||||
, const char* net_interface)
|
||||
{
|
||||
return m_impl->listen_on(port_range, net_interface);
|
||||
}
|
||||
|
||||
unsigned short session::listen_port() const
|
||||
{
|
||||
return m_impl->listen_port();
|
||||
}
|
||||
|
||||
session_status session::status() const
|
||||
{
|
||||
return m_impl->status();
|
||||
}
|
||||
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
|
||||
void session::start_dht(entry const& startup_state)
|
||||
{
|
||||
m_impl->start_dht(startup_state);
|
||||
}
|
||||
|
||||
void session::stop_dht()
|
||||
{
|
||||
m_impl->stop_dht();
|
||||
}
|
||||
|
||||
void session::set_dht_settings(dht_settings const& settings)
|
||||
{
|
||||
m_impl->set_dht_settings(settings);
|
||||
}
|
||||
|
||||
entry session::dht_state() const
|
||||
{
|
||||
return m_impl->dht_state();
|
||||
}
|
||||
|
||||
void session::add_dht_node(std::pair<std::string, int> const& node)
|
||||
{
|
||||
m_impl->add_dht_node(node);
|
||||
}
|
||||
|
||||
void session::add_dht_router(std::pair<std::string, int> const& node)
|
||||
{
|
||||
m_impl->add_dht_router(node);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool session::is_listening() const
|
||||
{
|
||||
return m_impl->is_listening();
|
||||
}
|
||||
|
||||
void session::set_settings(session_settings const& s)
|
||||
{
|
||||
m_impl->set_settings(s);
|
||||
}
|
||||
|
||||
session_settings const& session::settings()
|
||||
{
|
||||
return m_impl->settings();
|
||||
}
|
||||
|
||||
void session::set_max_uploads(int limit)
|
||||
{
|
||||
m_impl->set_max_uploads(limit);
|
||||
}
|
||||
|
||||
void session::set_max_connections(int limit)
|
||||
{
|
||||
m_impl->set_max_connections(limit);
|
||||
}
|
||||
|
||||
void session::set_max_half_open_connections(int limit)
|
||||
{
|
||||
m_impl->set_max_half_open_connections(limit);
|
||||
}
|
||||
|
||||
void session::set_upload_rate_limit(int bytes_per_second)
|
||||
{
|
||||
m_impl->set_upload_rate_limit(bytes_per_second);
|
||||
}
|
||||
|
||||
void session::set_download_rate_limit(int bytes_per_second)
|
||||
{
|
||||
m_impl->set_download_rate_limit(bytes_per_second);
|
||||
}
|
||||
|
||||
std::auto_ptr<alert> session::pop_alert()
|
||||
{
|
||||
return m_impl->pop_alert();
|
||||
}
|
||||
|
||||
void session::set_severity_level(alert::severity_t s)
|
||||
{
|
||||
m_impl->set_severity_level(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
1855
cpp/session_impl.cpp
Executable file
1855
cpp/session_impl.cpp
Executable file
File diff suppressed because it is too large
Load Diff
314
cpp/sha1.cpp
Executable file
314
cpp/sha1.cpp
Executable file
@ -0,0 +1,314 @@
|
||||
/*
|
||||
SHA-1 C++ conversion
|
||||
|
||||
original version:
|
||||
|
||||
SHA-1 in C
|
||||
By Steve Reid <sreid@sea-to-sky.net>
|
||||
100% Public Domain
|
||||
|
||||
changelog at the end of the file.
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
// if you don't want boost
|
||||
// replace with
|
||||
// #include <stdint.h>
|
||||
|
||||
#include <boost/cstdint.hpp>
|
||||
using boost::uint32_t;
|
||||
using boost::uint8_t;
|
||||
|
||||
#include "libtorrent/config.hpp"
|
||||
|
||||
struct TORRENT_EXPORT SHA1_CTX
|
||||
{
|
||||
uint32_t state[5];
|
||||
uint32_t count[2];
|
||||
uint8_t buffer[64];
|
||||
};
|
||||
|
||||
TORRENT_EXPORT void SHA1Init(SHA1_CTX* context);
|
||||
TORRENT_EXPORT void SHA1Update(SHA1_CTX* context, uint8_t const* data, uint32_t len);
|
||||
TORRENT_EXPORT void SHA1Final(SHA1_CTX* context, uint8_t* digest);
|
||||
|
||||
namespace
|
||||
{
|
||||
union CHAR64LONG16
|
||||
{
|
||||
uint8_t c[64];
|
||||
uint32_t l[16];
|
||||
};
|
||||
|
||||
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||
|
||||
// blk0() and blk() perform the initial expand.
|
||||
// I got the idea of expanding during the round function from SSLeay
|
||||
struct little_endian_blk0
|
||||
{
|
||||
static uint32_t apply(CHAR64LONG16* block, int i)
|
||||
{
|
||||
return block->l[i] = (rol(block->l[i],24)&0xFF00FF00)
|
||||
| (rol(block->l[i],8)&0x00FF00FF);
|
||||
}
|
||||
};
|
||||
|
||||
struct big_endian_blk0
|
||||
{
|
||||
static uint32_t apply(CHAR64LONG16* block, int i)
|
||||
{
|
||||
return block->l[i];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
|
||||
^block->l[(i+2)&15]^block->l[i&15],1))
|
||||
|
||||
// (R0+R1), R2, R3, R4 are the different operations used in SHA1
|
||||
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+BlkFun::apply(block, i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
|
||||
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
|
||||
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
|
||||
|
||||
// Hash a single 512-bit block. This is the core of the algorithm.
|
||||
template <class BlkFun>
|
||||
void SHA1Transform(uint32_t state[5], uint8_t const buffer[64])
|
||||
{
|
||||
using namespace std;
|
||||
uint32_t a, b, c, d, e;
|
||||
|
||||
CHAR64LONG16* block;
|
||||
uint8_t workspace[64];
|
||||
block = (CHAR64LONG16*)workspace;
|
||||
memcpy(block, buffer, 64);
|
||||
|
||||
// Copy context->state[] to working vars
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
// 4 rounds of 20 operations each. Loop unrolled.
|
||||
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
|
||||
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
|
||||
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
|
||||
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
|
||||
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
|
||||
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
|
||||
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
|
||||
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
|
||||
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
|
||||
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
|
||||
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
|
||||
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
|
||||
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
|
||||
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
|
||||
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
|
||||
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
|
||||
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
|
||||
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
||||
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
||||
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
||||
// Add the working vars back into context.state[]
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
// Wipe variables
|
||||
a = b = c = d = e = 0;
|
||||
}
|
||||
|
||||
void SHAPrintContext(SHA1_CTX *context, char *msg)
|
||||
{
|
||||
using namespace std;
|
||||
printf("%s (%d,%d) %x %x %x %x %x\n"
|
||||
, msg, context->count[0], context->count[1]
|
||||
, context->state[0], context->state[1]
|
||||
, context->state[2], context->state[3]
|
||||
, context->state[4]);
|
||||
}
|
||||
|
||||
template <class BlkFun>
|
||||
void internal_update(SHA1_CTX* context, uint8_t const* data, uint32_t len)
|
||||
{
|
||||
using namespace std;
|
||||
uint32_t i, j; // JHB
|
||||
|
||||
#ifdef VERBOSE
|
||||
SHAPrintContext(context, "before");
|
||||
#endif
|
||||
j = (context->count[0] >> 3) & 63;
|
||||
if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
|
||||
context->count[1] += (len >> 29);
|
||||
if ((j + len) > 63)
|
||||
{
|
||||
memcpy(&context->buffer[j], data, (i = 64-j));
|
||||
SHA1Transform<BlkFun>(context->state, context->buffer);
|
||||
for ( ; i + 63 < len; i += 64)
|
||||
{
|
||||
SHA1Transform<BlkFun>(context->state, &data[i]);
|
||||
}
|
||||
j = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
i = 0;
|
||||
}
|
||||
memcpy(&context->buffer[j], &data[i], len - i);
|
||||
#ifdef VERBOSE
|
||||
SHAPrintContext(context, "after ");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_big_endian()
|
||||
{
|
||||
uint32_t test = 1;
|
||||
return *reinterpret_cast<uint8_t*>(&test) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
// SHA1Init - Initialize new context
|
||||
|
||||
void SHA1Init(SHA1_CTX* context)
|
||||
{
|
||||
// SHA1 initialization constants
|
||||
context->state[0] = 0x67452301;
|
||||
context->state[1] = 0xEFCDAB89;
|
||||
context->state[2] = 0x98BADCFE;
|
||||
context->state[3] = 0x10325476;
|
||||
context->state[4] = 0xC3D2E1F0;
|
||||
context->count[0] = context->count[1] = 0;
|
||||
}
|
||||
|
||||
|
||||
// Run your data through this.
|
||||
|
||||
void SHA1Update(SHA1_CTX* context, uint8_t const* data, uint32_t len)
|
||||
{
|
||||
#if defined __BIG_ENDIAN__
|
||||
internal_update<big_endian_blk0>(context, data, len);
|
||||
#elif defined LITTLE_ENDIAN
|
||||
internal_update<little_endian_blk0>(context, data, len);
|
||||
#else
|
||||
// select different functions depending on endianess
|
||||
// and figure out the endianess runtime
|
||||
if (is_big_endian())
|
||||
internal_update<big_endian_blk0>(context, data, len);
|
||||
else
|
||||
internal_update<little_endian_blk0>(context, data, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Add padding and return the message digest.
|
||||
|
||||
void SHA1Final(SHA1_CTX* context, uint8_t* digest)
|
||||
{
|
||||
uint8_t finalcount[8];
|
||||
|
||||
for (uint32_t i = 0; i < 8; ++i)
|
||||
{
|
||||
// Endian independent
|
||||
finalcount[i] = static_cast<uint8_t>(
|
||||
(context->count[(i >= 4 ? 0 : 1)]
|
||||
>> ((3-(i & 3)) * 8) ) & 255);
|
||||
}
|
||||
|
||||
SHA1Update(context, (uint8_t const*)"\200", 1);
|
||||
while ((context->count[0] & 504) != 448)
|
||||
SHA1Update(context, (uint8_t const*)"\0", 1);
|
||||
SHA1Update(context, finalcount, 8); // Should cause a SHA1Transform()
|
||||
|
||||
for (uint32_t i = 0; i < 20; ++i)
|
||||
{
|
||||
digest[i] = static_cast<unsigned char>(
|
||||
(context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
|
||||
-----------------
|
||||
Modified 7/98
|
||||
By James H. Brown <jbrown@burgoyne.com>
|
||||
Still 100% Public Domain
|
||||
|
||||
Corrected a problem which generated improper hash values on 16 bit machines
|
||||
Routine SHA1Update changed from
|
||||
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
|
||||
len)
|
||||
to
|
||||
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
|
||||
long len)
|
||||
|
||||
The 'len' parameter was declared an int which works fine on 32 bit machines.
|
||||
However, on 16 bit machines an int is too small for the shifts being done
|
||||
against
|
||||
it. This caused the hash function to generate incorrect values if len was
|
||||
greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
|
||||
|
||||
Since the file IO in main() reads 16K at a time, any file 8K or larger would
|
||||
be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
|
||||
"a"s).
|
||||
|
||||
I also changed the declaration of variables i & j in SHA1Update to
|
||||
unsigned long from unsigned int for the same reason.
|
||||
|
||||
These changes should make no difference to any 32 bit implementations since
|
||||
an
|
||||
int and a long are the same size in those environments.
|
||||
|
||||
--
|
||||
I also corrected a few compiler warnings generated by Borland C.
|
||||
1. Added #include <process.h> for exit() prototype
|
||||
2. Removed unused variable 'j' in SHA1Final
|
||||
3. Changed exit(0) to return(0) at end of main.
|
||||
|
||||
ALL changes I made can be located by searching for comments containing 'JHB'
|
||||
-----------------
|
||||
Modified 8/98
|
||||
By Steve Reid <sreid@sea-to-sky.net>
|
||||
Still 100% public domain
|
||||
|
||||
1- Removed #include <process.h> and used return() instead of exit()
|
||||
2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
|
||||
3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
|
||||
|
||||
-----------------
|
||||
Modified 4/01
|
||||
By Saul Kravitz <Saul.Kravitz@celera.com>
|
||||
Still 100% PD
|
||||
Modified to run on Compaq Alpha hardware.
|
||||
|
||||
-----------------
|
||||
Converted to C++ 6/04
|
||||
By Arvid Norberg <arvidn@sourceforge.net>
|
||||
1- made the input buffer const, and made the
|
||||
previous SHA1HANDSOFF implicit
|
||||
2- uses C99 types with size guarantees
|
||||
from boost
|
||||
3- if none of __BIG_ENDIAN__ or LITTLE_ENDIAN
|
||||
are defined, endianess is determined
|
||||
at runtime. templates are used to duplicate
|
||||
the transform function for each endianess
|
||||
4- using anonymous namespace to avoid external
|
||||
linkage on internal functions
|
||||
5- using standard C++ includes
|
||||
|
||||
still 100% PD
|
||||
*/
|
||||
|
||||
/*
|
||||
Test Vectors (from FIPS PUB 180-1)
|
||||
"abc"
|
||||
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
||||
A million repetitions of "a"
|
||||
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
||||
*/
|
||||
91
cpp/stat.cpp
Executable file
91
cpp/stat.cpp
Executable file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2003, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
// TODO: Use two algorithms to estimate transfer rate.
|
||||
// one (simple) for transfer rates that are >= 1 packet
|
||||
// per second and one (low pass-filter) for rates < 1
|
||||
// packet per second.
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "libtorrent/stat.hpp"
|
||||
#include "libtorrent/invariant_check.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
#if defined _MSC_VER && _MSC_VER <= 1200
|
||||
#define for if (false) {} else for
|
||||
#endif
|
||||
|
||||
using namespace libtorrent;
|
||||
|
||||
void libtorrent::stat::second_tick(float tick_interval)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
for (int i = history - 2; i >= 0; --i)
|
||||
{
|
||||
m_download_rate_history[i + 1] = m_download_rate_history[i];
|
||||
m_upload_rate_history[i + 1] = m_upload_rate_history[i];
|
||||
m_download_payload_rate_history[i + 1] = m_download_payload_rate_history[i];
|
||||
m_upload_payload_rate_history[i + 1] = m_upload_payload_rate_history[i];
|
||||
}
|
||||
|
||||
m_download_rate_history[0] = (m_downloaded_payload + m_downloaded_protocol)
|
||||
/ tick_interval;
|
||||
m_upload_rate_history[0] = (m_uploaded_payload + m_uploaded_protocol)
|
||||
/ tick_interval;
|
||||
m_download_payload_rate_history[0] = m_downloaded_payload / tick_interval;
|
||||
m_upload_payload_rate_history[0] = m_uploaded_payload / tick_interval;
|
||||
|
||||
m_downloaded_payload = 0;
|
||||
m_uploaded_payload = 0;
|
||||
m_downloaded_protocol = 0;
|
||||
m_uploaded_protocol = 0;
|
||||
|
||||
m_mean_download_rate = 0;
|
||||
m_mean_upload_rate = 0;
|
||||
m_mean_download_payload_rate = 0;
|
||||
m_mean_upload_payload_rate = 0;
|
||||
|
||||
for (int i = 0; i < history; ++i)
|
||||
{
|
||||
m_mean_download_rate += m_download_rate_history[i];
|
||||
m_mean_upload_rate += m_upload_rate_history[i];
|
||||
m_mean_download_payload_rate += m_download_payload_rate_history[i];
|
||||
m_mean_upload_payload_rate += m_upload_payload_rate_history[i];
|
||||
}
|
||||
|
||||
m_mean_download_rate /= history;
|
||||
m_mean_upload_rate /= history;
|
||||
m_mean_download_payload_rate /= history;
|
||||
m_mean_upload_payload_rate /= history;
|
||||
}
|
||||
2172
cpp/storage.cpp
Executable file
2172
cpp/storage.cpp
Executable file
File diff suppressed because it is too large
Load Diff
2186
cpp/torrent.cpp
Executable file
2186
cpp/torrent.cpp
Executable file
File diff suppressed because it is too large
Load Diff
729
cpp/torrent_handle.cpp
Executable file
729
cpp/torrent_handle.cpp
Executable file
@ -0,0 +1,729 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2003, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 1)
|
||||
#endif
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/filesystem/convenience.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "libtorrent/peer_id.hpp"
|
||||
#include "libtorrent/bt_peer_connection.hpp"
|
||||
#include "libtorrent/torrent_info.hpp"
|
||||
#include "libtorrent/tracker_manager.hpp"
|
||||
#include "libtorrent/bencode.hpp"
|
||||
#include "libtorrent/hasher.hpp"
|
||||
#include "libtorrent/entry.hpp"
|
||||
#include "libtorrent/session.hpp"
|
||||
#include "libtorrent/aux_/session_impl.hpp"
|
||||
#include "libtorrent/invariant_check.hpp"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1300
|
||||
namespace std
|
||||
{
|
||||
using ::srand;
|
||||
using ::isalnum;
|
||||
};
|
||||
#endif
|
||||
|
||||
using boost::bind;
|
||||
using boost::mutex;
|
||||
using libtorrent::aux::session_impl;
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
namespace
|
||||
{
|
||||
void throw_invalid_handle()
|
||||
{
|
||||
throw invalid_handle();
|
||||
}
|
||||
|
||||
template<class Ret, class F>
|
||||
Ret call_member(
|
||||
session_impl* ses
|
||||
, aux::checker_impl* chk
|
||||
, sha1_hash const& hash
|
||||
, F f)
|
||||
{
|
||||
if (ses == 0) throw_invalid_handle();
|
||||
|
||||
if (chk)
|
||||
{
|
||||
mutex::scoped_lock l(chk->m_mutex);
|
||||
aux::piece_checker_data* d = chk->find_torrent(hash);
|
||||
if (d != 0) return f(*d->torrent_ptr);
|
||||
}
|
||||
|
||||
{
|
||||
session_impl::mutex_t::scoped_lock l(ses->m_mutex);
|
||||
boost::shared_ptr<torrent> t = ses->find_torrent(hash).lock();
|
||||
if (t) return f(*t);
|
||||
}
|
||||
|
||||
throw invalid_handle();
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
void torrent_handle::check_invariant() const
|
||||
{
|
||||
assert((m_ses == 0 && m_chk == 0) || (m_ses != 0));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void torrent_handle::set_max_uploads(int max_uploads) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert(max_uploads >= 2 || max_uploads == -1);
|
||||
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::set_max_uploads, _1, max_uploads));
|
||||
}
|
||||
|
||||
void torrent_handle::use_interface(const char* net_interface) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::use_interface, _1, net_interface));
|
||||
}
|
||||
|
||||
void torrent_handle::set_max_connections(int max_connections) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert(max_connections >= 2 || max_connections == -1);
|
||||
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::set_max_connections, _1, max_connections));
|
||||
}
|
||||
|
||||
void torrent_handle::set_peer_upload_limit(tcp::endpoint ip, int limit) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
assert(limit >= -1);
|
||||
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::set_peer_upload_limit, _1, ip, limit));
|
||||
}
|
||||
|
||||
void torrent_handle::set_peer_download_limit(tcp::endpoint ip, int limit) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
assert(limit >= -1);
|
||||
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::set_peer_download_limit, _1, ip, limit));
|
||||
}
|
||||
|
||||
void torrent_handle::set_upload_limit(int limit) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert(limit >= -1);
|
||||
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::set_upload_limit, _1, limit));
|
||||
}
|
||||
|
||||
void torrent_handle::set_download_limit(int limit) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert(limit >= -1);
|
||||
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::set_download_limit, _1, limit));
|
||||
}
|
||||
|
||||
bool torrent_handle::move_storage(
|
||||
boost::filesystem::path const& save_path) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
return call_member<bool>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::move_storage, _1, save_path));
|
||||
}
|
||||
|
||||
bool torrent_handle::has_metadata() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
return call_member<bool>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::valid_metadata, _1));
|
||||
}
|
||||
|
||||
bool torrent_handle::is_seed() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
return call_member<bool>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::is_seed, _1));
|
||||
}
|
||||
|
||||
bool torrent_handle::is_paused() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
return call_member<bool>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::is_paused, _1));
|
||||
}
|
||||
|
||||
void torrent_handle::pause() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::pause, _1));
|
||||
}
|
||||
|
||||
void torrent_handle::resume() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::resume, _1));
|
||||
}
|
||||
|
||||
void torrent_handle::set_tracker_login(std::string const& name
|
||||
, std::string const& password) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::set_tracker_login, _1, name, password));
|
||||
}
|
||||
|
||||
void torrent_handle::file_progress(std::vector<float>& progress)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (m_ses == 0) throw_invalid_handle();
|
||||
|
||||
if (m_chk)
|
||||
{
|
||||
mutex::scoped_lock l(m_chk->m_mutex);
|
||||
|
||||
aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
|
||||
if (d != 0)
|
||||
{
|
||||
if (!d->processing)
|
||||
{
|
||||
torrent_info const& info = d->torrent_ptr->torrent_file();
|
||||
progress.clear();
|
||||
progress.resize(info.num_files(), 0.f);
|
||||
return;
|
||||
}
|
||||
d->torrent_ptr->file_progress(progress);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
|
||||
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
|
||||
if (t) return t->file_progress(progress);
|
||||
}
|
||||
|
||||
throw_invalid_handle();
|
||||
}
|
||||
|
||||
torrent_status torrent_handle::status() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (m_ses == 0) throw_invalid_handle();
|
||||
|
||||
if (m_chk)
|
||||
{
|
||||
mutex::scoped_lock l(m_chk->m_mutex);
|
||||
|
||||
aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
|
||||
if (d != 0)
|
||||
{
|
||||
torrent_status st;
|
||||
|
||||
if (d->processing)
|
||||
{
|
||||
if (d->torrent_ptr->is_allocating())
|
||||
st.state = torrent_status::allocating;
|
||||
else
|
||||
st.state = torrent_status::checking_files;
|
||||
}
|
||||
else
|
||||
st.state = torrent_status::queued_for_checking;
|
||||
st.progress = d->progress;
|
||||
st.paused = d->torrent_ptr->is_paused();
|
||||
return st;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
|
||||
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
|
||||
if (t) return t->status();
|
||||
}
|
||||
|
||||
throw_invalid_handle();
|
||||
return torrent_status();
|
||||
}
|
||||
|
||||
void torrent_handle::set_sequenced_download_threshold(int threshold) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::set_sequenced_download_threshold, _1, threshold));
|
||||
}
|
||||
|
||||
void torrent_handle::filter_piece(int index, bool filter) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::filter_piece, _1, index, filter));
|
||||
}
|
||||
|
||||
void torrent_handle::filter_pieces(std::vector<bool> const& pieces) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::filter_pieces, _1, pieces));
|
||||
}
|
||||
|
||||
bool torrent_handle::is_piece_filtered(int index) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
return call_member<bool>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::is_piece_filtered, _1, index));
|
||||
}
|
||||
|
||||
std::vector<bool> torrent_handle::filtered_pieces() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
std::vector<bool> ret;
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::filtered_pieces, _1, boost::ref(ret)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void torrent_handle::filter_files(std::vector<bool> const& files) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::filter_files, _1, files));
|
||||
}
|
||||
|
||||
std::vector<announce_entry> const& torrent_handle::trackers() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
return call_member<std::vector<announce_entry> const&>(m_ses
|
||||
, m_chk, m_info_hash, bind(&torrent::trackers, _1));
|
||||
}
|
||||
|
||||
void torrent_handle::add_url_seed(std::string const& url)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
return call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::add_url_seed, _1, url));
|
||||
}
|
||||
|
||||
void torrent_handle::replace_trackers(
|
||||
std::vector<announce_entry> const& urls) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::replace_trackers, _1, urls));
|
||||
}
|
||||
|
||||
const torrent_info& torrent_handle::get_torrent_info() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (!has_metadata()) throw_invalid_handle();
|
||||
return call_member<torrent_info const&>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::torrent_file, _1));
|
||||
}
|
||||
|
||||
bool torrent_handle::is_valid() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (m_ses == 0) return false;
|
||||
|
||||
if (m_chk)
|
||||
{
|
||||
mutex::scoped_lock l(m_chk->m_mutex);
|
||||
aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
|
||||
if (d != 0) return true;
|
||||
}
|
||||
|
||||
{
|
||||
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
|
||||
boost::weak_ptr<torrent> t = m_ses->find_torrent(m_info_hash);
|
||||
if (!t.expired()) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
entry torrent_handle::write_resume_data() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
std::vector<int> piece_index;
|
||||
if (m_ses == 0) return entry();
|
||||
|
||||
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
|
||||
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
|
||||
if (!t) return entry();
|
||||
|
||||
if (!t->valid_metadata()) return entry();
|
||||
|
||||
t->filesystem().export_piece_map(piece_index);
|
||||
|
||||
entry ret(entry::dictionary_t);
|
||||
|
||||
ret["file-format"] = "libtorrent resume file";
|
||||
ret["file-version"] = 1;
|
||||
|
||||
const sha1_hash& info_hash = t->torrent_file().info_hash();
|
||||
ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end());
|
||||
|
||||
ret["slots"] = entry(entry::list_t);
|
||||
entry::list_type& slots = ret["slots"].list();
|
||||
std::copy(piece_index.begin(), piece_index.end(), std::back_inserter(slots));
|
||||
|
||||
const piece_picker& p = t->picker();
|
||||
|
||||
const std::vector<piece_picker::downloading_piece>& q
|
||||
= p.get_download_queue();
|
||||
|
||||
// blocks per piece
|
||||
int num_blocks_per_piece =
|
||||
static_cast<int>(t->torrent_file().piece_length()) / t->block_size();
|
||||
ret["blocks per piece"] = num_blocks_per_piece;
|
||||
|
||||
// unfinished pieces
|
||||
ret["unfinished"] = entry::list_type();
|
||||
entry::list_type& up = ret["unfinished"].list();
|
||||
|
||||
// info for each unfinished piece
|
||||
for (std::vector<piece_picker::downloading_piece>::const_iterator i
|
||||
= q.begin(); i != q.end(); ++i)
|
||||
{
|
||||
if (i->finished_blocks.count() == 0) continue;
|
||||
|
||||
entry piece_struct(entry::dictionary_t);
|
||||
|
||||
// the unfinished piece's index
|
||||
piece_struct["piece"] = i->index;
|
||||
|
||||
std::string bitmask;
|
||||
const int num_bitmask_bytes
|
||||
= std::max(num_blocks_per_piece / 8, 1);
|
||||
|
||||
for (int j = 0; j < num_bitmask_bytes; ++j)
|
||||
{
|
||||
unsigned char v = 0;
|
||||
for (int k = 0; k < 8; ++k)
|
||||
v |= i->finished_blocks[j*8+k]?(1 << k):0;
|
||||
bitmask.insert(bitmask.end(), v);
|
||||
}
|
||||
piece_struct["bitmask"] = bitmask;
|
||||
|
||||
assert(t->filesystem().slot_for_piece(i->index) >= 0);
|
||||
unsigned long adler
|
||||
= t->filesystem().piece_crc(
|
||||
t->filesystem().slot_for_piece(i->index)
|
||||
, t->block_size()
|
||||
, i->finished_blocks);
|
||||
|
||||
piece_struct["adler32"] = adler;
|
||||
|
||||
// push the struct onto the unfinished-piece list
|
||||
up.push_back(piece_struct);
|
||||
}
|
||||
|
||||
// write local peers
|
||||
|
||||
ret["peers"] = entry::list_type();
|
||||
entry::list_type& peer_list = ret["peers"].list();
|
||||
|
||||
policy& pol = t->get_policy();
|
||||
|
||||
for (policy::iterator i = pol.begin_peer()
|
||||
, end(pol.end_peer()); i != end; ++i)
|
||||
{
|
||||
// we cannot save remote connection
|
||||
// since we don't know their listen port
|
||||
// unless they gave us their listen port
|
||||
// through the extension handshake
|
||||
// so, if the peer is not connectable (i.e. we
|
||||
// don't know its listen port) or if it has
|
||||
// been banned, don't save it.
|
||||
if (i->type == policy::peer::not_connectable
|
||||
|| i->banned) continue;
|
||||
|
||||
tcp::endpoint ip = i->ip;
|
||||
entry peer(entry::dictionary_t);
|
||||
peer["ip"] = ip.address().to_string();
|
||||
peer["port"] = ip.port();
|
||||
peer_list.push_back(peer);
|
||||
}
|
||||
|
||||
std::vector<std::pair<size_type, std::time_t> > file_sizes
|
||||
= get_filesizes(t->torrent_file(), t->save_path());
|
||||
|
||||
ret["file sizes"] = entry::list_type();
|
||||
entry::list_type& fl = ret["file sizes"].list();
|
||||
for (std::vector<std::pair<size_type, std::time_t> >::iterator i
|
||||
= file_sizes.begin(), end(file_sizes.end()); i != end; ++i)
|
||||
{
|
||||
entry::list_type p;
|
||||
p.push_back(entry(i->first));
|
||||
p.push_back(entry(i->second));
|
||||
fl.push_back(entry(p));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
boost::filesystem::path torrent_handle::save_path() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
return call_member<boost::filesystem::path>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::save_path, _1));
|
||||
}
|
||||
|
||||
std::vector<char> const& torrent_handle::metadata() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
return call_member<std::vector<char> const&>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::metadata, _1));
|
||||
}
|
||||
|
||||
void torrent_handle::connect_peer(tcp::endpoint const& adr) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (m_ses == 0) throw_invalid_handle();
|
||||
|
||||
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
|
||||
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
|
||||
|
||||
if (!t)
|
||||
{
|
||||
// the torrent is being checked. Add the peer to its
|
||||
// peer list. The entries in there will be connected
|
||||
// once the checking is complete.
|
||||
mutex::scoped_lock l2(m_chk->m_mutex);
|
||||
|
||||
aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
|
||||
if (d == 0) throw_invalid_handle();
|
||||
d->peers.push_back(adr);
|
||||
return;
|
||||
}
|
||||
|
||||
peer_id id;
|
||||
std::fill(id.begin(), id.end(), 0);
|
||||
t->get_policy().peer_from_tracker(adr, id);
|
||||
}
|
||||
|
||||
void torrent_handle::force_reannounce(
|
||||
boost::posix_time::time_duration duration) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (m_ses == 0) throw_invalid_handle();
|
||||
|
||||
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
|
||||
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
|
||||
if (!t) throw_invalid_handle();
|
||||
|
||||
using boost::posix_time::second_clock;
|
||||
t->force_tracker_request(second_clock::universal_time()
|
||||
+ duration);
|
||||
}
|
||||
|
||||
void torrent_handle::force_reannounce() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (m_ses == 0) throw_invalid_handle();
|
||||
|
||||
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
|
||||
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
|
||||
if (!t) throw_invalid_handle();
|
||||
|
||||
t->force_tracker_request();
|
||||
}
|
||||
|
||||
void torrent_handle::set_ratio(float ratio) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert(ratio >= 0.f);
|
||||
|
||||
if (ratio < 1.f && ratio > 0.f)
|
||||
ratio = 1.f;
|
||||
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::set_ratio, _1, ratio));
|
||||
}
|
||||
|
||||
void torrent_handle::get_peer_info(std::vector<peer_info>& v) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
v.clear();
|
||||
if (m_ses == 0) throw_invalid_handle();
|
||||
|
||||
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
|
||||
|
||||
boost::shared_ptr<const torrent> t = m_ses->find_torrent(m_info_hash).lock();
|
||||
if (!t) return;
|
||||
|
||||
for (torrent::const_peer_iterator i = t->begin();
|
||||
i != t->end(); ++i)
|
||||
{
|
||||
peer_connection* peer = i->second;
|
||||
|
||||
// peers that haven't finished the handshake should
|
||||
// not be included in this list
|
||||
if (peer->associated_torrent().expired()) continue;
|
||||
|
||||
v.push_back(peer_info());
|
||||
peer_info& p = v.back();
|
||||
|
||||
peer->get_peer_info(p);
|
||||
}
|
||||
}
|
||||
|
||||
bool torrent_handle::send_chat_message(tcp::endpoint ip, std::string message) const
|
||||
{
|
||||
if (m_ses == 0) throw_invalid_handle();
|
||||
|
||||
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
|
||||
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
|
||||
if (!t) return false;
|
||||
|
||||
for (torrent::const_peer_iterator i = t->begin();
|
||||
i != t->end(); ++i)
|
||||
{
|
||||
peer_connection* peer = i->second;
|
||||
|
||||
// peers that haven't finished the handshake should
|
||||
// not be included in this list
|
||||
if (peer->associated_torrent().expired()) continue;
|
||||
|
||||
tcp::endpoint sender = peer->get_socket()->remote_endpoint();
|
||||
// loop until we find the required ip tcp::endpoint
|
||||
if (ip != sender) continue;
|
||||
|
||||
bt_peer_connection* p = dynamic_cast<bt_peer_connection*>(peer);
|
||||
if (!p) return false;
|
||||
|
||||
// peers that don's support chat message extension
|
||||
// should not be included either
|
||||
if (!p->supports_extension(extended_chat_message))
|
||||
return false;
|
||||
|
||||
// send the message
|
||||
p->write_chat_message(message);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void torrent_handle::get_download_queue(std::vector<partial_piece_info>& queue) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (m_ses == 0) throw_invalid_handle();
|
||||
|
||||
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
|
||||
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
|
||||
|
||||
queue.clear();
|
||||
if (!t) return;
|
||||
if (!t->valid_metadata()) return;
|
||||
|
||||
const piece_picker& p = t->picker();
|
||||
|
||||
const std::vector<piece_picker::downloading_piece>& q
|
||||
= p.get_download_queue();
|
||||
|
||||
for (std::vector<piece_picker::downloading_piece>::const_iterator i
|
||||
= q.begin(); i != q.end(); ++i)
|
||||
{
|
||||
partial_piece_info pi;
|
||||
pi.finished_blocks = i->finished_blocks;
|
||||
pi.requested_blocks = i->requested_blocks;
|
||||
for (int j = 0; j < partial_piece_info::max_blocks_per_piece; ++j)
|
||||
{
|
||||
pi.peer[j] = i->info[j].peer;
|
||||
pi.num_downloads[j] = i->info[j].num_downloads;
|
||||
}
|
||||
pi.piece_index = i->index;
|
||||
pi.blocks_in_piece = p.blocks_in_piece(i->index);
|
||||
queue.push_back(pi);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
833
cpp/torrent_info.cpp
Executable file
833
cpp/torrent_info.cpp
Executable file
@ -0,0 +1,833 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2003, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 1)
|
||||
#endif
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/date_time/gregorian/gregorian_types.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/next_prior.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "libtorrent/torrent_info.hpp"
|
||||
#include "libtorrent/bencode.hpp"
|
||||
#include "libtorrent/hasher.hpp"
|
||||
#include "libtorrent/entry.hpp"
|
||||
|
||||
using namespace libtorrent;
|
||||
using namespace boost::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
void convert_to_utf8(std::string& str, unsigned char chr)
|
||||
{
|
||||
str += 0xc0 | ((chr & 0xff) >> 6);
|
||||
str += 0x80 | (chr & 0x3f);
|
||||
}
|
||||
|
||||
void verify_encoding(file_entry& target)
|
||||
{
|
||||
std::string tmp_path;
|
||||
std::string file_path = target.path.string();
|
||||
bool valid_encoding = true;
|
||||
for (std::string::iterator i = file_path.begin()
|
||||
, end(file_path.end()); i != end; ++i)
|
||||
{
|
||||
// valid ascii-character
|
||||
if ((*i & 0x80) == 0)
|
||||
{
|
||||
tmp_path += *i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::distance(i, end) < 2)
|
||||
{
|
||||
convert_to_utf8(tmp_path, *i);
|
||||
valid_encoding = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// valid 2-byte utf-8 character
|
||||
if ((i[0] & 0xe0) == 0xc0
|
||||
&& (i[1] & 0xc0) == 0x80)
|
||||
{
|
||||
tmp_path += i[0];
|
||||
tmp_path += i[1];
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::distance(i, end) < 3)
|
||||
{
|
||||
convert_to_utf8(tmp_path, *i);
|
||||
valid_encoding = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// valid 3-byte utf-8 character
|
||||
if ((i[0] & 0xf0) == 0xe0
|
||||
&& (i[1] & 0xc0) == 0x80
|
||||
&& (i[2] & 0xc0) == 0x80)
|
||||
{
|
||||
tmp_path += i[0];
|
||||
tmp_path += i[1];
|
||||
tmp_path += i[2];
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::distance(i, end) < 4)
|
||||
{
|
||||
convert_to_utf8(tmp_path, *i);
|
||||
valid_encoding = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// valid 4-byte utf-8 character
|
||||
if ((i[0] & 0xf0) == 0xe0
|
||||
&& (i[1] & 0xc0) == 0x80
|
||||
&& (i[2] & 0xc0) == 0x80
|
||||
&& (i[3] & 0xc0) == 0x80)
|
||||
{
|
||||
tmp_path += i[0];
|
||||
tmp_path += i[1];
|
||||
tmp_path += i[2];
|
||||
tmp_path += i[3];
|
||||
i += 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
convert_to_utf8(tmp_path, *i);
|
||||
valid_encoding = false;
|
||||
}
|
||||
// the encoding was not valid utf-8
|
||||
// save the original encoding and replace the
|
||||
// commonly used path with the correctly
|
||||
// encoded string
|
||||
if (!valid_encoding)
|
||||
{
|
||||
target.orig_path.reset(new path(target.path));
|
||||
target.path = tmp_path;
|
||||
}
|
||||
}
|
||||
|
||||
void extract_single_file(const entry& dict, file_entry& target
|
||||
, std::string const& root_dir)
|
||||
{
|
||||
target.size = dict["length"].integer();
|
||||
target.path = root_dir;
|
||||
|
||||
|
||||
// prefer the name.utf-8
|
||||
// because if it exists, it is more
|
||||
// likely to be correctly encoded
|
||||
|
||||
const entry::list_type* list = 0;
|
||||
if (entry const* p = dict.find_key("path.utf-8"))
|
||||
{
|
||||
list = &p->list();
|
||||
}
|
||||
else
|
||||
{
|
||||
list = &dict["path"].list();
|
||||
}
|
||||
|
||||
for (entry::list_type::const_iterator i = list->begin();
|
||||
i != list->end(); ++i)
|
||||
{
|
||||
if (i->string() != "..")
|
||||
target.path /= i->string();
|
||||
}
|
||||
verify_encoding(target);
|
||||
if (target.path.is_complete()) throw std::runtime_error("torrent contains "
|
||||
"a file with an absolute path: '"
|
||||
+ target.path.native_file_string() + "'");
|
||||
}
|
||||
|
||||
void extract_files(const entry::list_type& list, std::vector<file_entry>& target
|
||||
, std::string const& root_dir)
|
||||
{
|
||||
size_type offset = 0;
|
||||
for (entry::list_type::const_iterator i = list.begin(); i != list.end(); ++i)
|
||||
{
|
||||
target.push_back(file_entry());
|
||||
extract_single_file(*i, target.back(), root_dir);
|
||||
target.back().offset = offset;
|
||||
offset += target.back().size;
|
||||
}
|
||||
}
|
||||
|
||||
void remove_dir(path& p)
|
||||
{
|
||||
assert(p.begin() != p.end());
|
||||
path tmp;
|
||||
for (path::iterator i = boost::next(p.begin()); i != p.end(); ++i)
|
||||
tmp /= *i;
|
||||
p = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
using namespace boost::gregorian;
|
||||
using namespace boost::posix_time;
|
||||
|
||||
// standard constructor that parses a torrent file
|
||||
torrent_info::torrent_info(const entry& torrent_file)
|
||||
: m_creation_date(date(not_a_date_time))
|
||||
, m_multifile(false)
|
||||
, m_private(false)
|
||||
, m_extra_info(entry::dictionary_t)
|
||||
{
|
||||
try
|
||||
{
|
||||
read_torrent_info(torrent_file);
|
||||
}
|
||||
catch(type_error&)
|
||||
{
|
||||
throw invalid_torrent_file();
|
||||
}
|
||||
}
|
||||
|
||||
// constructor used for creating new torrents
|
||||
// will not contain any hashes, comments, creation date
|
||||
// just the necessary to use it with piece manager
|
||||
// used for torrents with no metadata
|
||||
torrent_info::torrent_info(sha1_hash const& info_hash)
|
||||
: m_piece_length(256 * 1024)
|
||||
, m_total_size(0)
|
||||
, m_info_hash(info_hash)
|
||||
, m_name()
|
||||
, m_creation_date(second_clock::universal_time())
|
||||
, m_multifile(false)
|
||||
, m_extra_info(entry::dictionary_t)
|
||||
{
|
||||
}
|
||||
|
||||
torrent_info::torrent_info()
|
||||
: m_piece_length(256 * 1024)
|
||||
, m_total_size(0)
|
||||
, m_info_hash(0)
|
||||
, m_name()
|
||||
, m_creation_date(second_clock::universal_time())
|
||||
, m_multifile(false)
|
||||
, m_extra_info(entry::dictionary_t)
|
||||
{
|
||||
}
|
||||
|
||||
torrent_info::~torrent_info()
|
||||
{}
|
||||
|
||||
void torrent_info::set_piece_size(int size)
|
||||
{
|
||||
// make sure the size is an even power of 2
|
||||
#ifndef NDEBUG
|
||||
for (int i = 0; i < 32; ++i)
|
||||
{
|
||||
if (size & (1 << i))
|
||||
{
|
||||
assert((size & ~(1 << i)) == 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
m_piece_length = size;
|
||||
|
||||
|
||||
int num_pieces = static_cast<int>(
|
||||
(m_total_size + m_piece_length - 1) / m_piece_length);
|
||||
int old_num_pieces = static_cast<int>(m_piece_hash.size());
|
||||
|
||||
m_piece_hash.resize(num_pieces);
|
||||
for (int i = old_num_pieces; i < num_pieces; ++i)
|
||||
{
|
||||
m_piece_hash[i].clear();
|
||||
}
|
||||
}
|
||||
|
||||
void torrent_info::parse_info_section(entry const& info)
|
||||
{
|
||||
// encode the info-field in order to calculate it's sha1-hash
|
||||
std::vector<char> buf;
|
||||
bencode(std::back_inserter(buf), info);
|
||||
hasher h;
|
||||
h.update(&buf[0], (int)buf.size());
|
||||
m_info_hash = h.final();
|
||||
|
||||
// extract piece length
|
||||
m_piece_length = (int)info["piece length"].integer();
|
||||
if (m_piece_length <= 0) throw std::runtime_error("invalid torrent. piece length <= 0");
|
||||
|
||||
// extract file name (or the directory name if it's a multifile libtorrent)
|
||||
if (entry const* e = info.find_key("name.utf-8"))
|
||||
{ m_name = e->string(); }
|
||||
else
|
||||
{ m_name = info["name"].string(); }
|
||||
|
||||
path tmp = m_name;
|
||||
if (tmp.is_complete()) throw std::runtime_error("torrent contains "
|
||||
"a file with an absolute path: '" + m_name + "'");
|
||||
if (tmp.has_branch_path()) throw std::runtime_error(
|
||||
"torrent contains name with directories: '" + m_name + "'");
|
||||
|
||||
// extract file list
|
||||
entry const* i = info.find_key("files");
|
||||
if (i == 0)
|
||||
{
|
||||
// if there's no list of files, there has to be a length
|
||||
// field.
|
||||
file_entry e;
|
||||
e.path = m_name;
|
||||
e.offset = 0;
|
||||
e.size = info["length"].integer();
|
||||
m_files.push_back(e);
|
||||
}
|
||||
else
|
||||
{
|
||||
extract_files(i->list(), m_files, m_name);
|
||||
m_multifile = true;
|
||||
}
|
||||
|
||||
// calculate total size of all pieces
|
||||
m_total_size = 0;
|
||||
for (std::vector<file_entry>::iterator i = m_files.begin(); i != m_files.end(); ++i)
|
||||
m_total_size += i->size;
|
||||
|
||||
// extract sha-1 hashes for all pieces
|
||||
// we want this division to round upwards, that's why we have the
|
||||
// extra addition
|
||||
|
||||
int num_pieces = static_cast<int>((m_total_size + m_piece_length - 1) / m_piece_length);
|
||||
m_piece_hash.resize(num_pieces);
|
||||
const std::string& hash_string = info["pieces"].string();
|
||||
|
||||
if ((int)hash_string.length() != num_pieces * 20)
|
||||
throw invalid_torrent_file();
|
||||
|
||||
for (int i = 0; i < num_pieces; ++i)
|
||||
std::copy(
|
||||
hash_string.begin() + i*20
|
||||
, hash_string.begin() + (i+1)*20
|
||||
, m_piece_hash[i].begin());
|
||||
|
||||
for (entry::dictionary_type::const_iterator i = info.dict().begin()
|
||||
, end(info.dict().end()); i != end; ++i)
|
||||
{
|
||||
if (i->first == "pieces"
|
||||
|| i->first == "piece length"
|
||||
|| i->first == "length")
|
||||
continue;
|
||||
m_extra_info[i->first] = i->second;
|
||||
}
|
||||
|
||||
if (entry const* priv = info.find_key("private"))
|
||||
{
|
||||
if (priv->type() != entry::int_t
|
||||
|| priv->integer() != 0)
|
||||
{
|
||||
// this key exists and it's not 0.
|
||||
// consider the torrent private
|
||||
m_private = true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
std::vector<char> info_section_buf;
|
||||
entry gen_info_section = create_info_metadata();
|
||||
bencode(std::back_inserter(info_section_buf), gen_info_section);
|
||||
assert(hasher(&info_section_buf[0], info_section_buf.size()).final()
|
||||
== m_info_hash);
|
||||
#endif
|
||||
}
|
||||
|
||||
// extracts information from a libtorrent file and fills in the structures in
|
||||
// the torrent object
|
||||
void torrent_info::read_torrent_info(const entry& torrent_file)
|
||||
{
|
||||
// extract the url of the tracker
|
||||
if (entry const* i = torrent_file.find_key("announce-list"))
|
||||
{
|
||||
const entry::list_type& l = i->list();
|
||||
for (entry::list_type::const_iterator j = l.begin(); j != l.end(); ++j)
|
||||
{
|
||||
const entry::list_type& ll = j->list();
|
||||
for (entry::list_type::const_iterator k = ll.begin(); k != ll.end(); ++k)
|
||||
{
|
||||
announce_entry e(k->string());
|
||||
e.tier = (int)std::distance(l.begin(), j);
|
||||
m_urls.push_back(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_urls.size() == 0)
|
||||
{
|
||||
// the announce-list is empty
|
||||
// fall back to look for announce
|
||||
m_urls.push_back(announce_entry(
|
||||
torrent_file["announce"].string()));
|
||||
}
|
||||
// shuffle each tier
|
||||
std::vector<announce_entry>::iterator start = m_urls.begin();
|
||||
std::vector<announce_entry>::iterator stop;
|
||||
int current_tier = m_urls.front().tier;
|
||||
for (stop = m_urls.begin(); stop != m_urls.end(); ++stop)
|
||||
{
|
||||
if (stop->tier != current_tier)
|
||||
{
|
||||
std::random_shuffle(start, stop);
|
||||
start = stop;
|
||||
current_tier = stop->tier;
|
||||
}
|
||||
}
|
||||
std::random_shuffle(start, stop);
|
||||
}
|
||||
else if (entry const* i = torrent_file.find_key("announce"))
|
||||
{
|
||||
m_urls.push_back(announce_entry(i->string()));
|
||||
}
|
||||
|
||||
if (entry const* i = torrent_file.find_key("nodes"))
|
||||
{
|
||||
entry::list_type const& list = i->list();
|
||||
for (entry::list_type::const_iterator i(list.begin())
|
||||
, end(list.end()); i != end; ++i)
|
||||
{
|
||||
if (i->type() != entry::list_t) continue;
|
||||
entry::list_type const& l = i->list();
|
||||
entry::list_type::const_iterator iter = l.begin();
|
||||
if (l.size() < 1) continue;
|
||||
std::string const& hostname = iter->string();
|
||||
++iter;
|
||||
int port = 6881;
|
||||
if (l.end() != iter) port = iter->integer();
|
||||
m_nodes.push_back(std::make_pair(hostname, port));
|
||||
}
|
||||
}
|
||||
|
||||
// extract creation date
|
||||
try
|
||||
{
|
||||
m_creation_date = ptime(date(1970, Jan, 1))
|
||||
+ seconds(long(torrent_file["creation date"].integer()));
|
||||
}
|
||||
catch (type_error) {}
|
||||
|
||||
// if there are any url-seeds, extract them
|
||||
try
|
||||
{
|
||||
entry const& url_seeds = torrent_file["url-list"];
|
||||
if (url_seeds.type() == entry::string_t)
|
||||
{
|
||||
m_url_seeds.push_back(url_seeds.string());
|
||||
}
|
||||
else if (url_seeds.type() == entry::list_t)
|
||||
{
|
||||
entry::list_type const& l = url_seeds.list();
|
||||
for (entry::list_type::const_iterator i = l.begin();
|
||||
i != l.end(); ++i)
|
||||
{
|
||||
m_url_seeds.push_back(i->string());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (type_error&) {}
|
||||
|
||||
// extract comment
|
||||
if (entry const* e = torrent_file.find_key("comment.utf-8"))
|
||||
{ m_comment = e->string(); }
|
||||
else if (entry const* e = torrent_file.find_key("comment"))
|
||||
{ m_comment = e->string(); }
|
||||
|
||||
if (entry const* e = torrent_file.find_key("created by.utf-8"))
|
||||
{ m_created_by = e->string(); }
|
||||
else if (entry const* e = torrent_file.find_key("created by"))
|
||||
{ m_created_by = e->string(); }
|
||||
|
||||
parse_info_section(torrent_file["info"]);
|
||||
}
|
||||
|
||||
boost::optional<ptime>
|
||||
torrent_info::creation_date() const
|
||||
{
|
||||
if (m_creation_date != ptime(date(not_a_date_time)))
|
||||
{
|
||||
return boost::optional<ptime>(m_creation_date);
|
||||
}
|
||||
return boost::optional<ptime>();
|
||||
}
|
||||
|
||||
void torrent_info::add_tracker(std::string const& url, int tier)
|
||||
{
|
||||
announce_entry e(url);
|
||||
e.tier = tier;
|
||||
m_urls.push_back(e);
|
||||
|
||||
using boost::bind;
|
||||
std::sort(m_urls.begin(), m_urls.end(), boost::bind<bool>(std::less<int>()
|
||||
, bind(&announce_entry::tier, _1), bind(&announce_entry::tier, _2)));
|
||||
}
|
||||
|
||||
void torrent_info::add_file(boost::filesystem::path file, size_type size)
|
||||
{
|
||||
assert(file.begin() != file.end());
|
||||
|
||||
if (!file.has_branch_path())
|
||||
{
|
||||
// you have already added at least one file with a
|
||||
// path to the file (branch_path), which means that
|
||||
// all the other files need to be in the same top
|
||||
// directory as the first file.
|
||||
assert(m_files.empty());
|
||||
assert(!m_multifile);
|
||||
m_name = file.string();
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
if (!m_files.empty())
|
||||
assert(m_name == *file.begin());
|
||||
#endif
|
||||
m_multifile = true;
|
||||
m_name = *file.begin();
|
||||
}
|
||||
|
||||
file_entry e;
|
||||
e.path = file;
|
||||
e.size = size;
|
||||
m_files.push_back(e);
|
||||
|
||||
m_total_size += size;
|
||||
|
||||
int num_pieces = static_cast<int>(
|
||||
(m_total_size + m_piece_length - 1) / m_piece_length);
|
||||
int old_num_pieces = static_cast<int>(m_piece_hash.size());
|
||||
|
||||
m_piece_hash.resize(num_pieces);
|
||||
for (std::vector<sha1_hash>::iterator i = m_piece_hash.begin() + old_num_pieces;
|
||||
i != m_piece_hash.end(); ++i)
|
||||
{
|
||||
i->clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void torrent_info::add_url_seed(std::string const& url)
|
||||
{
|
||||
m_url_seeds.push_back(url);
|
||||
}
|
||||
|
||||
void torrent_info::set_comment(char const* str)
|
||||
{
|
||||
m_comment = str;
|
||||
}
|
||||
|
||||
void torrent_info::set_creator(char const* str)
|
||||
{
|
||||
m_created_by = str;
|
||||
}
|
||||
|
||||
entry torrent_info::create_info_metadata() const
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
// you have to add files to the torrent first
|
||||
assert(!m_files.empty());
|
||||
|
||||
entry info(m_extra_info);
|
||||
|
||||
if (!info.find_key("name"))
|
||||
info["name"] = m_name;
|
||||
|
||||
if (!m_multifile)
|
||||
{
|
||||
info["length"] = m_files.front().size;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!info.find_key("files"))
|
||||
{
|
||||
entry& files = info["files"];
|
||||
files = entry(entry::list_t);
|
||||
|
||||
for (std::vector<file_entry>::const_iterator i = m_files.begin();
|
||||
i != m_files.end(); ++i)
|
||||
{
|
||||
files.list().push_back(entry(entry::dictionary_t));
|
||||
entry& file_e = files.list().back();
|
||||
file_e["length"] = i->size;
|
||||
entry& path_e = file_e["path"];
|
||||
path_e = entry(entry::list_t);
|
||||
|
||||
fs::path const* file_path;
|
||||
if (i->orig_path) file_path = &(*i->orig_path);
|
||||
else file_path = &i->path;
|
||||
assert(file_path->has_branch_path());
|
||||
assert(*file_path->begin() == m_name);
|
||||
|
||||
for (fs::path::iterator j = boost::next(file_path->begin());
|
||||
j != file_path->end(); ++j)
|
||||
{
|
||||
path_e.list().push_back(entry(*j));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info["piece length"] = piece_length();
|
||||
entry& pieces = info["pieces"];
|
||||
pieces = entry(entry::string_t);
|
||||
|
||||
std::string& p = pieces.string();
|
||||
|
||||
for (std::vector<sha1_hash>::const_iterator i = m_piece_hash.begin();
|
||||
i != m_piece_hash.end(); ++i)
|
||||
{
|
||||
p.append((char*)i->begin(), (char*)i->end());
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
entry torrent_info::create_torrent() const
|
||||
{
|
||||
assert(m_piece_length > 0);
|
||||
|
||||
using namespace boost::gregorian;
|
||||
using namespace boost::posix_time;
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
entry dict(entry::dictionary_t);
|
||||
|
||||
if ((m_urls.empty() && m_nodes.empty()) || m_files.empty())
|
||||
{
|
||||
// TODO: throw something here
|
||||
// throw
|
||||
return entry();
|
||||
}
|
||||
|
||||
if (m_private) dict["private"] = 1;
|
||||
|
||||
if (!m_urls.empty())
|
||||
dict["announce"] = m_urls.front().url;
|
||||
|
||||
if (!m_nodes.empty())
|
||||
{
|
||||
entry& nodes = dict["nodes"];
|
||||
nodes = entry(entry::list_t);
|
||||
entry::list_type& nodes_list = nodes.list();
|
||||
for (nodes_t::const_iterator i = m_nodes.begin()
|
||||
, end(m_nodes.end()); i != end; ++i)
|
||||
{
|
||||
entry::list_type node;
|
||||
node.push_back(entry(i->first));
|
||||
node.push_back(entry(i->second));
|
||||
nodes_list.push_back(entry(node));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_urls.size() > 1)
|
||||
{
|
||||
entry trackers(entry::list_t);
|
||||
entry tier(entry::list_t);
|
||||
int current_tier = m_urls.front().tier;
|
||||
for (std::vector<announce_entry>::const_iterator i = m_urls.begin();
|
||||
i != m_urls.end(); ++i)
|
||||
{
|
||||
if (i->tier != current_tier)
|
||||
{
|
||||
current_tier = i->tier;
|
||||
trackers.list().push_back(tier);
|
||||
tier.list().clear();
|
||||
}
|
||||
tier.list().push_back(entry(i->url));
|
||||
}
|
||||
trackers.list().push_back(tier);
|
||||
dict["announce-list"] = trackers;
|
||||
}
|
||||
|
||||
if (!m_comment.empty())
|
||||
dict["comment"] = m_comment;
|
||||
|
||||
dict["creation date"] =
|
||||
(m_creation_date - ptime(date(1970, Jan, 1))).total_seconds();
|
||||
|
||||
if (!m_created_by.empty())
|
||||
dict["created by"] = m_created_by;
|
||||
|
||||
if (!m_url_seeds.empty())
|
||||
{
|
||||
if (m_url_seeds.size() == 1)
|
||||
{
|
||||
dict["url-list"] = m_url_seeds.front();
|
||||
}
|
||||
else
|
||||
{
|
||||
entry& list = dict["url-list"];
|
||||
list = entry(entry::list_t);
|
||||
for (std::vector<std::string>::const_iterator i
|
||||
= m_url_seeds.begin(); i != m_url_seeds.end(); ++i)
|
||||
{
|
||||
list.list().push_back(entry(*i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dict["info"] = create_info_metadata();
|
||||
|
||||
entry const& info_section = dict["info"];
|
||||
std::vector<char> buf;
|
||||
bencode(std::back_inserter(buf), info_section);
|
||||
m_info_hash = hasher(&buf[0], buf.size()).final();
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
void torrent_info::set_hash(int index, const sha1_hash& h)
|
||||
{
|
||||
assert(index >= 0);
|
||||
assert(index < (int)m_piece_hash.size());
|
||||
m_piece_hash[index] = h;
|
||||
}
|
||||
|
||||
void torrent_info::convert_file_names()
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void torrent_info::print(std::ostream& os) const
|
||||
{
|
||||
os << "trackers:\n";
|
||||
for (std::vector<announce_entry>::const_iterator i = trackers().begin();
|
||||
i != trackers().end(); ++i)
|
||||
{
|
||||
os << i->tier << ": " << i->url << "\n";
|
||||
}
|
||||
if (!m_comment.empty())
|
||||
os << "comment: " << m_comment << "\n";
|
||||
if (m_creation_date != ptime(date(not_a_date_time)))
|
||||
os << "creation date: " << to_simple_string(m_creation_date) << "\n";
|
||||
os << "private: " << (m_private?"yes":"no") << "\n";
|
||||
os << "number of pieces: " << num_pieces() << "\n";
|
||||
os << "piece length: " << piece_length() << "\n";
|
||||
os << "files:\n";
|
||||
for (file_iterator i = begin_files(); i != end_files(); ++i)
|
||||
os << " " << std::setw(11) << i->size << " " << i->path.string() << "\n";
|
||||
}
|
||||
|
||||
size_type torrent_info::piece_size(int index) const
|
||||
{
|
||||
assert(index >= 0 && index < num_pieces());
|
||||
if (index == num_pieces()-1)
|
||||
{
|
||||
size_type size = total_size()
|
||||
- (num_pieces() - 1) * piece_length();
|
||||
assert(size > 0);
|
||||
assert(size <= piece_length());
|
||||
return size;
|
||||
}
|
||||
else
|
||||
return piece_length();
|
||||
}
|
||||
|
||||
void torrent_info::add_node(std::pair<std::string, int> const& node)
|
||||
{
|
||||
m_nodes.push_back(node);
|
||||
}
|
||||
|
||||
std::vector<file_slice> torrent_info::map_block(int piece, size_type offset
|
||||
, int size) const
|
||||
{
|
||||
assert(num_files() > 0);
|
||||
std::vector<file_slice> ret;
|
||||
|
||||
size_type start = piece * (size_type)m_piece_length + offset;
|
||||
assert(start + size <= m_total_size);
|
||||
|
||||
// find the file iterator and file offset
|
||||
// TODO: make a vector that can map piece -> file index in O(1)
|
||||
size_type file_offset = start;
|
||||
std::vector<file_entry>::const_iterator file_iter;
|
||||
|
||||
int counter = 0;
|
||||
for (file_iter = begin_files();; ++counter, ++file_iter)
|
||||
{
|
||||
assert(file_iter != end_files());
|
||||
if (file_offset < file_iter->size)
|
||||
{
|
||||
file_slice f;
|
||||
f.file_index = counter;
|
||||
f.offset = file_offset;
|
||||
f.size = (std::min)(file_iter->size - file_offset, (size_type)size);
|
||||
size -= f.size;
|
||||
file_offset += f.size;
|
||||
ret.push_back(f);
|
||||
}
|
||||
|
||||
assert(size >= 0);
|
||||
if (size <= 0) break;
|
||||
|
||||
file_offset -= file_iter->size;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
peer_request torrent_info::map_file(int file_index, size_type file_offset
|
||||
, int size) const
|
||||
{
|
||||
assert(file_index < (int)m_files.size());
|
||||
assert(file_index >= 0);
|
||||
size_type offset = file_offset + m_files[file_index].offset;
|
||||
|
||||
peer_request ret;
|
||||
ret.piece = offset / piece_length();
|
||||
ret.start = offset - ret.piece * piece_length();
|
||||
ret.length = size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
569
cpp/tracker_manager.cpp
Executable file
569
cpp/tracker_manager.cpp
Executable file
@ -0,0 +1,569 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2003, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <cctype>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "zlib.h"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include "libtorrent/tracker_manager.hpp"
|
||||
#include "libtorrent/http_tracker_connection.hpp"
|
||||
#include "libtorrent/udp_tracker_connection.hpp"
|
||||
#include "libtorrent/entry.hpp"
|
||||
#include "libtorrent/bencode.hpp"
|
||||
#include "libtorrent/torrent.hpp"
|
||||
|
||||
using namespace libtorrent;
|
||||
using boost::tuples::make_tuple;
|
||||
using boost::tuples::tuple;
|
||||
using boost::bind;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum
|
||||
{
|
||||
minimum_tracker_response_length = 3,
|
||||
http_buffer_size = 2048
|
||||
};
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
FTEXT = 0x01,
|
||||
FHCRC = 0x02,
|
||||
FEXTRA = 0x04,
|
||||
FNAME = 0x08,
|
||||
FCOMMENT = 0x10,
|
||||
FRESERVED = 0xe0,
|
||||
|
||||
GZIP_MAGIC0 = 0x1f,
|
||||
GZIP_MAGIC1 = 0x8b
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
using boost::posix_time::second_clock;
|
||||
using boost::posix_time::seconds;
|
||||
using boost::posix_time::ptime;
|
||||
using boost::posix_time::time_duration;
|
||||
|
||||
// returns -1 if gzip header is invalid or the header size in bytes
|
||||
int gzip_header(const char* buf, int size)
|
||||
{
|
||||
assert(buf != 0);
|
||||
assert(size > 0);
|
||||
|
||||
const unsigned char* buffer = reinterpret_cast<const unsigned char*>(buf);
|
||||
const int total_size = size;
|
||||
|
||||
// The zip header cannot be shorter than 10 bytes
|
||||
if (size < 10) return -1;
|
||||
|
||||
// check the magic header of gzip
|
||||
if ((buffer[0] != GZIP_MAGIC0) || (buffer[1] != GZIP_MAGIC1)) return -1;
|
||||
|
||||
int method = buffer[2];
|
||||
int flags = buffer[3];
|
||||
|
||||
// check for reserved flag and make sure it's compressed with the correct metod
|
||||
if (method != Z_DEFLATED || (flags & FRESERVED) != 0) return -1;
|
||||
|
||||
// skip time, xflags, OS code
|
||||
size -= 10;
|
||||
buffer += 10;
|
||||
|
||||
if (flags & FEXTRA)
|
||||
{
|
||||
int extra_len;
|
||||
|
||||
if (size < 2) return -1;
|
||||
|
||||
extra_len = (buffer[1] << 8) | buffer[0];
|
||||
|
||||
if (size < (extra_len+2)) return -1;
|
||||
size -= (extra_len + 2);
|
||||
buffer += (extra_len + 2);
|
||||
}
|
||||
|
||||
if (flags & FNAME)
|
||||
{
|
||||
while (size && *buffer)
|
||||
{
|
||||
--size;
|
||||
++buffer;
|
||||
}
|
||||
if (!size || *buffer) return -1;
|
||||
|
||||
--size;
|
||||
++buffer;
|
||||
}
|
||||
|
||||
if (flags & FCOMMENT)
|
||||
{
|
||||
while (size && *buffer)
|
||||
{
|
||||
--size;
|
||||
++buffer;
|
||||
}
|
||||
if (!size || *buffer) return -1;
|
||||
|
||||
--size;
|
||||
++buffer;
|
||||
}
|
||||
|
||||
if (flags & FHCRC)
|
||||
{
|
||||
if (size < 2) return -1;
|
||||
|
||||
size -= 2;
|
||||
buffer += 2;
|
||||
}
|
||||
|
||||
return total_size - size;
|
||||
}
|
||||
|
||||
bool inflate_gzip(
|
||||
std::vector<char>& buffer
|
||||
, tracker_request const& req
|
||||
, request_callback* requester
|
||||
, int maximum_tracker_response_length)
|
||||
{
|
||||
assert(maximum_tracker_response_length > 0);
|
||||
|
||||
int header_len = gzip_header(&buffer[0], (int)buffer.size());
|
||||
if (header_len < 0)
|
||||
{
|
||||
requester->tracker_request_error(req, 200, "invalid gzip header in tracker response");
|
||||
return true;
|
||||
}
|
||||
|
||||
// start off wth one kilobyte and grow
|
||||
// if needed
|
||||
std::vector<char> inflate_buffer(1024);
|
||||
|
||||
// initialize the zlib-stream
|
||||
z_stream str;
|
||||
|
||||
// subtract 8 from the end of the buffer since that's CRC32 and input size
|
||||
// and those belong to the gzip file
|
||||
str.avail_in = (int)buffer.size() - header_len - 8;
|
||||
str.next_in = reinterpret_cast<Bytef*>(&buffer[header_len]);
|
||||
str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[0]);
|
||||
str.avail_out = (int)inflate_buffer.size();
|
||||
str.zalloc = Z_NULL;
|
||||
str.zfree = Z_NULL;
|
||||
str.opaque = 0;
|
||||
// -15 is really important. It will make inflate() not look for a zlib header
|
||||
// and just deflate the buffer
|
||||
if (inflateInit2(&str, -15) != Z_OK)
|
||||
{
|
||||
requester->tracker_request_error(req, 200, "gzip out of memory");
|
||||
return true;
|
||||
}
|
||||
|
||||
// inflate and grow inflate_buffer as needed
|
||||
int ret = inflate(&str, Z_SYNC_FLUSH);
|
||||
while (ret == Z_OK)
|
||||
{
|
||||
if (str.avail_out == 0)
|
||||
{
|
||||
if (inflate_buffer.size() >= (unsigned)maximum_tracker_response_length)
|
||||
{
|
||||
inflateEnd(&str);
|
||||
requester->tracker_request_error(req, 200
|
||||
, "tracker response too large");
|
||||
return true;
|
||||
}
|
||||
int new_size = (int)inflate_buffer.size() * 2;
|
||||
if (new_size > maximum_tracker_response_length) new_size = maximum_tracker_response_length;
|
||||
int old_size = (int)inflate_buffer.size();
|
||||
|
||||
inflate_buffer.resize(new_size);
|
||||
str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[old_size]);
|
||||
str.avail_out = new_size - old_size;
|
||||
}
|
||||
|
||||
ret = inflate(&str, Z_SYNC_FLUSH);
|
||||
}
|
||||
|
||||
inflate_buffer.resize(inflate_buffer.size() - str.avail_out);
|
||||
inflateEnd(&str);
|
||||
|
||||
if (ret != Z_STREAM_END)
|
||||
{
|
||||
requester->tracker_request_error(req, 200, "gzip error");
|
||||
return true;
|
||||
}
|
||||
|
||||
// commit the resulting buffer
|
||||
std::swap(buffer, inflate_buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string base64encode(const std::string& s)
|
||||
{
|
||||
static const char base64_table[] =
|
||||
{
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/'
|
||||
};
|
||||
|
||||
unsigned char inbuf[3];
|
||||
unsigned char outbuf[4];
|
||||
|
||||
std::string ret;
|
||||
for (std::string::const_iterator i = s.begin(); i != s.end();)
|
||||
{
|
||||
// available input is 1,2 or 3 bytes
|
||||
// since we read 3 bytes at a time at most
|
||||
int available_input = std::min(3, (int)std::distance(i, s.end()));
|
||||
|
||||
// clear input buffer
|
||||
std::fill(inbuf, inbuf+3, 0);
|
||||
|
||||
// read a chunk of input into inbuf
|
||||
for (int j = 0; j < available_input; ++j)
|
||||
{
|
||||
inbuf[j] = *i;
|
||||
++i;
|
||||
}
|
||||
|
||||
// encode inbuf to outbuf
|
||||
outbuf[0] = (inbuf[0] & 0xfc) >> 2;
|
||||
outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf [1] & 0xf0) >> 4);
|
||||
outbuf[2] = ((inbuf[1] & 0x0f) << 2) | ((inbuf [2] & 0xc0) >> 6);
|
||||
outbuf[3] = inbuf[2] & 0x3f;
|
||||
|
||||
// write output
|
||||
for (int j = 0; j < available_input+1; ++j)
|
||||
{
|
||||
ret += base64_table[outbuf[j]];
|
||||
}
|
||||
|
||||
// write pad
|
||||
for (int j = 0; j < 3 - available_input; ++j)
|
||||
{
|
||||
ret += '=';
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void intrusive_ptr_add_ref(timeout_handler const* c)
|
||||
{
|
||||
assert(c != 0);
|
||||
assert(c->m_refs >= 0);
|
||||
timeout_handler::mutex_t::scoped_lock l(c->m_mutex);
|
||||
++c->m_refs;
|
||||
}
|
||||
|
||||
void intrusive_ptr_release(timeout_handler const* c)
|
||||
{
|
||||
assert(c != 0);
|
||||
assert(c->m_refs > 0);
|
||||
timeout_handler::mutex_t::scoped_lock l(c->m_mutex);
|
||||
--c->m_refs;
|
||||
if (c->m_refs == 0)
|
||||
{
|
||||
l.unlock();
|
||||
delete c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
timeout_handler::timeout_handler(demuxer& d)
|
||||
: m_demuxer(d)
|
||||
, m_start_time(second_clock::universal_time())
|
||||
, m_read_time(second_clock::universal_time())
|
||||
, m_timeout(d)
|
||||
, m_completion_timeout(0)
|
||||
, m_read_timeout(0)
|
||||
, m_refs(0)
|
||||
{}
|
||||
|
||||
void timeout_handler::set_timeout(int completion_timeout, int read_timeout)
|
||||
{
|
||||
m_completion_timeout = completion_timeout;
|
||||
m_read_timeout = read_timeout;
|
||||
m_start_time = second_clock::universal_time();
|
||||
m_read_time = second_clock::universal_time();
|
||||
|
||||
m_timeout.expires_at(std::min(
|
||||
m_read_time + seconds(m_read_timeout)
|
||||
, m_start_time + seconds(m_completion_timeout)));
|
||||
m_timeout.async_wait(bind(&timeout_handler::timeout_callback, self(), _1));
|
||||
}
|
||||
|
||||
void timeout_handler::restart_read_timeout()
|
||||
{
|
||||
m_read_time = second_clock::universal_time();
|
||||
}
|
||||
|
||||
void timeout_handler::cancel()
|
||||
{
|
||||
m_completion_timeout = 0;
|
||||
m_timeout.cancel();
|
||||
}
|
||||
|
||||
void timeout_handler::timeout_callback(asio::error const& error) try
|
||||
{
|
||||
if (error) return;
|
||||
if (m_completion_timeout == 0) return;
|
||||
|
||||
ptime now(second_clock::universal_time());
|
||||
time_duration receive_timeout = now - m_read_time;
|
||||
time_duration completion_timeout = now - m_start_time;
|
||||
|
||||
if (m_read_timeout
|
||||
< receive_timeout.total_seconds()
|
||||
|| m_completion_timeout
|
||||
< completion_timeout.total_seconds())
|
||||
{
|
||||
on_timeout();
|
||||
return;
|
||||
}
|
||||
|
||||
m_timeout.expires_at(std::min(
|
||||
m_read_time + seconds(m_read_timeout)
|
||||
, m_start_time + seconds(m_completion_timeout)));
|
||||
m_timeout.async_wait(bind(&timeout_handler::timeout_callback, self(), _1));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
tracker_connection::tracker_connection(
|
||||
tracker_manager& man
|
||||
, tracker_request req
|
||||
, demuxer& d
|
||||
, boost::weak_ptr<request_callback> r)
|
||||
: timeout_handler(d)
|
||||
, m_requester(r)
|
||||
, m_man(man)
|
||||
, m_req(req)
|
||||
{}
|
||||
|
||||
request_callback& tracker_connection::requester()
|
||||
{
|
||||
boost::shared_ptr<request_callback> r = m_requester.lock();
|
||||
assert(r);
|
||||
return *r;
|
||||
}
|
||||
|
||||
void tracker_connection::fail(int code, char const* msg)
|
||||
{
|
||||
if (has_requester()) requester().tracker_request_error(
|
||||
m_req, code, msg);
|
||||
close();
|
||||
}
|
||||
|
||||
void tracker_connection::fail_timeout()
|
||||
{
|
||||
if (has_requester()) requester().tracker_request_timed_out(m_req);
|
||||
close();
|
||||
}
|
||||
|
||||
void tracker_connection::close()
|
||||
{
|
||||
cancel();
|
||||
m_man.remove_request(this);
|
||||
}
|
||||
|
||||
void tracker_manager::remove_request(tracker_connection const* c)
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
tracker_connections_t::iterator i = std::find(m_connections.begin()
|
||||
, m_connections.end(), boost::intrusive_ptr<const tracker_connection>(c));
|
||||
if (i == m_connections.end()) return;
|
||||
|
||||
m_connections.erase(i);
|
||||
}
|
||||
|
||||
tuple<std::string, std::string, int, std::string>
|
||||
parse_url_components(std::string url)
|
||||
{
|
||||
std::string hostname; // hostname only
|
||||
std::string protocol; // should be http
|
||||
int port = 80;
|
||||
|
||||
// PARSE URL
|
||||
std::string::iterator start = url.begin();
|
||||
// remove white spaces in front of the url
|
||||
while (start != url.end() && (*start == ' ' || *start == '\t'))
|
||||
++start;
|
||||
std::string::iterator end
|
||||
= std::find(url.begin(), url.end(), ':');
|
||||
protocol = std::string(start, end);
|
||||
|
||||
if (end == url.end()) throw std::runtime_error("invalid url");
|
||||
++end;
|
||||
if (end == url.end()) throw std::runtime_error("invalid url");
|
||||
if (*end != '/') throw std::runtime_error("invalid url");
|
||||
++end;
|
||||
if (end == url.end()) throw std::runtime_error("invalid url");
|
||||
if (*end != '/') throw std::runtime_error("invalid url");
|
||||
++end;
|
||||
start = end;
|
||||
|
||||
end = std::find(start, url.end(), '/');
|
||||
std::string::iterator port_pos
|
||||
= std::find(start, url.end(), ':');
|
||||
|
||||
if (port_pos < end)
|
||||
{
|
||||
hostname.assign(start, port_pos);
|
||||
++port_pos;
|
||||
try
|
||||
{
|
||||
port = boost::lexical_cast<int>(std::string(port_pos, end));
|
||||
}
|
||||
catch(boost::bad_lexical_cast&)
|
||||
{
|
||||
throw std::runtime_error("invalid url: \"" + url
|
||||
+ "\", port number expected");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hostname.assign(start, end);
|
||||
}
|
||||
|
||||
start = end;
|
||||
return make_tuple(protocol, hostname, port
|
||||
, std::string(start, url.end()));
|
||||
}
|
||||
|
||||
void tracker_manager::queue_request(
|
||||
demuxer& d
|
||||
, tracker_request req
|
||||
, std::string const& auth
|
||||
, boost::weak_ptr<request_callback> c)
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
assert(req.num_want >= 0);
|
||||
if (req.event == tracker_request::stopped)
|
||||
req.num_want = 0;
|
||||
|
||||
try
|
||||
{
|
||||
std::string protocol;
|
||||
std::string hostname;
|
||||
int port;
|
||||
std::string request_string;
|
||||
|
||||
boost::tie(protocol, hostname, port, request_string)
|
||||
= parse_url_components(req.url);
|
||||
|
||||
boost::intrusive_ptr<tracker_connection> con;
|
||||
|
||||
if (protocol == "http")
|
||||
{
|
||||
con = new http_tracker_connection(
|
||||
d
|
||||
, *this
|
||||
, req
|
||||
, hostname
|
||||
, port
|
||||
, request_string
|
||||
, c
|
||||
, m_settings
|
||||
, auth);
|
||||
}
|
||||
else if (protocol == "udp")
|
||||
{
|
||||
con = new udp_tracker_connection(
|
||||
d
|
||||
, *this
|
||||
, req
|
||||
, hostname
|
||||
, port
|
||||
, c
|
||||
, m_settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("unkown protocol in tracker url");
|
||||
}
|
||||
|
||||
m_connections.push_back(con);
|
||||
|
||||
if (con->has_requester()) con->requester().m_manager = this;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
if (boost::shared_ptr<request_callback> r = c.lock())
|
||||
r->tracker_request_error(req, -1, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void tracker_manager::abort_all_requests()
|
||||
{
|
||||
// removes all connections from m_connections
|
||||
// except those with a requester == 0 (since those are
|
||||
// 'event=stopped'-requests)
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
tracker_connections_t keep_connections;
|
||||
|
||||
for (tracker_connections_t::const_iterator i =
|
||||
m_connections.begin(); i != m_connections.end(); ++i)
|
||||
{
|
||||
tracker_request const& req = (*i)->tracker_req();
|
||||
if (req.event == tracker_request::stopped)
|
||||
keep_connections.push_back(*i);
|
||||
}
|
||||
|
||||
std::swap(m_connections, keep_connections);
|
||||
}
|
||||
|
||||
bool tracker_manager::empty() const
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
return m_connections.empty();
|
||||
}
|
||||
|
||||
}
|
||||
522
cpp/udp_tracker_connection.cpp
Executable file
522
cpp/udp_tracker_connection.cpp
Executable file
@ -0,0 +1,522 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2003, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <cctype>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "zlib.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 1)
|
||||
#endif
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "libtorrent/tracker_manager.hpp"
|
||||
#include "libtorrent/udp_tracker_connection.hpp"
|
||||
#include "libtorrent/io.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
enum
|
||||
{
|
||||
udp_connection_retries = 4,
|
||||
udp_announce_retries = 15,
|
||||
udp_connect_timeout = 15,
|
||||
udp_announce_timeout = 10,
|
||||
udp_buffer_size = 2048
|
||||
};
|
||||
}
|
||||
|
||||
using namespace boost::posix_time;
|
||||
using boost::bind;
|
||||
using boost::lexical_cast;
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
udp_tracker_connection::udp_tracker_connection(
|
||||
demuxer& d
|
||||
, tracker_manager& man
|
||||
, tracker_request const& req
|
||||
, std::string const& hostname
|
||||
, unsigned short port
|
||||
, boost::weak_ptr<request_callback> c
|
||||
, session_settings const& stn)
|
||||
: tracker_connection(man, req, d, c)
|
||||
, m_man(man)
|
||||
, m_name_lookup(d)
|
||||
, m_port(port)
|
||||
, m_transaction_id(0)
|
||||
, m_connection_id(0)
|
||||
, m_settings(stn)
|
||||
, m_attempts(0)
|
||||
{
|
||||
m_socket.reset(new datagram_socket(d));
|
||||
tcp::resolver::query q(hostname, "0");
|
||||
m_name_lookup.async_resolve(q
|
||||
, boost::bind(&udp_tracker_connection::name_lookup, self(), _1, _2));
|
||||
set_timeout(m_settings.tracker_completion_timeout
|
||||
, m_settings.tracker_receive_timeout);
|
||||
}
|
||||
|
||||
void udp_tracker_connection::name_lookup(asio::error const& error
|
||||
, tcp::resolver::iterator i) try
|
||||
{
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
if (!m_socket) return; // the operation was aborted
|
||||
if (error || i == tcp::resolver::iterator())
|
||||
{
|
||||
fail(-1, error.what());
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (has_requester()) requester().debug_log("udp tracker name lookup successful");
|
||||
#endif
|
||||
restart_read_timeout();
|
||||
m_target = udp::endpoint(i->endpoint().address(), m_port);
|
||||
if (has_requester()) requester().m_tracker_address
|
||||
= tcp::endpoint(i->endpoint().address(), m_port);
|
||||
m_socket->connect(m_target);
|
||||
send_udp_connect();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
fail(-1, e.what());
|
||||
};
|
||||
|
||||
void udp_tracker_connection::on_timeout()
|
||||
{
|
||||
m_socket.reset();
|
||||
m_name_lookup.cancel();
|
||||
fail_timeout();
|
||||
}
|
||||
|
||||
void udp_tracker_connection::send_udp_connect()
|
||||
{
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (has_requester())
|
||||
{
|
||||
requester().debug_log("==> UDP_TRACKER_CONNECT ["
|
||||
+ lexical_cast<std::string>(tracker_req().info_hash) + "]");
|
||||
}
|
||||
#endif
|
||||
if (!m_socket) return; // the operation was aborted
|
||||
|
||||
char send_buf[16];
|
||||
char* ptr = send_buf;
|
||||
|
||||
if (m_transaction_id == 0)
|
||||
m_transaction_id = rand() ^ (rand() << 16);
|
||||
|
||||
// connection_id
|
||||
detail::write_uint32(0x417, ptr);
|
||||
detail::write_uint32(0x27101980, ptr);
|
||||
// action (connect)
|
||||
detail::write_int32(action_connect, ptr);
|
||||
// transaction_id
|
||||
detail::write_int32(m_transaction_id, ptr);
|
||||
|
||||
m_socket->send(asio::buffer((void*)send_buf, 16), 0);
|
||||
++m_attempts;
|
||||
m_buffer.resize(udp_buffer_size);
|
||||
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
|
||||
, boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2));
|
||||
}
|
||||
|
||||
void udp_tracker_connection::connect_response(asio::error const& error
|
||||
, std::size_t bytes_transferred) try
|
||||
{
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
if (!m_socket) return; // the operation was aborted
|
||||
if (error)
|
||||
{
|
||||
fail(-1, error.what());
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_target != m_sender)
|
||||
{
|
||||
// this packet was not received from the tracker
|
||||
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
|
||||
, boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred >= udp_buffer_size)
|
||||
{
|
||||
fail(-1, "udp response too big");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred < 8)
|
||||
{
|
||||
fail(-1, "got a message with size < 8");
|
||||
return;
|
||||
}
|
||||
|
||||
restart_read_timeout();
|
||||
|
||||
const char* ptr = &m_buffer[0];
|
||||
int action = detail::read_int32(ptr);
|
||||
int transaction = detail::read_int32(ptr);
|
||||
|
||||
if (action == action_error)
|
||||
{
|
||||
fail(-1, std::string(ptr, bytes_transferred - 8).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (action != action_connect)
|
||||
{
|
||||
fail(-1, "invalid action in connect reply");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_transaction_id != transaction)
|
||||
{
|
||||
fail(-1, "incorrect transaction id");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred < 16)
|
||||
{
|
||||
fail(-1, "udp_tracker_connection: "
|
||||
"got a message with size < 16");
|
||||
return;
|
||||
}
|
||||
// reset transaction
|
||||
m_transaction_id = 0;
|
||||
m_attempts = 0;
|
||||
m_connection_id = detail::read_int64(ptr);
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (has_requester())
|
||||
{
|
||||
requester().debug_log("<== UDP_TRACKER_CONNECT_RESPONSE ["
|
||||
+ lexical_cast<std::string>(m_connection_id) + "]");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (tracker_req().kind == tracker_request::announce_request)
|
||||
send_udp_announce();
|
||||
else if (tracker_req().kind == tracker_request::scrape_request)
|
||||
send_udp_scrape();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
fail(-1, e.what());
|
||||
}
|
||||
|
||||
void udp_tracker_connection::send_udp_announce()
|
||||
{
|
||||
if (m_transaction_id == 0)
|
||||
m_transaction_id = rand() ^ (rand() << 16);
|
||||
|
||||
if (!m_socket) return; // the operation was aborted
|
||||
|
||||
std::vector<char> buf;
|
||||
std::back_insert_iterator<std::vector<char> > out(buf);
|
||||
|
||||
tracker_request const& req = tracker_req();
|
||||
|
||||
// connection_id
|
||||
detail::write_int64(m_connection_id, out);
|
||||
// action (announce)
|
||||
detail::write_int32(action_announce, out);
|
||||
// transaction_id
|
||||
detail::write_int32(m_transaction_id, out);
|
||||
// info_hash
|
||||
std::copy(req.info_hash.begin(), req.info_hash.end(), out);
|
||||
// peer_id
|
||||
std::copy(req.pid.begin(), req.pid.end(), out);
|
||||
// downloaded
|
||||
detail::write_int64(req.downloaded, out);
|
||||
// left
|
||||
detail::write_int64(req.left, out);
|
||||
// uploaded
|
||||
detail::write_int64(req.uploaded, out);
|
||||
// event
|
||||
detail::write_int32(req.event, out);
|
||||
// ip address
|
||||
detail::write_int32(0, out);
|
||||
// key
|
||||
detail::write_int32(req.key, out);
|
||||
// num_want
|
||||
detail::write_int32(req.num_want, out);
|
||||
// port
|
||||
detail::write_uint16(req.listen_port, out);
|
||||
// extensions
|
||||
detail::write_uint16(0, out);
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (has_requester())
|
||||
{
|
||||
requester().debug_log("==> UDP_TRACKER_ANNOUNCE ["
|
||||
+ lexical_cast<std::string>(req.info_hash) + "]");
|
||||
}
|
||||
#endif
|
||||
|
||||
m_socket->send(asio::buffer(buf), 0);
|
||||
++m_attempts;
|
||||
|
||||
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
|
||||
, bind(&udp_tracker_connection::announce_response, self(), _1, _2));
|
||||
}
|
||||
|
||||
void udp_tracker_connection::send_udp_scrape()
|
||||
{
|
||||
if (m_transaction_id == 0)
|
||||
m_transaction_id = rand() ^ (rand() << 16);
|
||||
|
||||
if (!m_socket) return; // the operation was aborted
|
||||
|
||||
std::vector<char> buf;
|
||||
std::back_insert_iterator<std::vector<char> > out(buf);
|
||||
|
||||
// connection_id
|
||||
detail::write_int64(m_connection_id, out);
|
||||
// action (scrape)
|
||||
detail::write_int32(action_scrape, out);
|
||||
// transaction_id
|
||||
detail::write_int32(m_transaction_id, out);
|
||||
// info_hash
|
||||
std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end(), out);
|
||||
|
||||
m_socket->send(asio::buffer(&buf[0], buf.size()), 0);
|
||||
++m_attempts;
|
||||
|
||||
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
|
||||
, bind(&udp_tracker_connection::scrape_response, self(), _1, _2));
|
||||
}
|
||||
|
||||
void udp_tracker_connection::announce_response(asio::error const& error
|
||||
, std::size_t bytes_transferred) try
|
||||
{
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
if (!m_socket) return; // the operation was aborted
|
||||
if (error)
|
||||
{
|
||||
fail(-1, error.what());
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_target != m_sender)
|
||||
{
|
||||
// this packet was not received from the tracker
|
||||
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
|
||||
, bind(&udp_tracker_connection::connect_response, self(), _1, _2));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred >= udp_buffer_size)
|
||||
{
|
||||
fail(-1, "udp response too big");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred < 8)
|
||||
{
|
||||
fail(-1, "got a message with size < 8");
|
||||
return;
|
||||
}
|
||||
|
||||
restart_read_timeout();
|
||||
char* buf = &m_buffer[0];
|
||||
int action = detail::read_int32(buf);
|
||||
int transaction = detail::read_int32(buf);
|
||||
|
||||
if (transaction != m_transaction_id)
|
||||
{
|
||||
fail(-1, "incorrect transaction id");
|
||||
return;
|
||||
}
|
||||
|
||||
if (action == action_error)
|
||||
{
|
||||
fail(-1, std::string(buf, bytes_transferred - 8).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (action != action_announce)
|
||||
{
|
||||
fail(-1, "invalid action in announce response");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred < 20)
|
||||
{
|
||||
fail(-1, "got a message with size < 20");
|
||||
return;
|
||||
}
|
||||
|
||||
int interval = detail::read_int32(buf);
|
||||
int incomplete = detail::read_int32(buf);
|
||||
int complete = detail::read_int32(buf);
|
||||
int num_peers = (bytes_transferred - 20) / 6;
|
||||
if ((bytes_transferred - 20) % 6 != 0)
|
||||
{
|
||||
fail(-1, "invalid udp tracker response length");
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (has_requester())
|
||||
{
|
||||
requester().debug_log("<== UDP_TRACKER_ANNOUNCE_RESPONSE");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!has_requester())
|
||||
{
|
||||
m_man.remove_request(this);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<peer_entry> peer_list;
|
||||
for (int i = 0; i < num_peers; ++i)
|
||||
{
|
||||
peer_entry e;
|
||||
std::stringstream s;
|
||||
s << (int)detail::read_uint8(buf) << ".";
|
||||
s << (int)detail::read_uint8(buf) << ".";
|
||||
s << (int)detail::read_uint8(buf) << ".";
|
||||
s << (int)detail::read_uint8(buf);
|
||||
e.ip = s.str();
|
||||
e.port = detail::read_uint16(buf);
|
||||
e.pid.clear();
|
||||
peer_list.push_back(e);
|
||||
}
|
||||
|
||||
requester().tracker_response(tracker_req(), peer_list, interval
|
||||
, complete, incomplete);
|
||||
|
||||
m_man.remove_request(this);
|
||||
return;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
fail(-1, e.what());
|
||||
}; // msvc 7.1 seems to require this
|
||||
|
||||
void udp_tracker_connection::scrape_response(asio::error const& error
|
||||
, std::size_t bytes_transferred) try
|
||||
{
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
if (!m_socket) return; // the operation was aborted
|
||||
if (error)
|
||||
{
|
||||
fail(-1, error.what());
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_target != m_sender)
|
||||
{
|
||||
// this packet was not received from the tracker
|
||||
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
|
||||
, bind(&udp_tracker_connection::connect_response, self(), _1, _2));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred >= udp_buffer_size)
|
||||
{
|
||||
fail(-1, "udp response too big");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred < 8)
|
||||
{
|
||||
fail(-1, "got a message with size < 8");
|
||||
return;
|
||||
}
|
||||
|
||||
restart_read_timeout();
|
||||
char* buf = &m_buffer[0];
|
||||
int action = detail::read_int32(buf);
|
||||
int transaction = detail::read_int32(buf);
|
||||
|
||||
if (transaction != m_transaction_id)
|
||||
{
|
||||
fail(-1, "incorrect transaction id");
|
||||
return;
|
||||
}
|
||||
|
||||
if (action == action_error)
|
||||
{
|
||||
fail(-1, std::string(buf, bytes_transferred - 8).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (action != action_scrape)
|
||||
{
|
||||
fail(-1, "invalid action in announce response");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred < 20)
|
||||
{
|
||||
fail(-1, "got a message with size < 20");
|
||||
return;
|
||||
}
|
||||
|
||||
int complete = detail::read_int32(buf);
|
||||
/*int downloaded = */detail::read_int32(buf);
|
||||
int incomplete = detail::read_int32(buf);
|
||||
|
||||
if (!has_requester())
|
||||
{
|
||||
m_man.remove_request(this);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<peer_entry> peer_list;
|
||||
requester().tracker_response(tracker_req(), peer_list, 0
|
||||
, complete, incomplete);
|
||||
|
||||
m_man.remove_request(this);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
fail(-1, e.what());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
455
cpp/web_peer_connection.cpp
Executable file
455
cpp/web_peer_connection.cpp
Executable file
@ -0,0 +1,455 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2003, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <boost/bind.hpp>
|
||||
#include <sstream>
|
||||
|
||||
#include "libtorrent/web_peer_connection.hpp"
|
||||
#include "libtorrent/session.hpp"
|
||||
#include "libtorrent/identify_client.hpp"
|
||||
#include "libtorrent/entry.hpp"
|
||||
#include "libtorrent/bencode.hpp"
|
||||
#include "libtorrent/alert_types.hpp"
|
||||
#include "libtorrent/invariant_check.hpp"
|
||||
#include "libtorrent/io.hpp"
|
||||
#include "libtorrent/version.hpp"
|
||||
#include "libtorrent/aux_/session_impl.hpp"
|
||||
|
||||
using namespace boost::posix_time;
|
||||
using boost::bind;
|
||||
using boost::shared_ptr;
|
||||
using libtorrent::aux::session_impl;
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
web_peer_connection::web_peer_connection(
|
||||
session_impl& ses
|
||||
, boost::weak_ptr<torrent> t
|
||||
, boost::shared_ptr<stream_socket> s
|
||||
, tcp::endpoint const& remote
|
||||
, std::string const& url)
|
||||
: peer_connection(ses, t, s, remote)
|
||||
, m_url(url)
|
||||
, m_first_request(true)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
m_max_out_request_queue = ses.settings().urlseed_pipeline_size;
|
||||
|
||||
// since this is a web seed, change the timeout
|
||||
// according to the settings.
|
||||
set_timeout(ses.settings().urlseed_timeout);
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << "*** web_peer_connection\n";
|
||||
#endif
|
||||
|
||||
std::string protocol;
|
||||
boost::tie(protocol, m_host, m_port, m_path)
|
||||
= parse_url_components(url);
|
||||
|
||||
m_server_string = "URL seed @ ";
|
||||
m_server_string += m_host;
|
||||
}
|
||||
|
||||
web_peer_connection::~web_peer_connection()
|
||||
{}
|
||||
|
||||
boost::optional<piece_block_progress>
|
||||
web_peer_connection::downloading_piece_progress() const
|
||||
{
|
||||
if (!m_parser.header_finished() || m_requests.empty())
|
||||
return boost::optional<piece_block_progress>();
|
||||
|
||||
boost::shared_ptr<torrent> t = associated_torrent().lock();
|
||||
assert(t);
|
||||
|
||||
int body_start = m_parser.body_start();
|
||||
buffer::const_interval recv_buffer = receive_buffer();
|
||||
assert(body_start <= recv_buffer.left());
|
||||
piece_block_progress ret;
|
||||
|
||||
ret.piece_index = m_requests.front().piece;
|
||||
ret.block_index = m_requests.front().start / t->block_size();
|
||||
ret.bytes_downloaded = recv_buffer.left() - body_start;
|
||||
ret.full_block_bytes = m_requests.front().length;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void web_peer_connection::on_connected()
|
||||
{
|
||||
boost::shared_ptr<torrent> t = associated_torrent().lock();
|
||||
assert(t);
|
||||
|
||||
// this is always a seed
|
||||
incoming_bitfield(std::vector<bool>(
|
||||
t->torrent_file().num_pieces(), true));
|
||||
// it is always possible to request pieces
|
||||
incoming_unchoke();
|
||||
|
||||
reset_recv_buffer(512*1024+1024);
|
||||
}
|
||||
|
||||
void web_peer_connection::write_request(peer_request const& r)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
boost::shared_ptr<torrent> t = associated_torrent().lock();
|
||||
assert(t);
|
||||
|
||||
assert(t->valid_metadata());
|
||||
|
||||
bool single_file_request = false;
|
||||
if (!m_path.empty() && m_path[m_path.size() - 1] != '/')
|
||||
single_file_request = true;
|
||||
|
||||
torrent_info const& info = t->torrent_file();
|
||||
|
||||
std::string request;
|
||||
|
||||
m_requests.push_back(r);
|
||||
|
||||
bool using_proxy = false;
|
||||
if (!m_ses.settings().proxy_ip.empty())
|
||||
using_proxy = true;
|
||||
|
||||
if (single_file_request)
|
||||
{
|
||||
request += "GET ";
|
||||
if (using_proxy) request += m_url;
|
||||
else request += escape_path(m_path.c_str(), m_path.length());
|
||||
request += " HTTP/1.1\r\n";
|
||||
request += "Host: ";
|
||||
request += m_host;
|
||||
if (m_first_request)
|
||||
{
|
||||
request += "\r\nUser-Agent: ";
|
||||
request += m_ses.settings().user_agent;
|
||||
}
|
||||
if (using_proxy && !m_ses.settings().proxy_login.empty())
|
||||
{
|
||||
request += "\r\nProxy-Authorization: Basic ";
|
||||
request += base64encode(m_ses.settings().proxy_login + ":"
|
||||
+ m_ses.settings().proxy_password);
|
||||
}
|
||||
if (using_proxy)
|
||||
{
|
||||
request += "\r\nProxy-Connection: keep-alive";
|
||||
}
|
||||
request += "\r\nRange: bytes=";
|
||||
request += boost::lexical_cast<std::string>(r.piece
|
||||
* info.piece_length() + r.start);
|
||||
request += "-";
|
||||
request += boost::lexical_cast<std::string>(r.piece
|
||||
* info.piece_length() + r.start + r.length - 1);
|
||||
if (m_first_request || using_proxy)
|
||||
request += "\r\nConnection: keep-alive";
|
||||
request += "\r\n\r\n";
|
||||
m_first_request = false;
|
||||
m_file_requests.push_back(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<file_slice> files = info.map_block(r.piece, r.start
|
||||
, r.length);
|
||||
|
||||
for (std::vector<file_slice>::iterator i = files.begin();
|
||||
i != files.end(); ++i)
|
||||
{
|
||||
file_slice const& f = *i;
|
||||
|
||||
request += "GET ";
|
||||
if (using_proxy)
|
||||
{
|
||||
request += m_url;
|
||||
std::string path = info.file_at(f.file_index).path.string();
|
||||
request += escape_path(path.c_str(), path.length());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string path = m_path;
|
||||
path += info.file_at(f.file_index).path.string();
|
||||
request += escape_path(path.c_str(), path.length());
|
||||
}
|
||||
request += " HTTP/1.1\r\n";
|
||||
request += "Host: ";
|
||||
request += m_host;
|
||||
if (m_first_request)
|
||||
{
|
||||
request += "\r\nUser-Agent: ";
|
||||
request += m_ses.settings().user_agent;
|
||||
}
|
||||
if (using_proxy && !m_ses.settings().proxy_login.empty())
|
||||
{
|
||||
request += "\r\nProxy-Authorization: Basic ";
|
||||
request += base64encode(m_ses.settings().proxy_login + ":"
|
||||
+ m_ses.settings().proxy_password);
|
||||
}
|
||||
if (using_proxy)
|
||||
{
|
||||
request += "\r\nProxy-Connection: keep-alive";
|
||||
}
|
||||
request += "\r\nRange: bytes=";
|
||||
request += boost::lexical_cast<std::string>(f.offset);
|
||||
request += "-";
|
||||
request += boost::lexical_cast<std::string>(f.offset + f.size - 1);
|
||||
if (m_first_request || using_proxy)
|
||||
request += "\r\nConnection: keep-alive";
|
||||
request += "\r\n\r\n";
|
||||
m_first_request = false;
|
||||
m_file_requests.push_back(f.file_index);
|
||||
}
|
||||
}
|
||||
|
||||
send_buffer(request.c_str(), request.c_str() + request.size());
|
||||
}
|
||||
|
||||
// --------------------------
|
||||
// RECEIVE DATA
|
||||
// --------------------------
|
||||
|
||||
// throws exception when the client should be disconnected
|
||||
void web_peer_connection::on_receive(const asio::error& error
|
||||
, std::size_t bytes_transferred)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (error)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
boost::shared_ptr<torrent> t = associated_torrent().lock();
|
||||
assert(t);
|
||||
|
||||
incoming_piece_fragment();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
buffer::const_interval recv_buffer = receive_buffer();
|
||||
int payload;
|
||||
int protocol;
|
||||
boost::tie(payload, protocol) = m_parser.incoming(recv_buffer);
|
||||
m_statistics.received_bytes(payload, protocol);
|
||||
|
||||
if (m_parser.status_code() != 206 && m_parser.status_code() != -1)
|
||||
{
|
||||
// we should not try this server again.
|
||||
t->remove_url_seed(m_url);
|
||||
if (m_parser.status_code() == 404)
|
||||
throw std::runtime_error("File not found on server");
|
||||
throw std::runtime_error("HTTP server does not support byte range requests");
|
||||
}
|
||||
|
||||
if (!m_parser.finished()) break;
|
||||
|
||||
std::string server_version = m_parser.header<std::string>("Server");
|
||||
if (!server_version.empty())
|
||||
{
|
||||
m_server_string = "URL seed @ ";
|
||||
m_server_string += m_host;
|
||||
m_server_string += " (";
|
||||
m_server_string += server_version;
|
||||
m_server_string += ")";
|
||||
}
|
||||
|
||||
std::stringstream range_str(m_parser.header<std::string>("Content-Range"));
|
||||
size_type range_start;
|
||||
size_type range_end;
|
||||
char dummy;
|
||||
std::string bytes;
|
||||
range_str >> bytes >> range_start >> dummy >> range_end;
|
||||
if (!range_str)
|
||||
{
|
||||
// we should not try this server again.
|
||||
t->remove_url_seed(m_url);
|
||||
throw std::runtime_error("invalid range in HTTP response: " + range_str.str());
|
||||
}
|
||||
// the http range is inclusive
|
||||
range_end++;
|
||||
|
||||
torrent_info const& info = t->torrent_file();
|
||||
|
||||
if (m_requests.empty() || m_file_requests.empty())
|
||||
throw std::runtime_error("unexpected HTTP response");
|
||||
|
||||
int file_index = m_file_requests.front();
|
||||
m_file_requests.pop_front();
|
||||
|
||||
peer_request r = info.map_file(file_index, range_start
|
||||
, range_end - range_start);
|
||||
|
||||
buffer::const_interval http_body = m_parser.get_body();
|
||||
|
||||
if (r == m_requests.front())
|
||||
{
|
||||
m_requests.pop_front();
|
||||
incoming_piece(r, http_body.begin);
|
||||
cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_piece.empty())
|
||||
{
|
||||
// this is not the first partial request we get
|
||||
if (m_intermediate_piece.start + m_intermediate_piece.length != r.start
|
||||
|| m_intermediate_piece.piece != r.piece)
|
||||
{
|
||||
throw std::runtime_error("invalid range in HTTP response");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// this is the first part of a partial request
|
||||
if (r.start != m_requests.front().start
|
||||
|| r.piece != m_requests.front().piece)
|
||||
{
|
||||
throw std::runtime_error("invalid range in HTTP response");
|
||||
}
|
||||
m_intermediate_piece.piece = r.piece;
|
||||
m_intermediate_piece.start = r.start;
|
||||
m_intermediate_piece.length = 0;
|
||||
}
|
||||
|
||||
m_piece.reserve(info.piece_length());
|
||||
std::copy(http_body.begin, http_body.end, back_inserter(m_piece));
|
||||
m_intermediate_piece.length += r.length;
|
||||
if (m_intermediate_piece.length == m_requests.front().length)
|
||||
{
|
||||
assert(m_requests.front() == m_intermediate_piece);
|
||||
assert(int(m_piece.size()) == m_intermediate_piece.length);
|
||||
m_requests.pop_front();
|
||||
incoming_piece(m_intermediate_piece, &m_piece[0]);
|
||||
m_piece.clear();
|
||||
}
|
||||
else if (m_intermediate_piece.length > m_requests.front().length)
|
||||
{
|
||||
throw std::runtime_error("too large HTTP response body");
|
||||
}
|
||||
|
||||
cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024);
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------
|
||||
// SEND DATA
|
||||
// --------------------------
|
||||
|
||||
void web_peer_connection::get_peer_info(peer_info& p) const
|
||||
{
|
||||
assert(!associated_torrent().expired());
|
||||
|
||||
p.down_speed = statistics().download_rate();
|
||||
p.up_speed = statistics().upload_rate();
|
||||
p.payload_down_speed = statistics().download_payload_rate();
|
||||
p.payload_up_speed = statistics().upload_payload_rate();
|
||||
p.pid = pid();
|
||||
p.ip = remote();
|
||||
|
||||
p.total_download = statistics().total_payload_download();
|
||||
p.total_upload = statistics().total_payload_upload();
|
||||
|
||||
if (m_ul_bandwidth_quota.given == std::numeric_limits<int>::max())
|
||||
p.upload_limit = -1;
|
||||
else
|
||||
p.upload_limit = m_ul_bandwidth_quota.given;
|
||||
|
||||
if (m_dl_bandwidth_quota.given == std::numeric_limits<int>::max())
|
||||
p.download_limit = -1;
|
||||
else
|
||||
p.download_limit = m_dl_bandwidth_quota.given;
|
||||
|
||||
p.load_balancing = total_free_upload();
|
||||
|
||||
p.download_queue_length = (int)download_queue().size();
|
||||
p.upload_queue_length = (int)upload_queue().size();
|
||||
|
||||
if (boost::optional<piece_block_progress> ret = downloading_piece_progress())
|
||||
{
|
||||
p.downloading_piece_index = ret->piece_index;
|
||||
p.downloading_block_index = ret->block_index;
|
||||
p.downloading_progress = ret->bytes_downloaded;
|
||||
p.downloading_total = ret->full_block_bytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
p.downloading_piece_index = -1;
|
||||
p.downloading_block_index = -1;
|
||||
p.downloading_progress = 0;
|
||||
p.downloading_total = 0;
|
||||
}
|
||||
|
||||
p.flags = 0;
|
||||
if (is_interesting()) p.flags |= peer_info::interesting;
|
||||
if (is_choked()) p.flags |= peer_info::choked;
|
||||
if (is_peer_interested()) p.flags |= peer_info::remote_interested;
|
||||
if (has_peer_choked()) p.flags |= peer_info::remote_choked;
|
||||
if (is_local()) p.flags |= peer_info::local_connection;
|
||||
if (!is_connecting() && m_server_string.empty())
|
||||
p.flags |= peer_info::handshake;
|
||||
if (is_connecting() && !is_queued()) p.flags |= peer_info::connecting;
|
||||
if (is_queued()) p.flags |= peer_info::queued;
|
||||
|
||||
p.pieces = get_bitfield();
|
||||
p.seed = is_seed();
|
||||
|
||||
p.client = m_server_string;
|
||||
p.connection_type = peer_info::web_seed;
|
||||
}
|
||||
|
||||
// throws exception when the client should be disconnected
|
||||
void web_peer_connection::on_sent(asio::error const& error
|
||||
, std::size_t bytes_transferred)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (error) return;
|
||||
m_statistics.sent_bytes(0, bytes_transferred);
|
||||
}
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
void web_peer_connection::check_invariant() const
|
||||
{
|
||||
/*
|
||||
assert(m_num_pieces == std::count(
|
||||
m_have_piece.begin()
|
||||
, m_have_piece.end()
|
||||
, true));
|
||||
*/ }
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
622
flood.py
Normal file
622
flood.py
Normal file
@ -0,0 +1,622 @@
|
||||
#
|
||||
# Copyright (C) 2006 Alon Zakai ('Kripken') <kripkensteiner@gmail.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
|
||||
# Deluge Library, a.k.a. Flood, previously known as python-libtorrent:
|
||||
#
|
||||
# Flood is a Python library for torrenting, that includes
|
||||
# Flood, which is Python code, and Flood_core, which is also a Python
|
||||
# module, but written in C++, and includes the libtorrent torrent library. Only
|
||||
# Flood should be visible, and only it should be imported, in the client.
|
||||
# Flood_core contains mainly libtorrent-interfacing code, and a few other things
|
||||
# that make most sense to write at that level. Flood contains all other
|
||||
# torrent-system management: queueing, configuration management, persistent
|
||||
# list of torrents, etc.
|
||||
#
|
||||
|
||||
# Documentation:
|
||||
# Torrents have 3 structures:
|
||||
# 1. torrent_info - persistent data, like name, upload speed cap, etc.
|
||||
# 2. core_torrent_state - transient state data from the core. This may take
|
||||
# time to calculate, so we do if efficiently
|
||||
# 3. supp_torrent_state - supplementary torrent data, from Flood
|
||||
|
||||
|
||||
import flood_core
|
||||
import os, shutil
|
||||
import pickle
|
||||
import time
|
||||
|
||||
|
||||
# Constants
|
||||
|
||||
TORRENTS_SUBDIR = "torrentfiles"
|
||||
|
||||
STATE_FILENAME = "persistent.state"
|
||||
PREFS_FILENAME = "prefs.state"
|
||||
DHT_FILENAME = "dht.state"
|
||||
|
||||
CACHED_DATA_EXPIRATION = 1 # seconds, like the output of time.time()
|
||||
|
||||
# "max_half_open" : -1,
|
||||
DEFAULT_PREFS = {
|
||||
"max_uploads" : 2, # a.k.a. upload slots
|
||||
"listen_on" : [6881,9999],
|
||||
"max_connections" : 80,
|
||||
"use_DHT" : True,
|
||||
"max_active_torrents" : -1,
|
||||
"auto_seed_ratio" : -1,
|
||||
"max_download_rate" : -1,
|
||||
"max_upload_rate" : -1
|
||||
}
|
||||
|
||||
PREF_FUNCTIONS = {
|
||||
"max_uploads" : flood_core.set_max_uploads,
|
||||
"listen_on" : flood_core.set_listen_on,
|
||||
"max_connections" : flood_core.set_max_connections,
|
||||
"use_DHT" : None, # not a normal pref in that is is applied only on start
|
||||
"max_active_torrents" : None, # no need for a function, applied constantly
|
||||
"auto_seed_ratio" : None, # no need for a function, applied constantly
|
||||
"max_download_rate" : flood_core.set_download_rate_limit,
|
||||
"max_upload_rate" : flood_core.set_upload_rate_limit
|
||||
}
|
||||
|
||||
|
||||
# Exceptions
|
||||
|
||||
class FloodError(Exception):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
||||
|
||||
class InvalidEncodingError(FloodError):
|
||||
pass
|
||||
|
||||
class FilesystemError(FloodError):
|
||||
pass
|
||||
|
||||
class DuplicateTorrentError(FloodError):
|
||||
pass
|
||||
|
||||
class InvalidTorrentError(FloodError):
|
||||
pass
|
||||
|
||||
|
||||
# A cached data item
|
||||
|
||||
class cached_data:
|
||||
def __init__(self, get_method, key):
|
||||
self.get_method = get_method
|
||||
self.key = key
|
||||
|
||||
self.timestamp = -1
|
||||
|
||||
def get(self, efficiently=True):
|
||||
if self.timestamp == -1 or time.time() > self.timestamp + CACHED_DATA_EXPIRATION or \
|
||||
not efficiently:
|
||||
self.data = self.get_method(self.key)
|
||||
self.timestamp = time.time()
|
||||
|
||||
return self.data
|
||||
|
||||
|
||||
# Persistent information for a single torrent
|
||||
|
||||
class torrent_info:
|
||||
def __init__(self, filename, save_dir, compact):
|
||||
self.filename = filename
|
||||
self.save_dir = save_dir
|
||||
self.compact = compact
|
||||
|
||||
self.user_paused = False # start out unpaused
|
||||
self.uploaded_memory = 0
|
||||
|
||||
self.delete_me = False # set this to true, to delete it on next sync
|
||||
|
||||
|
||||
# The persistent state of the torrent system. Everything in this will be pickled
|
||||
|
||||
class persistent_state:
|
||||
def __init__(self):
|
||||
# Torrents
|
||||
self.torrents = []
|
||||
|
||||
# Prepare queue (queue is pickled, just like everything else)
|
||||
self.queue = [] # queue[x] is the unique_ID of the x-th queue position. Simple.
|
||||
|
||||
|
||||
# The manager for the torrent system
|
||||
|
||||
class manager:
|
||||
# blank_slate mode ignores the two pickle files and DHT state file, i.e. you start
|
||||
# completely fresh. When quitting, the old files will be overwritten
|
||||
def __init__(self, client_ID, version, user_agent, base_dir, blank_slate=False):
|
||||
self.base_dir = base_dir
|
||||
|
||||
# Ensure directories exist
|
||||
if not TORRENTS_SUBDIR in os.listdir(self.base_dir):
|
||||
os.mkdir(self.base_dir + "/" + TORRENTS_SUBDIR)
|
||||
|
||||
# Pre-initialize the core's data structures
|
||||
flood_core.pre_init(FloodError,
|
||||
InvalidEncodingError,
|
||||
FilesystemError,
|
||||
DuplicateTorrentError,
|
||||
InvalidTorrentError)
|
||||
|
||||
# Start up the core
|
||||
assert(len(version) == 4)
|
||||
flood_core.init(client_ID,
|
||||
int(version[0]),
|
||||
int(version[1]),
|
||||
int(version[2]),
|
||||
int(version[3]),
|
||||
user_agent)
|
||||
|
||||
self.constants = flood_core.constants()
|
||||
|
||||
# Unique IDs are NOT in the state, since they are temporary for each session
|
||||
self.unique_IDs = {} # unique_ID -> a torrent_info object, i.e. persistent data
|
||||
|
||||
# Saved torrent core_states. We do not poll the core in a costly manner, necessarily
|
||||
self.saved_core_torrent_states = {} # unique_ID -> torrent_state
|
||||
|
||||
# supplementary torrent states
|
||||
self.supp_torrent_states = {} # unique_ID->dict of data
|
||||
|
||||
# Saved torrent core_states. We do not poll the core in a costly manner, necessarily
|
||||
self.saved_core_torrent_peer_infos = {} # unique_ID -> torrent_state
|
||||
|
||||
# Unpickle the preferences, or create a new one
|
||||
self.prefs = DEFAULT_PREFS
|
||||
if not blank_slate:
|
||||
try:
|
||||
pkl_file = open(self.base_dir + "/" + PREFS_FILENAME, 'rb')
|
||||
self.prefs = pickle.load(pkl_file)
|
||||
pkl_file.close()
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
# Apply preferences. Note that this is before any torrents are added
|
||||
self.apply_prefs()
|
||||
|
||||
# Apply DHT, if needed. Note that this is before any torrents are added
|
||||
if self.get_pref('use_DHT'):
|
||||
if not blank_slate:
|
||||
flood_core.start_DHT(self.base_dir + "/" + DHT_FILENAME)
|
||||
else:
|
||||
flood_core.start_DHT("")
|
||||
|
||||
# Unpickle the state, or create a new one
|
||||
if not blank_slate:
|
||||
try:
|
||||
pkl_file = open(self.base_dir + "/" + STATE_FILENAME, 'rb')
|
||||
self.state = pickle.load(pkl_file)
|
||||
pkl_file.close()
|
||||
|
||||
# Sync with the core: tell core about torrents, and get unique_IDs
|
||||
self.sync()
|
||||
|
||||
# Apply all the file filters, right after adding the torrents
|
||||
self.apply_all_file_filters()
|
||||
except IOError:
|
||||
self.state = persistent_state()
|
||||
else:
|
||||
self.state = persistent_state()
|
||||
|
||||
def quit(self):
|
||||
# Analyze data needed for pickling, etc.
|
||||
self.pre_quitting()
|
||||
|
||||
# Pickle the prefs
|
||||
print "Pickling prefs..."
|
||||
output = open(self.base_dir + "/" + PREFS_FILENAME, 'wb')
|
||||
pickle.dump(self.prefs, output)
|
||||
output.close()
|
||||
|
||||
# Pickle the state
|
||||
print "Pickling state..."
|
||||
output = open(self.base_dir + "/" + STATE_FILENAME, 'wb')
|
||||
pickle.dump(self.state, output)
|
||||
output.close()
|
||||
|
||||
# Save fastresume data
|
||||
print "Saving fastresume data..."
|
||||
self.save_fastresume_data()
|
||||
|
||||
# Stop DHT, if needed
|
||||
if self.get_pref('use_DHT'):
|
||||
print "Stopping DHT..."
|
||||
flood_core.stop_DHT(self.base_dir + "/" + DHT_FILENAME)
|
||||
|
||||
# Shutdown torrent core
|
||||
print "Quitting the core..."
|
||||
flood_core.quit()
|
||||
|
||||
def pre_quitting(self):
|
||||
# Save the uploaded data from this session to the existing upload memory
|
||||
for unique_ID in self.unique_IDs.keys():
|
||||
self.unique_IDs[unique_ID].uploaded_memory = \
|
||||
self.unique_IDs[unique_ID].uploaded_memory + \
|
||||
self.get_core_torrent_state(unique_ID, False)['total_upload'] # Purposefully ineffi.
|
||||
|
||||
# Preference management functions
|
||||
|
||||
def get_pref(self, key):
|
||||
# If we have a value, return, else fallback on default_prefs, else raise an error
|
||||
# the fallback is useful if the source has newer prefs than the existing pref state,
|
||||
# which was created by an old version of the source
|
||||
if key in self.prefs.keys():
|
||||
return self.prefs[key]
|
||||
elif key in DEFAULT_PREFS:
|
||||
self.prefs[key] = DEFAULT_PREFS[key]
|
||||
return self.prefs[key]
|
||||
else:
|
||||
raise FloodError("Asked for a pref that doesn't exist: " + key)
|
||||
|
||||
def set_pref(self, key, value):
|
||||
# Make sure this is a valid key
|
||||
if key not in DEFAULT_PREFS.keys():
|
||||
raise FloodError("Asked to change a pref that isn't valid: " + key)
|
||||
|
||||
self.prefs[key] = value
|
||||
|
||||
# Apply the pref, if applicable
|
||||
if PREF_FUNCTIONS[key] is not None:
|
||||
PREF_FUNCTIONS[key](value)
|
||||
|
||||
# Torrent addition and removal functions
|
||||
|
||||
def add_torrent(self, filename, save_dir, compact):
|
||||
self.add_torrent_ns(filename, save_dir, compact)
|
||||
return self.sync() # Syncing will create a new torrent in the core, and return it's ID
|
||||
|
||||
def remove_torrent(self, unique_ID, data_also):
|
||||
# Save some data before we remove the torrent, needed later in this func
|
||||
temp = self.unique_IDs[unique_ID]
|
||||
temp_fileinfo = flood_core.get_fileinfo(unique_ID)
|
||||
|
||||
self.remove_torrent_ns(unique_ID)
|
||||
self.sync()
|
||||
|
||||
# Remove .torrent and .fastresume
|
||||
os.remove(temp.filename)
|
||||
try:
|
||||
# Must be after removal of the torrent, because that saves a new .fastresume
|
||||
os.remove(temp.filename + ".fastresume")
|
||||
except OSError:
|
||||
pass # Perhaps there never was one to begin with
|
||||
|
||||
# Remove data, if asked to do so
|
||||
if data_also:
|
||||
# Must be done AFTER the torrent is removed
|
||||
# Note: can this be to the trash?
|
||||
for filedata in temp_fileinfo:
|
||||
filename = filedata['path']
|
||||
try:
|
||||
os.remove(temp.save_dir + "/" + filename)
|
||||
except OSError:
|
||||
pass # No file just means it wasn't downloaded, we can continue
|
||||
|
||||
# A separate function, because people may want to call it from time to time
|
||||
def save_fastresume_data(self):
|
||||
for unique_ID in self.unique_IDs:
|
||||
flood_core.save_fastresume(unique_ID, self.unique_IDs[unique_ID].filename)
|
||||
|
||||
# State retrieval functions
|
||||
|
||||
def get_state(self):
|
||||
ret = flood_core.get_session_info()
|
||||
|
||||
# Get additional data from our level
|
||||
ret['is_listening'] = flood_core.is_listening()
|
||||
ret['port'] = flood_core.listening_port()
|
||||
if self.get_pref('use_DHT'):
|
||||
ret['DHT_nodes'] = flood_core.get_DHT_info()
|
||||
|
||||
return ret
|
||||
|
||||
# This is the EXTERNAL function, for the GUI. It returns the core_state + supp_state
|
||||
def get_torrent_state(self, unique_ID):
|
||||
ret = self.get_core_torrent_state(unique_ID, True).copy()
|
||||
|
||||
# Add the flood-level things to the flood_core data
|
||||
if self.get_supp_torrent_state(unique_ID) is not None:
|
||||
ret.update(self.get_supp_torrent_state(unique_ID))
|
||||
|
||||
# Get queue position
|
||||
ret['queue_pos'] = self.queue.index(unique_ID)
|
||||
|
||||
return ret
|
||||
|
||||
def get_torrent_peer_info(self, unique_ID):
|
||||
# Perhaps at some time we may add info here
|
||||
return get_core_torrent_peer_info(unique_ID)
|
||||
|
||||
# Queueing functions
|
||||
|
||||
def queue_up(self, unique_ID):
|
||||
curr_index = self.get_queue_index(unique_ID)
|
||||
if curr_index > 0:
|
||||
temp = self.state.queue[curr_index - 1]
|
||||
self.state.queue[curr_index - 1] = unique_ID
|
||||
self.state.queue[curr_index] = temp
|
||||
|
||||
def queue_down(self, unique_ID):
|
||||
curr_index = self.get_queue_index(unique_ID)
|
||||
if curr_index < (len(self.state.queue) - 1):
|
||||
temp = self.state.queue[curr_index + 1]
|
||||
self.state.queue[curr_index + 1] = unique_ID
|
||||
self.state.queue[curr_index] = temp
|
||||
|
||||
def queue_bottom(self, unique_ID):
|
||||
curr_index = self.get_queue_index(unique_ID)
|
||||
if curr_index < (len(self.state.queue) - 1):
|
||||
self.state.queue.remove(curr_index)
|
||||
self.state.queue.append(unique_ID)
|
||||
|
||||
def clear_completed(self):
|
||||
for unique_ID in self.unique_IDs:
|
||||
torrent_state = self.get_core_torrent_state(unique_ID)
|
||||
if torrent_state['progress'] == 100.0:
|
||||
self.remove_torrent_ns(unique_ID)
|
||||
|
||||
self.sync()
|
||||
|
||||
# Enforce the queue: pause/unpause as needed, based on queue and user_pausing
|
||||
# This should be called after changes to relevant parameters (user_pausing, or
|
||||
# altering max_active_torrents), or just from time to time
|
||||
# ___ALL queuing code should be in this function, and ONLY here___
|
||||
def apply_queue(self, efficient = True):
|
||||
# Handle autoseeding - downqueue as needed
|
||||
|
||||
if self.auto_seed_ratio != -1:
|
||||
for unique_ID in self.unique_IDs:
|
||||
if self.get_core_torrent_state(unique_ID, efficient)['is_seed']:
|
||||
torrent_state = self.get_core_torrent_state(unique_ID, efficient)
|
||||
ratio = self.calc_ratio(unique_ID, torrent_state)
|
||||
if ratio >= self.auto_seed_ratio:
|
||||
self.queue_bottom(unique_ID)
|
||||
|
||||
# Pause and resume torrents
|
||||
for index in range(len(self.state.queue)):
|
||||
unique_ID = self.state.queue[index]
|
||||
if (index < self.state.max_active_torrents or self.state_max_active_torrents == -1) \
|
||||
and self.get_core_torrent_state(unique_ID, efficient)['is_paused'] \
|
||||
and not self.is_user_paused(unique_ID):
|
||||
flood_core.resume(unique_ID)
|
||||
elif not self.get_core_torrent_state(unique_ID, efficient)['is_paused'] and \
|
||||
(index >= self.state.max_active_torrents or self.is_user_paused(unique_ID)):
|
||||
flood_core.pause(unique_ID)
|
||||
|
||||
# Event handling
|
||||
|
||||
def handle_events(self):
|
||||
# Handle them for the backend's purposes, but still send them up in case the client
|
||||
# wants to do something - show messages, for example
|
||||
ret = []
|
||||
|
||||
event = flood_core.pop_event()
|
||||
|
||||
while event is not None:
|
||||
# print "EVENT: ", event
|
||||
|
||||
ret.append(event)
|
||||
|
||||
if event['event_type'] is self.constants['EVENT_FINISHED']:
|
||||
# If we are autoseeding, then we need to apply the queue
|
||||
if self.auto_seed_ratio == -1:
|
||||
self.apply_queue(efficient = False) # To work on current data
|
||||
elif event['event_type'] is self.constants['EVENT_TRACKER']:
|
||||
unique_ID = event['unique_ID']
|
||||
status = event['tracker_status']
|
||||
message = event['message']
|
||||
tracker = message[message.find('"')+1:message.rfind('"')]
|
||||
|
||||
self.set_supp_torrent_state_val(unique_ID,
|
||||
"tracker_status",
|
||||
(tracker, status))
|
||||
|
||||
old_state = self.get_supp_torrent_state(unique_ID)
|
||||
try:
|
||||
new = old_state['tracker_messages']
|
||||
except KeyError:
|
||||
new = {}
|
||||
|
||||
new[tracker] = message
|
||||
|
||||
self.set_supp_torrent_state_val(unique_ID,
|
||||
"tracker_messages",
|
||||
new)
|
||||
|
||||
event = flood_core.pop_event()
|
||||
|
||||
return ret
|
||||
|
||||
# Filtering functions
|
||||
|
||||
def set_file_filter(self, unique_ID, file_filter):
|
||||
assert(len(file_filter) == self.get_core_torrent_state(unique_ID, True)['num_files'])
|
||||
|
||||
self.unique_IDs[unique_ID].file_filter = file_filter[:]
|
||||
|
||||
flood_core.set_filter_out(file_filter)
|
||||
|
||||
def get_file_filter(self, unique_ID):
|
||||
try:
|
||||
return self.unique_IDs[unique_ID].file_filter[:]
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
# Called when a session starts, to apply existing filters
|
||||
def apply_all_file_filters(self):
|
||||
for unique_ID in self.unique_IDs.keys():
|
||||
try:
|
||||
self.set_file_filter(unique_ID, self.unique_IDs[unique_ID].file_filter)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# Advanced statistics - these may be SLOW. The client should call these only
|
||||
# when needed, and perhaps only once in a long while (they are mostly just
|
||||
# approximations anyhow
|
||||
|
||||
def calc_availability(self, unique_ID):
|
||||
return flood_stats.calc_availability(self.get_core_torrent_peer_info(unique_ID))
|
||||
|
||||
def calc_swarm_speed(self, unique_ID):
|
||||
pieces_per_sec = flood_stats.calc_swarm_speed(self.get_core_torrent_peer_info(unique_ID))
|
||||
piece_length = self.get_core_torrent_state(unique_ID, efficiently=True)
|
||||
|
||||
return pieces_per_sec * piece_length
|
||||
|
||||
# Miscellaneous minor functions
|
||||
|
||||
def set_user_pause(self, unique_ID, new_value):
|
||||
self.unique_IDs[unique_ID].user_paused = new_value
|
||||
self.apply_queue()
|
||||
|
||||
def is_user_paused(self, unique_ID):
|
||||
return self.unique_IDs[unique_ID].user_paused
|
||||
|
||||
def get_num_torrents(self):
|
||||
return flood_core.get_num_torrents()
|
||||
|
||||
def get_unique_IDs(self):
|
||||
return self.unique_IDs.keys()
|
||||
|
||||
|
||||
####################
|
||||
# Internal functions
|
||||
####################
|
||||
|
||||
# Efficient: use a saved state, if it hasn't expired yet
|
||||
def get_core_torrent_state(self, unique_ID, efficiently=True):
|
||||
if unique_ID not in self.saved_core_torrent_states.keys():
|
||||
self.saved_core_torrent_states[unique_ID] = cached_data(flood_core.get_torrent_state,
|
||||
unique_ID)
|
||||
|
||||
return self.saved_core_torrent_states[unique_ID].get(efficiently)
|
||||
|
||||
def get_supp_torrent_state(self, unique_ID):
|
||||
try:
|
||||
return self.supp_torrent_states[unique_ID]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def set_supp_torrent_state_val(self, unique_ID, key, val):
|
||||
try:
|
||||
if self.supp_torrent_states[unique_ID] is None:
|
||||
self.supp_torrent_states[unique_ID] = {}
|
||||
except KeyError:
|
||||
self.supp_torrent_states[unique_ID] = {}
|
||||
|
||||
self.supp_torrent_states[unique_ID][key] = val
|
||||
|
||||
def get_core_torrent_peer_info(self, unique_ID, efficiently=True):
|
||||
if unique_ID not in self.saved_torrent_peer_infos.keys():
|
||||
self.saved_torrent_peer_infos[unique_ID] = cached_data(flood_core.get_peer_info,
|
||||
unique_ID)
|
||||
|
||||
return self.saved_torrent_peer_infos[unique_ID].get(efficiently)
|
||||
|
||||
# Non-syncing functions. Used when we loop over such events, and sync manually at the end
|
||||
|
||||
def add_torrent_ns(self, filename, save_dir, compact):
|
||||
# Cache torrent file
|
||||
(temp, filename_short) = os.path.split(filename)
|
||||
|
||||
if filename_short in os.listdir(self.base_dir + "/" + TORRENTS_SUBDIR):
|
||||
raise FloodError("Duplicate Torrent, it appears: " + filename_short)
|
||||
|
||||
full_new_name = self.base_dir + "/" + TORRENTS_SUBDIR + "/" + filename_short
|
||||
|
||||
shutil.copy(filename, full_new_name)
|
||||
|
||||
# Create torrent object
|
||||
new_torrent = torrent_info(full_new_name, save_dir, compact)
|
||||
self.state.torrents.append(new_torrent)
|
||||
|
||||
def remove_torrent_ns(self, unique_ID):
|
||||
self.unique_IDs[unique_ID].delete_me = True
|
||||
|
||||
# Sync the state.torrents and unique_IDs lists with the core
|
||||
# ___ALL syncing code with the core is here, and ONLY here___
|
||||
# Also all self-syncing is done here (various lists)
|
||||
|
||||
def sync(self):
|
||||
ret = None # We return new added unique ID(s), or None
|
||||
|
||||
# Add torrents to core and unique_IDs
|
||||
torrents_with_unique_ID = self.unique_IDs.values()
|
||||
|
||||
for torrent in self.state.torrents:
|
||||
if torrent not in torrents_with_unique_ID:
|
||||
# print "Adding torrent to core:", torrent.filename, torrent.save_dir, torrent.compact
|
||||
unique_ID = flood_core.add_torrent(torrent.filename,
|
||||
torrent.save_dir,
|
||||
torrent.compact)
|
||||
# print "Got unique ID:", unique_ID
|
||||
ret = unique_ID
|
||||
self.unique_IDs[unique_ID] = torrent
|
||||
|
||||
# Remove torrents from core, unique_IDs and queue
|
||||
to_delete = []
|
||||
for torrent in self.state.torrents:
|
||||
if torrent.delete_me:
|
||||
flood_core.remove_torrent(torrent.unique_ID, torrent.filename)
|
||||
to_delete.append(torrent.unique_ID)
|
||||
|
||||
for unique_ID in to_delete:
|
||||
self.state.torrents.remove(self.unique_IDs[unique_ID])
|
||||
self.state.queue.remove(self.unique_IDs[unique_ID])
|
||||
del self.unique_IDs[unique_ID]
|
||||
|
||||
# Add torrents to queue - at the end, of course
|
||||
for unique_ID in self.unique_IDs:
|
||||
if unique_ID not in self.state.queue:
|
||||
self.state.queue.append(unique_ID)
|
||||
|
||||
assert(len(self.unique_IDs) == len(self.state.torrents))
|
||||
assert(len(self.unique_IDs) == len(self.state.queue))
|
||||
assert(len(self.unique_IDs) == flood_core.get_num_torrents())
|
||||
|
||||
return ret
|
||||
|
||||
def get_queue_index(self, unique_ID):
|
||||
return self.state.queue.index(unique_ID)
|
||||
|
||||
def apply_prefs(self):
|
||||
print "Applying preferences"
|
||||
assert(len(PREF_FUNCTIONS) == len(DEFAULT_PREFS))
|
||||
|
||||
for pref in PREF_FUNCTIONS.keys():
|
||||
if PREF_FUNCTIONS[pref] is not None:
|
||||
PREF_FUNCTIONS[pref](self.get_pref(pref))
|
||||
|
||||
# Calculations
|
||||
|
||||
def calc_ratio(self, unique_ID, torrent_state):
|
||||
up = float(torrent_state['total_upload'] + self.unique_IDs[unique_ID].uploaded_memory)
|
||||
down = float(torrent_state["total_done"])
|
||||
|
||||
try:
|
||||
ret = float(up/down)
|
||||
except:
|
||||
ret = -1
|
||||
|
||||
return ret
|
||||
78
flood_stats.py
Normal file
78
flood_stats.py
Normal file
@ -0,0 +1,78 @@
|
||||
#
|
||||
# Copyright (C) 2006 Alon Zakai ('Kripken') <kripkensteiner@gmail.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
|
||||
import time
|
||||
|
||||
# Global variables. Caching of saved data, mostly
|
||||
|
||||
old_peer_info = None
|
||||
old_peer_info_timestamp = None
|
||||
|
||||
# Availability - how many complete copies are among our peers
|
||||
def calc_availability(peer_info):
|
||||
if len(peer_info) == 0:
|
||||
return 0
|
||||
|
||||
num_pieces = len(peer_info[0].pieces)
|
||||
|
||||
freqs = [0]*num_pieces
|
||||
|
||||
for peer in peer_info:
|
||||
for piece in num_pieces:
|
||||
freqs[piece] = freqs[piece] + peer['pieces'][piece]
|
||||
|
||||
minimum = min(freqs)
|
||||
# frac = freqs.count(minimum + 1) # Does this mean something?
|
||||
|
||||
return minimum
|
||||
|
||||
# Swarm speed - try to guess the speed of the entire swarm
|
||||
# We return #pieces / second. The calling function should convert pieces to KB, if it wants
|
||||
# Note that we return the delta from the last call. If the client calls too soon, this may
|
||||
# be too unreliable. But the client can smooth things out, if desired
|
||||
def calc_swarm_speed(peer_info):
|
||||
if old_peer_info is not None:
|
||||
new_pieces = 0
|
||||
peers_known = 0
|
||||
|
||||
# List new peers
|
||||
new_peer_IPs = {} # ip->peerinfo dict (from the core)
|
||||
for peer in peer_info:
|
||||
new_peer_IPs[peer['ip']] = peer
|
||||
|
||||
for new_IP in new_peer_IPs.keys():
|
||||
if new_IP in old_peer_IPs.keys():
|
||||
# We know this peer from before, see what changed
|
||||
peers_known = peers_known + 1
|
||||
delta = sum(new_peer_IPs[new_IP].pieces) - sum(old_peer_IPs[new_IP].pieces)
|
||||
|
||||
if delta >= 0:
|
||||
new_pieces = new_pieces + delta
|
||||
else:
|
||||
print "Flood.stat.calc_swarm_speed: Bad Delta: ", delta, old_peer_IPs[new_IP].pieces, new_peer_IPs[new_IP].pieces
|
||||
|
||||
# Calculate final value
|
||||
time_delta = time.time() - old_peer_info_timestamp
|
||||
ret = float(new_pieces)/( float(peers_known) * time_delta )
|
||||
|
||||
# Save info
|
||||
old_peer_info = peer_info
|
||||
old_peer_info_timestamp = time.time()
|
||||
old_peer_IPs = new_peer_IPs
|
||||
|
||||
return ret
|
||||
174
include/libtorrent/alert.hpp
Executable file
174
include/libtorrent/alert.hpp
Executable file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2003, Arvid Norberg, Daniel Wallin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef TORRENT_ALERT_HPP_INCLUDED
|
||||
#define TORRENT_ALERT_HPP_INCLUDED
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <typeinfo>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 1)
|
||||
#endif
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
#include <boost/preprocessor/repetition/enum_params_with_a_default.hpp>
|
||||
#include <boost/preprocessor/repetition/enum.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_params.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_shifted_params.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "libtorrent/config.hpp"
|
||||
|
||||
#define TORRENT_MAX_ALERT_TYPES 10
|
||||
|
||||
namespace libtorrent {
|
||||
|
||||
class TORRENT_EXPORT alert
|
||||
{
|
||||
public:
|
||||
enum severity_t { debug, info, warning, critical, fatal, none };
|
||||
|
||||
alert(severity_t severity, const std::string& msg);
|
||||
virtual ~alert();
|
||||
|
||||
// a timestamp is automatically created in the constructor
|
||||
boost::posix_time::ptime timestamp() const;
|
||||
|
||||
const std::string& msg() const;
|
||||
|
||||
severity_t severity() const;
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const = 0;
|
||||
|
||||
private:
|
||||
std::string m_msg;
|
||||
severity_t m_severity;
|
||||
boost::posix_time::ptime m_timestamp;
|
||||
};
|
||||
|
||||
class TORRENT_EXPORT alert_manager
|
||||
{
|
||||
public:
|
||||
alert_manager();
|
||||
~alert_manager();
|
||||
|
||||
void post_alert(const alert& alert_);
|
||||
bool pending() const;
|
||||
std::auto_ptr<alert> get();
|
||||
|
||||
void set_severity(alert::severity_t severity);
|
||||
bool should_post(alert::severity_t severity) const;
|
||||
|
||||
private:
|
||||
std::queue<alert*> m_alerts;
|
||||
alert::severity_t m_severity;
|
||||
mutable boost::mutex m_mutex;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT unhandled_alert : std::exception
|
||||
{
|
||||
unhandled_alert() {}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct void_;
|
||||
|
||||
template<
|
||||
class Handler
|
||||
, BOOST_PP_ENUM_PARAMS(TORRENT_MAX_ALERT_TYPES, class T)
|
||||
>
|
||||
void handle_alert_dispatch(
|
||||
const std::auto_ptr<alert>& alert_
|
||||
, const Handler& handler
|
||||
, const std::type_info& typeid_
|
||||
, BOOST_PP_ENUM_BINARY_PARAMS(TORRENT_MAX_ALERT_TYPES, T, *p))
|
||||
{
|
||||
if (typeid_ == typeid(T0))
|
||||
handler(*static_cast<T0*>(alert_.get()));
|
||||
else
|
||||
handle_alert_dispatch(
|
||||
alert_
|
||||
, handler
|
||||
, typeid_
|
||||
, BOOST_PP_ENUM_SHIFTED_PARAMS(TORRENT_MAX_ALERT_TYPES, p), (void_*)0
|
||||
);
|
||||
}
|
||||
|
||||
template<class Handler>
|
||||
void handle_alert_dispatch(
|
||||
const std::auto_ptr<alert>& alert_
|
||||
, const Handler& handler
|
||||
, const std::type_info& typeid_
|
||||
, BOOST_PP_ENUM_PARAMS(TORRENT_MAX_ALERT_TYPES, void_* BOOST_PP_INTERCEPT))
|
||||
{
|
||||
throw unhandled_alert();
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<
|
||||
BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(TORRENT_MAX_ALERT_TYPES, class T, detail::void_)
|
||||
>
|
||||
struct TORRENT_EXPORT handle_alert
|
||||
{
|
||||
template<class Handler>
|
||||
handle_alert(
|
||||
const std::auto_ptr<alert>& alert_
|
||||
, const Handler& handler)
|
||||
{
|
||||
#define ALERT_POINTER_TYPE(z, n, text) (BOOST_PP_CAT(T, n)*)0
|
||||
|
||||
detail::handle_alert_dispatch(
|
||||
alert_
|
||||
, handler
|
||||
, typeid(*alert_)
|
||||
, BOOST_PP_ENUM(TORRENT_MAX_ALERT_TYPES, ALERT_POINTER_TYPE, _)
|
||||
);
|
||||
|
||||
#undef ALERT_POINTER_TYPE
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace libtorrent
|
||||
|
||||
#endif // TORRENT_ALERT_HPP_INCLUDED
|
||||
|
||||
300
include/libtorrent/alert_types.hpp
Executable file
300
include/libtorrent/alert_types.hpp
Executable file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2003, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef TORRENT_ALERT_TYPES_HPP_INCLUDED
|
||||
#define TORRENT_ALERT_TYPES_HPP_INCLUDED
|
||||
|
||||
#include "libtorrent/alert.hpp"
|
||||
#include "libtorrent/torrent_handle.hpp"
|
||||
#include "libtorrent/socket.hpp"
|
||||
#include "libtorrent/peer_connection.hpp"
|
||||
#include "libtorrent/config.hpp"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
struct TORRENT_EXPORT tracker_alert: alert
|
||||
{
|
||||
tracker_alert(torrent_handle const& h
|
||||
, int times
|
||||
, int status
|
||||
, std::string const& msg)
|
||||
: alert(alert::warning, msg)
|
||||
, handle(h)
|
||||
, times_in_row(times)
|
||||
, status_code(status)
|
||||
{}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new tracker_alert(*this)); }
|
||||
|
||||
torrent_handle handle;
|
||||
int times_in_row;
|
||||
int status_code;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT tracker_warning_alert: alert
|
||||
{
|
||||
tracker_warning_alert(torrent_handle const& h
|
||||
, std::string const& msg)
|
||||
: alert(alert::warning, msg)
|
||||
, handle(h)
|
||||
{}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new tracker_warning_alert(*this)); }
|
||||
|
||||
torrent_handle handle;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct TORRENT_EXPORT tracker_reply_alert: alert
|
||||
{
|
||||
tracker_reply_alert(torrent_handle const& h
|
||||
, std::string const& msg)
|
||||
: alert(alert::info, msg)
|
||||
, handle(h)
|
||||
{}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new tracker_reply_alert(*this)); }
|
||||
|
||||
torrent_handle handle;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT tracker_announce_alert: alert
|
||||
{
|
||||
tracker_announce_alert(torrent_handle const& h, std::string const& msg)
|
||||
: alert(alert::info, msg)
|
||||
, handle(h)
|
||||
{}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new tracker_announce_alert(*this)); }
|
||||
|
||||
torrent_handle handle;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT hash_failed_alert: alert
|
||||
{
|
||||
hash_failed_alert(
|
||||
torrent_handle const& h
|
||||
, int index
|
||||
, std::string const& msg)
|
||||
: alert(alert::info, msg)
|
||||
, handle(h)
|
||||
, piece_index(index)
|
||||
{ assert(index >= 0);}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new hash_failed_alert(*this)); }
|
||||
|
||||
torrent_handle handle;
|
||||
int piece_index;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT peer_ban_alert: alert
|
||||
{
|
||||
peer_ban_alert(tcp::endpoint const& pip, torrent_handle h, std::string const& msg)
|
||||
: alert(alert::info, msg)
|
||||
, ip(pip)
|
||||
, handle(h)
|
||||
{}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new peer_ban_alert(*this)); }
|
||||
|
||||
tcp::endpoint ip;
|
||||
torrent_handle handle;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT peer_error_alert: alert
|
||||
{
|
||||
peer_error_alert(tcp::endpoint const& pip, peer_id const& pid_, std::string const& msg)
|
||||
: alert(alert::debug, msg)
|
||||
, ip(pip)
|
||||
, pid(pid_)
|
||||
{}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new peer_error_alert(*this)); }
|
||||
|
||||
tcp::endpoint ip;
|
||||
peer_id pid;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT chat_message_alert: alert
|
||||
{
|
||||
chat_message_alert(
|
||||
const torrent_handle& h
|
||||
, const tcp::endpoint& sender
|
||||
, const std::string& msg)
|
||||
: alert(alert::critical, msg)
|
||||
, handle(h)
|
||||
, ip(sender)
|
||||
{}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new chat_message_alert(*this)); }
|
||||
|
||||
torrent_handle handle;
|
||||
tcp::endpoint ip;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT invalid_request_alert: alert
|
||||
{
|
||||
invalid_request_alert(
|
||||
peer_request const& r
|
||||
, torrent_handle const& h
|
||||
, tcp::endpoint const& sender
|
||||
, peer_id const& pid_
|
||||
, std::string const& msg)
|
||||
: alert(alert::debug, msg)
|
||||
, handle(h)
|
||||
, ip(sender)
|
||||
, request(r)
|
||||
, pid(pid_)
|
||||
{}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new invalid_request_alert(*this)); }
|
||||
|
||||
torrent_handle handle;
|
||||
tcp::endpoint ip;
|
||||
peer_request request;
|
||||
peer_id pid;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT torrent_finished_alert: alert
|
||||
{
|
||||
torrent_finished_alert(
|
||||
const torrent_handle& h
|
||||
, const std::string& msg)
|
||||
: alert(alert::warning, msg)
|
||||
, handle(h)
|
||||
{}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new torrent_finished_alert(*this)); }
|
||||
|
||||
torrent_handle handle;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT url_seed_alert: alert
|
||||
{
|
||||
url_seed_alert(
|
||||
const std::string& url_
|
||||
, const std::string& msg)
|
||||
: alert(alert::warning, msg)
|
||||
, url(url_)
|
||||
{}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new url_seed_alert(*this)); }
|
||||
|
||||
std::string url;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT file_error_alert: alert
|
||||
{
|
||||
file_error_alert(
|
||||
const torrent_handle& h
|
||||
, const std::string& msg)
|
||||
: alert(alert::fatal, msg)
|
||||
, handle(h)
|
||||
{}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new file_error_alert(*this)); }
|
||||
|
||||
torrent_handle handle;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT metadata_failed_alert: alert
|
||||
{
|
||||
metadata_failed_alert(
|
||||
const torrent_handle& h
|
||||
, const std::string& msg)
|
||||
: alert(alert::info, msg)
|
||||
, handle(h)
|
||||
{}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new metadata_failed_alert(*this)); }
|
||||
|
||||
torrent_handle handle;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT metadata_received_alert: alert
|
||||
{
|
||||
metadata_received_alert(
|
||||
const torrent_handle& h
|
||||
, const std::string& msg)
|
||||
: alert(alert::info, msg)
|
||||
, handle(h)
|
||||
{}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new metadata_received_alert(*this)); }
|
||||
|
||||
torrent_handle handle;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT listen_failed_alert: alert
|
||||
{
|
||||
listen_failed_alert(
|
||||
const std::string& msg)
|
||||
: alert(alert::fatal, msg)
|
||||
{}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new listen_failed_alert(*this)); }
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT fastresume_rejected_alert: alert
|
||||
{
|
||||
fastresume_rejected_alert(torrent_handle const& h
|
||||
, std::string const& msg)
|
||||
: alert(alert::warning, msg)
|
||||
, handle(h)
|
||||
{}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new fastresume_rejected_alert(*this)); }
|
||||
|
||||
torrent_handle handle;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
72
include/libtorrent/allocate_resources.hpp
Normal file
72
include/libtorrent/allocate_resources.hpp
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2003, Magnus Jonsson
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef TORRENT_ALLOCATE_RESOURCES_HPP_INCLUDED
|
||||
#define TORRENT_ALLOCATE_RESOURCES_HPP_INCLUDED
|
||||
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "libtorrent/resource_request.hpp"
|
||||
#include "libtorrent/peer_id.hpp"
|
||||
#include "libtorrent/socket.hpp"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
class peer_connection;
|
||||
class torrent;
|
||||
|
||||
int saturated_add(int a, int b);
|
||||
|
||||
// Function to allocate a limited resource fairly among many consumers.
|
||||
// It takes into account the current use, and the consumer's desired use.
|
||||
// Should be invoked periodically to allow it adjust to the situation (make
|
||||
// sure "used" is updated between calls!).
|
||||
// If resources = std::numeric_limits<int>::max() it means there is an infinite
|
||||
// supply of resources (so everyone can get what they want).
|
||||
|
||||
void allocate_resources(
|
||||
int resources
|
||||
, std::map<sha1_hash, boost::shared_ptr<torrent> >& torrents
|
||||
, resource_request torrent::* res);
|
||||
|
||||
void allocate_resources(
|
||||
int resources
|
||||
, std::map<tcp::endpoint, peer_connection*>& connections
|
||||
, resource_request peer_connection::* res);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
71
include/libtorrent/asio.hpp
Normal file
71
include/libtorrent/asio.hpp
Normal file
@ -0,0 +1,71 @@
|
||||
//
|
||||
// asio.hpp
|
||||
// ~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_HPP
|
||||
#define ASIO_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/basic_datagram_socket.hpp"
|
||||
#include "asio/basic_deadline_timer.hpp"
|
||||
#include "asio/basic_io_object.hpp"
|
||||
#include "asio/basic_resolver.hpp"
|
||||
#include "asio/basic_socket_acceptor.hpp"
|
||||
#include "asio/basic_socket_iostream.hpp"
|
||||
#include "asio/basic_socket_streambuf.hpp"
|
||||
#include "asio/basic_stream_socket.hpp"
|
||||
#include "asio/basic_streambuf.hpp"
|
||||
#include "asio/buffer.hpp"
|
||||
#include "asio/buffered_read_stream_fwd.hpp"
|
||||
#include "asio/buffered_read_stream.hpp"
|
||||
#include "asio/buffered_stream_fwd.hpp"
|
||||
#include "asio/buffered_stream.hpp"
|
||||
#include "asio/buffered_write_stream_fwd.hpp"
|
||||
#include "asio/buffered_write_stream.hpp"
|
||||
#include "asio/completion_condition.hpp"
|
||||
#include "asio/datagram_socket_service.hpp"
|
||||
#include "asio/deadline_timer_service.hpp"
|
||||
#include "asio/deadline_timer.hpp"
|
||||
#include "asio/error_handler.hpp"
|
||||
#include "asio/error.hpp"
|
||||
#include "asio/handler_alloc_hook.hpp"
|
||||
#include "asio/handler_invoke_hook.hpp"
|
||||
#include "asio/io_service.hpp"
|
||||
#include "asio/ip/address.hpp"
|
||||
#include "asio/ip/address_v4.hpp"
|
||||
#include "asio/ip/address_v6.hpp"
|
||||
#include "asio/ip/basic_endpoint.hpp"
|
||||
#include "asio/ip/basic_resolver_entry.hpp"
|
||||
#include "asio/ip/basic_resolver_iterator.hpp"
|
||||
#include "asio/ip/basic_resolver_query.hpp"
|
||||
#include "asio/ip/host_name.hpp"
|
||||
#include "asio/ip/multicast.hpp"
|
||||
#include "asio/ip/resolver_query_base.hpp"
|
||||
#include "asio/ip/tcp.hpp"
|
||||
#include "asio/ip/udp.hpp"
|
||||
#include "asio/is_read_buffered.hpp"
|
||||
#include "asio/is_write_buffered.hpp"
|
||||
#include "asio/placeholders.hpp"
|
||||
#include "asio/read.hpp"
|
||||
#include "asio/read_until.hpp"
|
||||
#include "asio/resolver_service.hpp"
|
||||
#include "asio/socket_acceptor_service.hpp"
|
||||
#include "asio/socket_base.hpp"
|
||||
#include "asio/strand.hpp"
|
||||
#include "asio/stream_socket_service.hpp"
|
||||
#include "asio/streambuf.hpp"
|
||||
#include "asio/system_exception.hpp"
|
||||
#include "asio/thread.hpp"
|
||||
#include "asio/time_traits.hpp"
|
||||
#include "asio/write.hpp"
|
||||
|
||||
#endif // ASIO_HPP
|
||||
802
include/libtorrent/asio/basic_datagram_socket.hpp
Normal file
802
include/libtorrent/asio/basic_datagram_socket.hpp
Normal file
@ -0,0 +1,802 @@
|
||||
//
|
||||
// basic_datagram_socket.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_BASIC_DATAGRAM_SOCKET_HPP
|
||||
#define ASIO_BASIC_DATAGRAM_SOCKET_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstddef>
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/basic_socket.hpp"
|
||||
#include "asio/datagram_socket_service.hpp"
|
||||
#include "asio/error_handler.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
/// Provides datagram-oriented socket functionality.
|
||||
/**
|
||||
* The basic_datagram_socket class template provides asynchronous and blocking
|
||||
* datagram-oriented socket functionality.
|
||||
*
|
||||
* @par Thread Safety:
|
||||
* @e Distinct @e objects: Safe.@n
|
||||
* @e Shared @e objects: Unsafe.
|
||||
*
|
||||
* @par Concepts:
|
||||
* Async_Object, Error_Source.
|
||||
*/
|
||||
template <typename Protocol,
|
||||
typename Service = datagram_socket_service<Protocol> >
|
||||
class basic_datagram_socket
|
||||
: public basic_socket<Protocol, Service>
|
||||
{
|
||||
public:
|
||||
/// The native representation of a socket.
|
||||
typedef typename Service::native_type native_type;
|
||||
|
||||
/// The protocol type.
|
||||
typedef Protocol protocol_type;
|
||||
|
||||
/// The endpoint type.
|
||||
typedef typename Protocol::endpoint endpoint_type;
|
||||
|
||||
/// Construct a basic_datagram_socket without opening it.
|
||||
/**
|
||||
* This constructor creates a datagram socket without opening it. The open()
|
||||
* function must be called before data can be sent or received on the socket.
|
||||
*
|
||||
* @param io_service The io_service object that the datagram socket will use
|
||||
* to dispatch handlers for any asynchronous operations performed on the
|
||||
* socket.
|
||||
*/
|
||||
explicit basic_datagram_socket(asio::io_service& io_service)
|
||||
: basic_socket<Protocol, Service>(io_service)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct and open a basic_datagram_socket.
|
||||
/**
|
||||
* This constructor creates and opens a datagram socket.
|
||||
*
|
||||
* @param io_service The io_service object that the datagram socket will use
|
||||
* to dispatch handlers for any asynchronous operations performed on the
|
||||
* socket.
|
||||
*
|
||||
* @param protocol An object specifying protocol parameters to be used.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
basic_datagram_socket(asio::io_service& io_service,
|
||||
const protocol_type& protocol)
|
||||
: basic_socket<Protocol, Service>(io_service, protocol)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct a basic_datagram_socket, opening it and binding it to the given
|
||||
/// local endpoint.
|
||||
/**
|
||||
* This constructor creates a datagram socket and automatically opens it bound
|
||||
* to the specified endpoint on the local machine. The protocol used is the
|
||||
* protocol associated with the given endpoint.
|
||||
*
|
||||
* @param io_service The io_service object that the datagram socket will use
|
||||
* to dispatch handlers for any asynchronous operations performed on the
|
||||
* socket.
|
||||
*
|
||||
* @param endpoint An endpoint on the local machine to which the datagram
|
||||
* socket will be bound.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
basic_datagram_socket(asio::io_service& io_service,
|
||||
const endpoint_type& endpoint)
|
||||
: basic_socket<Protocol, Service>(io_service, endpoint)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct a basic_datagram_socket on an existing native socket.
|
||||
/**
|
||||
* This constructor creates a datagram socket object to hold an existing
|
||||
* native socket.
|
||||
*
|
||||
* @param io_service The io_service object that the datagram socket will use
|
||||
* to dispatch handlers for any asynchronous operations performed on the
|
||||
* socket.
|
||||
*
|
||||
* @param protocol An object specifying protocol parameters to be used.
|
||||
*
|
||||
* @param native_socket The new underlying socket implementation.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
basic_datagram_socket(asio::io_service& io_service,
|
||||
const protocol_type& protocol, const native_type& native_socket)
|
||||
: basic_socket<Protocol, Service>(io_service, protocol, native_socket)
|
||||
{
|
||||
}
|
||||
|
||||
/// Send some data on a connected socket.
|
||||
/**
|
||||
* This function is used to send data on the datagram socket. The function
|
||||
* call will block until the data has been sent successfully or an error
|
||||
* occurs.
|
||||
*
|
||||
* @param buffers One ore more data buffers to be sent on the socket.
|
||||
*
|
||||
* @returns The number of bytes sent.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @note The send operation can only be used with a connected socket. Use
|
||||
* the send_to function to send data on an unconnected datagram socket.
|
||||
*
|
||||
* @par Example:
|
||||
* To send a single data buffer use the @ref buffer function as follows:
|
||||
* @code socket.send(asio::buffer(data, size)); @endcode
|
||||
* See the @ref buffer documentation for information on sending multiple
|
||||
* buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Const_Buffers>
|
||||
std::size_t send(const Const_Buffers& buffers)
|
||||
{
|
||||
return this->service.send(this->implementation, buffers, 0, throw_error());
|
||||
}
|
||||
|
||||
/// Send some data on a connected socket.
|
||||
/**
|
||||
* This function is used to send data on the datagram socket. The function
|
||||
* call will block until the data has been sent successfully or an error
|
||||
* occurs.
|
||||
*
|
||||
* @param buffers One ore more data buffers to be sent on the socket.
|
||||
*
|
||||
* @param flags Flags specifying how the send call is to be made.
|
||||
*
|
||||
* @returns The number of bytes sent.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @note The send operation can only be used with a connected socket. Use
|
||||
* the send_to function to send data on an unconnected datagram socket.
|
||||
*/
|
||||
template <typename Const_Buffers>
|
||||
std::size_t send(const Const_Buffers& buffers,
|
||||
socket_base::message_flags flags)
|
||||
{
|
||||
return this->service.send(this->implementation, buffers, flags,
|
||||
throw_error());
|
||||
}
|
||||
|
||||
/// Send some data on a connected socket.
|
||||
/**
|
||||
* This function is used to send data on the datagram socket. The function
|
||||
* call will block until the data has been sent successfully or an error
|
||||
* occurs.
|
||||
*
|
||||
* @param buffers One or more data buffers to be sent on the socket.
|
||||
*
|
||||
* @param flags Flags specifying how the send call is to be made.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation.
|
||||
* ); @endcode
|
||||
*
|
||||
* @returns The number of bytes sent.
|
||||
*
|
||||
* @note The send operation can only be used with a connected socket. Use
|
||||
* the send_to function to send data on an unconnected datagram socket.
|
||||
*/
|
||||
template <typename Const_Buffers, typename Error_Handler>
|
||||
std::size_t send(const Const_Buffers& buffers,
|
||||
socket_base::message_flags flags, Error_Handler error_handler)
|
||||
{
|
||||
return this->service.send(this->implementation, buffers, flags,
|
||||
error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous send on a connected socket.
|
||||
/**
|
||||
* This function is used to send data on the datagram socket. The function
|
||||
* call will block until the data has been sent successfully or an error
|
||||
* occurs.
|
||||
*
|
||||
* @param buffers One or more data buffers to be sent on the socket. Although
|
||||
* the buffers object may be copied as necessary, ownership of the underlying
|
||||
* memory blocks is retained by the caller, which must guarantee that they
|
||||
* remain valid until the handler is called.
|
||||
*
|
||||
* @param handler The handler to be called when the send operation completes.
|
||||
* Copies will be made of the handler as required. The function signature of
|
||||
* the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error, // Result of operation.
|
||||
* std::size_t bytes_transferred // Number of bytes sent.
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*
|
||||
* @note The async_send operation can only be used with a connected socket.
|
||||
* Use the async_send_to function to send data on an unconnected datagram
|
||||
* socket.
|
||||
*
|
||||
* @par Example:
|
||||
* To send a single data buffer use the @ref buffer function as follows:
|
||||
* @code
|
||||
* socket.async_send(asio::buffer(data, size), handler);
|
||||
* @endcode
|
||||
* See the @ref buffer documentation for information on sending multiple
|
||||
* buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Const_Buffers, typename Handler>
|
||||
void async_send(const Const_Buffers& buffers, Handler handler)
|
||||
{
|
||||
this->service.async_send(this->implementation, buffers, 0, handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous send on a connected socket.
|
||||
/**
|
||||
* This function is used to send data on the datagram socket. The function
|
||||
* call will block until the data has been sent successfully or an error
|
||||
* occurs.
|
||||
*
|
||||
* @param buffers One or more data buffers to be sent on the socket. Although
|
||||
* the buffers object may be copied as necessary, ownership of the underlying
|
||||
* memory blocks is retained by the caller, which must guarantee that they
|
||||
* remain valid until the handler is called.
|
||||
*
|
||||
* @param flags Flags specifying how the send call is to be made.
|
||||
*
|
||||
* @param handler The handler to be called when the send operation completes.
|
||||
* Copies will be made of the handler as required. The function signature of
|
||||
* the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error, // Result of operation.
|
||||
* std::size_t bytes_transferred // Number of bytes sent.
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*
|
||||
* @note The async_send operation can only be used with a connected socket.
|
||||
* Use the async_send_to function to send data on an unconnected datagram
|
||||
* socket.
|
||||
*/
|
||||
template <typename Const_Buffers, typename Handler>
|
||||
void async_send(const Const_Buffers& buffers,
|
||||
socket_base::message_flags flags, Handler handler)
|
||||
{
|
||||
this->service.async_send(this->implementation, buffers, flags, handler);
|
||||
}
|
||||
|
||||
/// Send a datagram to the specified endpoint.
|
||||
/**
|
||||
* This function is used to send a datagram to the specified remote endpoint.
|
||||
* The function call will block until the data has been sent successfully or
|
||||
* an error occurs.
|
||||
*
|
||||
* @param buffers One or more data buffers to be sent to the remote endpoint.
|
||||
*
|
||||
* @param destination The remote endpoint to which the data will be sent.
|
||||
*
|
||||
* @returns The number of bytes sent.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @par Example:
|
||||
* To send a single data buffer use the @ref buffer function as follows:
|
||||
* @code
|
||||
* asio::ip::udp::endpoint destination(
|
||||
* asio::ip::address::from_string("1.2.3.4"), 12345);
|
||||
* socket.send_to(asio::buffer(data, size), destination);
|
||||
* @endcode
|
||||
* See the @ref buffer documentation for information on sending multiple
|
||||
* buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Const_Buffers>
|
||||
std::size_t send_to(const Const_Buffers& buffers,
|
||||
const endpoint_type& destination)
|
||||
{
|
||||
return this->service.send_to(this->implementation, buffers, destination, 0,
|
||||
throw_error());
|
||||
}
|
||||
|
||||
/// Send a datagram to the specified endpoint.
|
||||
/**
|
||||
* This function is used to send a datagram to the specified remote endpoint.
|
||||
* The function call will block until the data has been sent successfully or
|
||||
* an error occurs.
|
||||
*
|
||||
* @param buffers One or more data buffers to be sent to the remote endpoint.
|
||||
*
|
||||
* @param destination The remote endpoint to which the data will be sent.
|
||||
*
|
||||
* @param flags Flags specifying how the send call is to be made.
|
||||
*
|
||||
* @returns The number of bytes sent.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
template <typename Const_Buffers>
|
||||
std::size_t send_to(const Const_Buffers& buffers,
|
||||
const endpoint_type& destination, socket_base::message_flags flags)
|
||||
{
|
||||
return this->service.send_to(this->implementation, buffers, destination,
|
||||
flags, throw_error());
|
||||
}
|
||||
|
||||
/// Send a datagram to the specified endpoint.
|
||||
/**
|
||||
* This function is used to send a datagram to the specified remote endpoint.
|
||||
* The function call will block until the data has been sent successfully or
|
||||
* an error occurs.
|
||||
*
|
||||
* @param buffers One or more data buffers to be sent to the remote endpoint.
|
||||
*
|
||||
* @param destination The remote endpoint to which the data will be sent.
|
||||
*
|
||||
* @param flags Flags specifying how the send call is to be made.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation.
|
||||
* ); @endcode
|
||||
*
|
||||
* @returns The number of bytes sent.
|
||||
*/
|
||||
template <typename Const_Buffers, typename Error_Handler>
|
||||
std::size_t send_to(const Const_Buffers& buffers,
|
||||
const endpoint_type& destination, socket_base::message_flags flags,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
return this->service.send_to(this->implementation, buffers, destination,
|
||||
flags, error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous send.
|
||||
/**
|
||||
* This function is used to asynchronously send a datagram to the specified
|
||||
* remote endpoint. The function call always returns immediately.
|
||||
*
|
||||
* @param buffers One or more data buffers to be sent to the remote endpoint.
|
||||
* Although the buffers object may be copied as necessary, ownership of the
|
||||
* underlying memory blocks is retained by the caller, which must guarantee
|
||||
* that they remain valid until the handler is called.
|
||||
*
|
||||
* @param destination The remote endpoint to which the data will be sent.
|
||||
* Copies will be made of the endpoint as required.
|
||||
*
|
||||
* @param handler The handler to be called when the send operation completes.
|
||||
* Copies will be made of the handler as required. The function signature of
|
||||
* the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error, // Result of operation.
|
||||
* std::size_t bytes_transferred // Number of bytes sent.
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*
|
||||
* @par Example:
|
||||
* To send a single data buffer use the @ref buffer function as follows:
|
||||
* @code
|
||||
* asio::ip::udp::endpoint destination(
|
||||
* asio::ip::address::from_string("1.2.3.4"), 12345);
|
||||
* socket.async_send_to(
|
||||
* asio::buffer(data, size), destination, handler);
|
||||
* @endcode
|
||||
* See the @ref buffer documentation for information on sending multiple
|
||||
* buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Const_Buffers, typename Handler>
|
||||
void async_send_to(const Const_Buffers& buffers,
|
||||
const endpoint_type& destination, Handler handler)
|
||||
{
|
||||
this->service.async_send_to(this->implementation, buffers, destination, 0,
|
||||
handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous send.
|
||||
/**
|
||||
* This function is used to asynchronously send a datagram to the specified
|
||||
* remote endpoint. The function call always returns immediately.
|
||||
*
|
||||
* @param buffers One or more data buffers to be sent to the remote endpoint.
|
||||
* Although the buffers object may be copied as necessary, ownership of the
|
||||
* underlying memory blocks is retained by the caller, which must guarantee
|
||||
* that they remain valid until the handler is called.
|
||||
*
|
||||
* @param flags Flags specifying how the send call is to be made.
|
||||
*
|
||||
* @param destination The remote endpoint to which the data will be sent.
|
||||
* Copies will be made of the endpoint as required.
|
||||
*
|
||||
* @param handler The handler to be called when the send operation completes.
|
||||
* Copies will be made of the handler as required. The function signature of
|
||||
* the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error, // Result of operation.
|
||||
* std::size_t bytes_transferred // Number of bytes sent.
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*/
|
||||
template <typename Const_Buffers, typename Handler>
|
||||
void async_send_to(const Const_Buffers& buffers,
|
||||
const endpoint_type& destination, socket_base::message_flags flags,
|
||||
Handler handler)
|
||||
{
|
||||
this->service.async_send_to(this->implementation, buffers, destination,
|
||||
flags, handler);
|
||||
}
|
||||
|
||||
/// Receive some data on a connected socket.
|
||||
/**
|
||||
* This function is used to receive data on the datagram socket. The function
|
||||
* call will block until data has been received successfully or an error
|
||||
* occurs.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be received.
|
||||
*
|
||||
* @returns The number of bytes received.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @note The receive operation can only be used with a connected socket. Use
|
||||
* the receive_from function to receive data on an unconnected datagram
|
||||
* socket.
|
||||
*
|
||||
* @par Example:
|
||||
* To receive into a single data buffer use the @ref buffer function as
|
||||
* follows:
|
||||
* @code socket.receive(asio::buffer(data, size)); @endcode
|
||||
* See the @ref buffer documentation for information on receiving into
|
||||
* multiple buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Mutable_Buffers>
|
||||
std::size_t receive(const Mutable_Buffers& buffers)
|
||||
{
|
||||
return this->service.receive(this->implementation, buffers, 0,
|
||||
throw_error());
|
||||
}
|
||||
|
||||
/// Receive some data on a connected socket.
|
||||
/**
|
||||
* This function is used to receive data on the datagram socket. The function
|
||||
* call will block until data has been received successfully or an error
|
||||
* occurs.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be received.
|
||||
*
|
||||
* @param flags Flags specifying how the receive call is to be made.
|
||||
*
|
||||
* @returns The number of bytes received.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @note The receive operation can only be used with a connected socket. Use
|
||||
* the receive_from function to receive data on an unconnected datagram
|
||||
* socket.
|
||||
*/
|
||||
template <typename Mutable_Buffers>
|
||||
std::size_t receive(const Mutable_Buffers& buffers,
|
||||
socket_base::message_flags flags)
|
||||
{
|
||||
return this->service.receive(this->implementation, buffers, flags,
|
||||
throw_error());
|
||||
}
|
||||
|
||||
/// Receive some data on a connected socket.
|
||||
/**
|
||||
* This function is used to receive data on the datagram socket. The function
|
||||
* call will block until data has been received successfully or an error
|
||||
* occurs.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be received.
|
||||
*
|
||||
* @param flags Flags specifying how the receive call is to be made.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation.
|
||||
* ); @endcode
|
||||
*
|
||||
* @returns The number of bytes received.
|
||||
*
|
||||
* @note The receive operation can only be used with a connected socket. Use
|
||||
* the receive_from function to receive data on an unconnected datagram
|
||||
* socket.
|
||||
*/
|
||||
template <typename Mutable_Buffers, typename Error_Handler>
|
||||
std::size_t receive(const Mutable_Buffers& buffers,
|
||||
socket_base::message_flags flags, Error_Handler error_handler)
|
||||
{
|
||||
return this->service.receive(this->implementation, buffers, flags,
|
||||
error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous receive on a connected socket.
|
||||
/**
|
||||
* This function is used to asynchronously receive data from the datagram
|
||||
* socket. The function call always returns immediately.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be received.
|
||||
* Although the buffers object may be copied as necessary, ownership of the
|
||||
* underlying memory blocks is retained by the caller, which must guarantee
|
||||
* that they remain valid until the handler is called.
|
||||
*
|
||||
* @param handler The handler to be called when the receive operation
|
||||
* completes. Copies will be made of the handler as required. The function
|
||||
* signature of the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error, // Result of operation.
|
||||
* std::size_t bytes_transferred // Number of bytes received.
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*
|
||||
* @note The async_receive operation can only be used with a connected socket.
|
||||
* Use the async_receive_from function to receive data on an unconnected
|
||||
* datagram socket.
|
||||
*
|
||||
* @par Example:
|
||||
* To receive into a single data buffer use the @ref buffer function as
|
||||
* follows:
|
||||
* @code
|
||||
* socket.async_receive(asio::buffer(data, size), handler);
|
||||
* @endcode
|
||||
* See the @ref buffer documentation for information on receiving into
|
||||
* multiple buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Mutable_Buffers, typename Handler>
|
||||
void async_receive(const Mutable_Buffers& buffers, Handler handler)
|
||||
{
|
||||
this->service.async_receive(this->implementation, buffers, 0, handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous receive on a connected socket.
|
||||
/**
|
||||
* This function is used to asynchronously receive data from the datagram
|
||||
* socket. The function call always returns immediately.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be received.
|
||||
* Although the buffers object may be copied as necessary, ownership of the
|
||||
* underlying memory blocks is retained by the caller, which must guarantee
|
||||
* that they remain valid until the handler is called.
|
||||
*
|
||||
* @param flags Flags specifying how the receive call is to be made.
|
||||
*
|
||||
* @param handler The handler to be called when the receive operation
|
||||
* completes. Copies will be made of the handler as required. The function
|
||||
* signature of the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error, // Result of operation.
|
||||
* std::size_t bytes_transferred // Number of bytes received.
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*
|
||||
* @note The async_receive operation can only be used with a connected socket.
|
||||
* Use the async_receive_from function to receive data on an unconnected
|
||||
* datagram socket.
|
||||
*/
|
||||
template <typename Mutable_Buffers, typename Handler>
|
||||
void async_receive(const Mutable_Buffers& buffers,
|
||||
socket_base::message_flags flags, Handler handler)
|
||||
{
|
||||
this->service.async_receive(this->implementation, buffers, flags, handler);
|
||||
}
|
||||
|
||||
/// Receive a datagram with the endpoint of the sender.
|
||||
/**
|
||||
* This function is used to receive a datagram. The function call will block
|
||||
* until data has been received successfully or an error occurs.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be received.
|
||||
*
|
||||
* @param sender_endpoint An endpoint object that receives the endpoint of
|
||||
* the remote sender of the datagram.
|
||||
*
|
||||
* @returns The number of bytes received.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @par Example:
|
||||
* To receive into a single data buffer use the @ref buffer function as
|
||||
* follows:
|
||||
* @code
|
||||
* asio::ip::udp::endpoint sender_endpoint;
|
||||
* socket.receive_from(
|
||||
* asio::buffer(data, size), sender_endpoint);
|
||||
* @endcode
|
||||
* See the @ref buffer documentation for information on receiving into
|
||||
* multiple buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Mutable_Buffers>
|
||||
std::size_t receive_from(const Mutable_Buffers& buffers,
|
||||
endpoint_type& sender_endpoint)
|
||||
{
|
||||
return this->service.receive_from(this->implementation, buffers,
|
||||
sender_endpoint, 0, throw_error());
|
||||
}
|
||||
|
||||
/// Receive a datagram with the endpoint of the sender.
|
||||
/**
|
||||
* This function is used to receive a datagram. The function call will block
|
||||
* until data has been received successfully or an error occurs.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be received.
|
||||
*
|
||||
* @param sender_endpoint An endpoint object that receives the endpoint of
|
||||
* the remote sender of the datagram.
|
||||
*
|
||||
* @param flags Flags specifying how the receive call is to be made.
|
||||
*
|
||||
* @returns The number of bytes received.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
template <typename Mutable_Buffers>
|
||||
std::size_t receive_from(const Mutable_Buffers& buffers,
|
||||
endpoint_type& sender_endpoint, socket_base::message_flags flags)
|
||||
{
|
||||
return this->service.receive_from(this->implementation, buffers,
|
||||
sender_endpoint, flags, throw_error());
|
||||
}
|
||||
|
||||
/// Receive a datagram with the endpoint of the sender.
|
||||
/**
|
||||
* This function is used to receive a datagram. The function call will block
|
||||
* until data has been received successfully or an error occurs.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be received.
|
||||
*
|
||||
* @param sender_endpoint An endpoint object that receives the endpoint of
|
||||
* the remote sender of the datagram.
|
||||
*
|
||||
* @param flags Flags specifying how the receive call is to be made.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation.
|
||||
* ); @endcode
|
||||
*
|
||||
* @returns The number of bytes received.
|
||||
*/
|
||||
template <typename Mutable_Buffers, typename Error_Handler>
|
||||
std::size_t receive_from(const Mutable_Buffers& buffers,
|
||||
endpoint_type& sender_endpoint, socket_base::message_flags flags,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
return this->service.receive_from(this->implementation, buffers,
|
||||
sender_endpoint, flags, error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous receive.
|
||||
/**
|
||||
* This function is used to asynchronously receive a datagram. The function
|
||||
* call always returns immediately.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be received.
|
||||
* Although the buffers object may be copied as necessary, ownership of the
|
||||
* underlying memory blocks is retained by the caller, which must guarantee
|
||||
* that they remain valid until the handler is called.
|
||||
*
|
||||
* @param sender_endpoint An endpoint object that receives the endpoint of
|
||||
* the remote sender of the datagram. Ownership of the sender_endpoint object
|
||||
* is retained by the caller, which must guarantee that it is valid until the
|
||||
* handler is called.
|
||||
*
|
||||
* @param handler The handler to be called when the receive operation
|
||||
* completes. Copies will be made of the handler as required. The function
|
||||
* signature of the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error, // Result of operation.
|
||||
* std::size_t bytes_transferred // Number of bytes received.
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*
|
||||
* @par Example:
|
||||
* To receive into a single data buffer use the @ref buffer function as
|
||||
* follows:
|
||||
* @code socket.async_receive_from(
|
||||
* asio::buffer(data, size), 0, sender_endpoint, handler); @endcode
|
||||
* See the @ref buffer documentation for information on receiving into
|
||||
* multiple buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Mutable_Buffers, typename Handler>
|
||||
void async_receive_from(const Mutable_Buffers& buffers,
|
||||
endpoint_type& sender_endpoint, Handler handler)
|
||||
{
|
||||
this->service.async_receive_from(this->implementation, buffers,
|
||||
sender_endpoint, 0, handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous receive.
|
||||
/**
|
||||
* This function is used to asynchronously receive a datagram. The function
|
||||
* call always returns immediately.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be received.
|
||||
* Although the buffers object may be copied as necessary, ownership of the
|
||||
* underlying memory blocks is retained by the caller, which must guarantee
|
||||
* that they remain valid until the handler is called.
|
||||
*
|
||||
* @param sender_endpoint An endpoint object that receives the endpoint of
|
||||
* the remote sender of the datagram. Ownership of the sender_endpoint object
|
||||
* is retained by the caller, which must guarantee that it is valid until the
|
||||
* handler is called.
|
||||
*
|
||||
* @param flags Flags specifying how the receive call is to be made.
|
||||
*
|
||||
* @param handler The handler to be called when the receive operation
|
||||
* completes. Copies will be made of the handler as required. The function
|
||||
* signature of the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error, // Result of operation.
|
||||
* std::size_t bytes_transferred // Number of bytes received.
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*/
|
||||
template <typename Mutable_Buffers, typename Handler>
|
||||
void async_receive_from(const Mutable_Buffers& buffers,
|
||||
endpoint_type& sender_endpoint, socket_base::message_flags flags,
|
||||
Handler handler)
|
||||
{
|
||||
this->service.async_receive_from(this->implementation, buffers,
|
||||
sender_endpoint, flags, handler);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_BASIC_DATAGRAM_SOCKET_HPP
|
||||
309
include/libtorrent/asio/basic_deadline_timer.hpp
Normal file
309
include/libtorrent/asio/basic_deadline_timer.hpp
Normal file
@ -0,0 +1,309 @@
|
||||
//
|
||||
// basic_deadline_timer.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_BASIC_DEADLINE_TIMER_HPP
|
||||
#define ASIO_BASIC_DEADLINE_TIMER_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstddef>
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/basic_io_object.hpp"
|
||||
#include "asio/deadline_timer_service.hpp"
|
||||
#include "asio/error.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
/// Provides waitable timer functionality.
|
||||
/**
|
||||
* The basic_deadline_timer class template provides the ability to perform a
|
||||
* blocking or asynchronous wait for a timer to expire.
|
||||
*
|
||||
* Most applications will use the asio::deadline_timer typedef.
|
||||
*
|
||||
* @par Thread Safety:
|
||||
* @e Distinct @e objects: Safe.@n
|
||||
* @e Shared @e objects: Unsafe.
|
||||
*
|
||||
* @par Concepts:
|
||||
* Async_Object, Error_Source.
|
||||
*
|
||||
* @sa @ref deadline_timer_reset
|
||||
*
|
||||
* @par Examples:
|
||||
* Performing a blocking wait:
|
||||
* @code
|
||||
* // Construct a timer without setting an expiry time.
|
||||
* asio::deadline_timer timer(io_service);
|
||||
*
|
||||
* // Set an expiry time relative to now.
|
||||
* timer.expires_from_now(boost::posix_time::seconds(5));
|
||||
*
|
||||
* // Wait for the timer to expire.
|
||||
* timer.wait();
|
||||
* @endcode
|
||||
*
|
||||
* @par
|
||||
* Performing an asynchronous wait:
|
||||
* @code
|
||||
* void handler(const asio::error& error)
|
||||
* {
|
||||
* if (!error)
|
||||
* {
|
||||
* // Timer expired.
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* // Construct a timer with an absolute expiry time.
|
||||
* asio::deadline_timer timer(io_service,
|
||||
* boost::posix_time::time_from_string("2005-12-07 23:59:59.000"));
|
||||
*
|
||||
* // Start an asynchronous wait.
|
||||
* timer.async_wait(handler);
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Time_Type,
|
||||
typename Time_Traits = asio::time_traits<Time_Type>,
|
||||
typename Service = deadline_timer_service<Time_Type, Time_Traits> >
|
||||
class basic_deadline_timer
|
||||
: public basic_io_object<Service>
|
||||
{
|
||||
public:
|
||||
/// The type used for reporting errors.
|
||||
typedef asio::error error_type;
|
||||
|
||||
/// The time traits type.
|
||||
typedef Time_Traits traits_type;
|
||||
|
||||
/// The time type.
|
||||
typedef typename traits_type::time_type time_type;
|
||||
|
||||
/// The duration type.
|
||||
typedef typename traits_type::duration_type duration_type;
|
||||
|
||||
/// Constructor.
|
||||
/**
|
||||
* This constructor creates a timer without setting an expiry time. The
|
||||
* expires_at() or expires_from_now() functions must be called to set an
|
||||
* expiry time before the timer can be waited on.
|
||||
*
|
||||
* @param io_service The io_service object that the timer will use to dispatch
|
||||
* handlers for any asynchronous operations performed on the timer.
|
||||
*/
|
||||
explicit basic_deadline_timer(asio::io_service& io_service)
|
||||
: basic_io_object<Service>(io_service)
|
||||
{
|
||||
}
|
||||
|
||||
/// Constructor to set a particular expiry time as an absolute time.
|
||||
/**
|
||||
* This constructor creates a timer and sets the expiry time.
|
||||
*
|
||||
* @param io_service The io_service object that the timer will use to dispatch
|
||||
* handlers for any asynchronous operations performed on the timer.
|
||||
*
|
||||
* @param expiry_time The expiry time to be used for the timer, expressed
|
||||
* as an absolute time.
|
||||
*/
|
||||
basic_deadline_timer(asio::io_service& io_service,
|
||||
const time_type& expiry_time)
|
||||
: basic_io_object<Service>(io_service)
|
||||
{
|
||||
this->service.expires_at(this->implementation, expiry_time);
|
||||
}
|
||||
|
||||
/// Constructor to set a particular expiry time relative to now.
|
||||
/**
|
||||
* This constructor creates a timer and sets the expiry time.
|
||||
*
|
||||
* @param io_service The io_service object that the timer will use to dispatch
|
||||
* handlers for any asynchronous operations performed on the timer.
|
||||
*
|
||||
* @param expiry_time The expiry time to be used for the timer, relative to
|
||||
* now.
|
||||
*/
|
||||
basic_deadline_timer(asio::io_service& io_service,
|
||||
const duration_type& expiry_time)
|
||||
: basic_io_object<Service>(io_service)
|
||||
{
|
||||
this->service.expires_from_now(this->implementation, expiry_time);
|
||||
}
|
||||
|
||||
/// Cancel any asynchronous operations that are waiting on the timer.
|
||||
/**
|
||||
* This function forces the completion of any pending asynchronous wait
|
||||
* operations against the timer. The handler for each cancelled operation will
|
||||
* be invoked with the asio::error::operation_aborted error code.
|
||||
*
|
||||
* Cancelling the timer does not change the expiry time.
|
||||
*
|
||||
* @return The number of asynchronous operations that were cancelled.
|
||||
*/
|
||||
std::size_t cancel()
|
||||
{
|
||||
return this->service.cancel(this->implementation);
|
||||
}
|
||||
|
||||
/// Get the timer's expiry time as an absolute time.
|
||||
/**
|
||||
* This function may be used to obtain the timer's current expiry time.
|
||||
* Whether the timer has expired or not does not affect this value.
|
||||
*/
|
||||
time_type expires_at() const
|
||||
{
|
||||
return this->service.expires_at(this->implementation);
|
||||
}
|
||||
|
||||
/// Set the timer's expiry time as an absolute time.
|
||||
/**
|
||||
* This function sets the expiry time. Any pending asynchronous wait
|
||||
* operations will be cancelled. The handler for each cancelled operation will
|
||||
* be invoked with the asio::error::operation_aborted error code.
|
||||
*
|
||||
* See @ref deadline_timer_reset for more information on altering the expiry
|
||||
* time of an active timer.
|
||||
*
|
||||
* @param expiry_time The expiry time to be used for the timer.
|
||||
*
|
||||
* @return The number of asynchronous operations that were cancelled.
|
||||
*/
|
||||
std::size_t expires_at(const time_type& expiry_time)
|
||||
{
|
||||
return this->service.expires_at(this->implementation, expiry_time);
|
||||
}
|
||||
|
||||
/// Get the timer's expiry time relative to now.
|
||||
/**
|
||||
* This function may be used to obtain the timer's current expiry time.
|
||||
* Whether the timer has expired or not does not affect this value.
|
||||
*/
|
||||
duration_type expires_from_now() const
|
||||
{
|
||||
return this->service.expires_from_now(this->implementation);
|
||||
}
|
||||
|
||||
/// Set the timer's expiry time relative to now.
|
||||
/**
|
||||
* This function sets the expiry time. Any pending asynchronous wait
|
||||
* operations will be cancelled. The handler for each cancelled operation will
|
||||
* be invoked with the asio::error::operation_aborted error code.
|
||||
*
|
||||
* See @ref deadline_timer_reset for more information on altering the expiry
|
||||
* time of an active timer.
|
||||
*
|
||||
* @param expiry_time The expiry time to be used for the timer.
|
||||
*
|
||||
* @return The number of asynchronous operations that were cancelled.
|
||||
*/
|
||||
std::size_t expires_from_now(const duration_type& expiry_time)
|
||||
{
|
||||
return this->service.expires_from_now(this->implementation, expiry_time);
|
||||
}
|
||||
|
||||
/// Perform a blocking wait on the timer.
|
||||
/**
|
||||
* This function is used to wait for the timer to expire. This function
|
||||
* blocks and does not return until the timer has expired.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
void wait()
|
||||
{
|
||||
this->service.wait(this->implementation);
|
||||
}
|
||||
|
||||
/// Start an asynchronous wait on the timer.
|
||||
/**
|
||||
* This function may be used to initiate an asynchronous wait against the
|
||||
* timer. It always returns immediately.
|
||||
*
|
||||
* For each call to async_wait(), the supplied handler will be called exactly
|
||||
* once. The handler will be called when:
|
||||
*
|
||||
* @li The timer has expired.
|
||||
*
|
||||
* @li The timer was cancelled, in which case the handler is passed the error
|
||||
* code asio::error::operation_aborted.
|
||||
*
|
||||
* @param handler The handler to be called when the timer expires. Copies
|
||||
* will be made of the handler as required. The function signature of the
|
||||
* handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*/
|
||||
template <typename Handler>
|
||||
void async_wait(Handler handler)
|
||||
{
|
||||
this->service.async_wait(this->implementation, handler);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @page deadline_timer_reset Changing an active deadline_timer's expiry time
|
||||
*
|
||||
* Changing the expiry time of a timer while there are pending asynchronous
|
||||
* waits causes those wait operations to be cancelled. To ensure that the action
|
||||
* associated with the timer is performed only once, use something like this:
|
||||
* used:
|
||||
*
|
||||
* @code
|
||||
* void on_some_event()
|
||||
* {
|
||||
* if (my_timer.expires_from_now(seconds(5)) > 0)
|
||||
* {
|
||||
* // We managed to cancel the timer. Start new asynchronous wait.
|
||||
* my_timer.async_wait(on_timeout);
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* // Too late, timer has already expired!
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* void on_timeout(const asio::error& e)
|
||||
* {
|
||||
* if (e != asio::error::operation_aborted)
|
||||
* {
|
||||
* // Timer was not cancelled, take necessary action.
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @li The asio::basic_deadline_timer::expires_from_now() function
|
||||
* cancels any pending asynchronous waits, and returns the number of
|
||||
* asynchronous waits that were cancelled. If it returns 0 then you were too
|
||||
* late and the wait handler has already been executed, or will soon be
|
||||
* executed. If it returns 1 then the wait handler was successfully cancelled.
|
||||
*
|
||||
* @li If a wait handler is cancelled, the asio::error passed to it
|
||||
* contains the value asio::error::operation_aborted.
|
||||
*
|
||||
* @sa asio::basic_deadline_timer
|
||||
*/
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_BASIC_DEADLINE_TIMER_HPP
|
||||
75
include/libtorrent/asio/basic_io_object.hpp
Normal file
75
include/libtorrent/asio/basic_io_object.hpp
Normal file
@ -0,0 +1,75 @@
|
||||
//
|
||||
// basic_io_object.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_BASIC_IO_OBJECT_HPP
|
||||
#define ASIO_BASIC_IO_OBJECT_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/io_service.hpp"
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
/// Base class for all I/O objects.
|
||||
template <typename Service>
|
||||
class basic_io_object
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
/// The type of the service that will be used to provide I/O operations.
|
||||
typedef Service service_type;
|
||||
|
||||
/// The underlying implementation type of I/O object.
|
||||
typedef typename service_type::implementation_type implementation_type;
|
||||
|
||||
/// Get the io_service associated with the object.
|
||||
/**
|
||||
* This function may be used to obtain the io_service object that the I/O
|
||||
* object uses to dispatch handlers for asynchronous operations.
|
||||
*
|
||||
* @return A reference to the io_service object that the I/O object will use
|
||||
* to dispatch handlers. Ownership is not transferred to the caller.
|
||||
*/
|
||||
asio::io_service& io_service()
|
||||
{
|
||||
return service.io_service();
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Construct a basic_io_object.
|
||||
explicit basic_io_object(asio::io_service& io_service)
|
||||
: service(asio::use_service<Service>(io_service))
|
||||
{
|
||||
service.construct(implementation);
|
||||
}
|
||||
|
||||
/// Protected destructor to prevent deletion through this type.
|
||||
~basic_io_object()
|
||||
{
|
||||
service.destroy(implementation);
|
||||
}
|
||||
|
||||
// The backend service implementation.
|
||||
service_type& service;
|
||||
|
||||
// The underlying native implementation.
|
||||
implementation_type implementation;
|
||||
};
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_BASIC_IO_OBJECT_HPP
|
||||
252
include/libtorrent/asio/basic_resolver.hpp
Normal file
252
include/libtorrent/asio/basic_resolver.hpp
Normal file
@ -0,0 +1,252 @@
|
||||
//
|
||||
// basic_resolver.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_BASIC_RESOLVER_HPP
|
||||
#define ASIO_BASIC_RESOLVER_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/basic_io_object.hpp"
|
||||
#include "asio/error.hpp"
|
||||
#include "asio/error_handler.hpp"
|
||||
#include "asio/resolver_service.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
/// Provides endpoint resolution functionality.
|
||||
/**
|
||||
* The basic_resolver class template provides the ability to resolve a query
|
||||
* to a list of endpoints.
|
||||
*
|
||||
* @par Thread Safety:
|
||||
* @e Distinct @e objects: Safe.@n
|
||||
* @e Shared @e objects: Unsafe.
|
||||
*
|
||||
* @par Concepts:
|
||||
* Async_Object, Error_Source.
|
||||
*/
|
||||
template <typename Protocol, typename Service = resolver_service<Protocol> >
|
||||
class basic_resolver
|
||||
: public basic_io_object<Service>
|
||||
{
|
||||
public:
|
||||
/// The protocol type.
|
||||
typedef Protocol protocol_type;
|
||||
|
||||
/// The endpoint type.
|
||||
typedef typename Protocol::endpoint endpoint_type;
|
||||
|
||||
/// The query type.
|
||||
typedef typename Protocol::resolver_query query;
|
||||
|
||||
/// The iterator type.
|
||||
typedef typename Protocol::resolver_iterator iterator;
|
||||
|
||||
/// The type used for reporting errors.
|
||||
typedef asio::error error_type;
|
||||
|
||||
/// Constructor.
|
||||
/**
|
||||
* This constructor creates a basic_resolver.
|
||||
*
|
||||
* @param io_service The io_service object that the resolver will use to
|
||||
* dispatch handlers for any asynchronous operations performed on the timer.
|
||||
*/
|
||||
explicit basic_resolver(asio::io_service& io_service)
|
||||
: basic_io_object<Service>(io_service)
|
||||
{
|
||||
}
|
||||
|
||||
/// Cancel any asynchronous operations that are waiting on the resolver.
|
||||
/**
|
||||
* This function forces the completion of any pending asynchronous
|
||||
* operations on the host resolver. The handler for each cancelled operation
|
||||
* will be invoked with the asio::error::operation_aborted error code.
|
||||
*/
|
||||
void cancel()
|
||||
{
|
||||
return this->service.cancel(this->implementation);
|
||||
}
|
||||
|
||||
/// Resolve a query to a list of entries.
|
||||
/**
|
||||
* This function is used to resolve a query into a list of endpoint entries.
|
||||
*
|
||||
* @param q A query object that determines what endpoints will be returned.
|
||||
*
|
||||
* @returns A forward-only iterator that can be used to traverse the list
|
||||
* of endpoint entries.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @note A default constructed iterator represents the end of the list.
|
||||
*
|
||||
* @note A successful call to this function is guaranteed to return at least
|
||||
* one entry.
|
||||
*/
|
||||
iterator resolve(const query& q)
|
||||
{
|
||||
return this->service.resolve(this->implementation, q, throw_error());
|
||||
}
|
||||
|
||||
/// Resolve a query to a list of entries.
|
||||
/**
|
||||
* This function is used to resolve a query into a list of endpoint entries.
|
||||
*
|
||||
* @param q A query object that determines what endpoints will be returned.
|
||||
*
|
||||
* @returns A forward-only iterator that can be used to traverse the list
|
||||
* of endpoint entries. Returns a default constructed iterator if an error
|
||||
* occurs.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation.
|
||||
* ); @endcode
|
||||
*
|
||||
* @note A default constructed iterator represents the end of the list.
|
||||
*
|
||||
* @note A successful call to this function is guaranteed to return at least
|
||||
* one entry.
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
iterator resolve(const query& q, Error_Handler error_handler)
|
||||
{
|
||||
return this->service.resolve(this->implementation, q, error_handler);
|
||||
}
|
||||
|
||||
/// Asynchronously resolve a query to a list of entries.
|
||||
/**
|
||||
* This function is used to asynchronously resolve a query into a list of
|
||||
* endpoint entries.
|
||||
*
|
||||
* @param q A query object that determines what endpoints will be returned.
|
||||
*
|
||||
* @param handler The handler to be called when the resolve operation
|
||||
* completes. Copies will be made of the handler as required. The function
|
||||
* signature of the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error, // Result of operation.
|
||||
* resolver::iterator iterator // Forward-only iterator that can be used to
|
||||
* // traverse the list of endpoint entries.
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*
|
||||
* @note A default constructed iterator represents the end of the list.
|
||||
*
|
||||
* @note A successful resolve operation is guaranteed to pass at least one
|
||||
* entry to the handler.
|
||||
*/
|
||||
template <typename Handler>
|
||||
void async_resolve(const query& q, Handler handler)
|
||||
{
|
||||
return this->service.async_resolve(this->implementation, q, handler);
|
||||
}
|
||||
|
||||
/// Resolve an endpoint to a list of entries.
|
||||
/**
|
||||
* This function is used to resolve an endpoint into a list of endpoint
|
||||
* entries.
|
||||
*
|
||||
* @param e An endpoint object that determines what endpoints will be
|
||||
* returned.
|
||||
*
|
||||
* @returns A forward-only iterator that can be used to traverse the list
|
||||
* of endpoint entries.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @note A default constructed iterator represents the end of the list.
|
||||
*
|
||||
* @note A successful call to this function is guaranteed to return at least
|
||||
* one entry.
|
||||
*/
|
||||
iterator resolve(const endpoint_type& e)
|
||||
{
|
||||
return this->service.resolve(this->implementation, e, throw_error());
|
||||
}
|
||||
|
||||
/// Resolve an endpoint to a list of entries.
|
||||
/**
|
||||
* This function is used to resolve an endpoint into a list of endpoint
|
||||
* entries.
|
||||
*
|
||||
* @param e An endpoint object that determines what endpoints will be
|
||||
* returned.
|
||||
*
|
||||
* @returns A forward-only iterator that can be used to traverse the list
|
||||
* of endpoint entries. Returns a default constructed iterator if an error
|
||||
* occurs.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation.
|
||||
* ); @endcode
|
||||
*
|
||||
* @note A default constructed iterator represents the end of the list.
|
||||
*
|
||||
* @note A successful call to this function is guaranteed to return at least
|
||||
* one entry.
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
iterator resolve(const endpoint_type& e, Error_Handler error_handler)
|
||||
{
|
||||
return this->service.resolve(this->implementation, e, error_handler);
|
||||
}
|
||||
|
||||
/// Asynchronously resolve an endpoint to a list of entries.
|
||||
/**
|
||||
* This function is used to asynchronously resolve an endpoint into a list of
|
||||
* endpoint entries.
|
||||
*
|
||||
* @param e An endpoint object that determines what endpoints will be
|
||||
* returned.
|
||||
*
|
||||
* @param handler The handler to be called when the resolve operation
|
||||
* completes. Copies will be made of the handler as required. The function
|
||||
* signature of the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error, // Result of operation.
|
||||
* resolver::iterator iterator // Forward-only iterator that can be used to
|
||||
* // traverse the list of endpoint entries.
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*
|
||||
* @note A default constructed iterator represents the end of the list.
|
||||
*
|
||||
* @note A successful resolve operation is guaranteed to pass at least one
|
||||
* entry to the handler.
|
||||
*/
|
||||
template <typename Handler>
|
||||
void async_resolve(const endpoint_type& e, Handler handler)
|
||||
{
|
||||
return this->service.async_resolve(this->implementation, e, handler);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_BASIC_RESOLVER_HPP
|
||||
919
include/libtorrent/asio/basic_socket.hpp
Normal file
919
include/libtorrent/asio/basic_socket.hpp
Normal file
@ -0,0 +1,919 @@
|
||||
//
|
||||
// basic_socket.hpp
|
||||
// ~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_BASIC_SOCKET_HPP
|
||||
#define ASIO_BASIC_SOCKET_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/basic_io_object.hpp"
|
||||
#include "asio/error.hpp"
|
||||
#include "asio/error_handler.hpp"
|
||||
#include "asio/socket_base.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
/// Provides socket functionality.
|
||||
/**
|
||||
* The basic_socket class template provides functionality that is common to both
|
||||
* stream-oriented and datagram-oriented sockets.
|
||||
*
|
||||
* @par Thread Safety:
|
||||
* @e Distinct @e objects: Safe.@n
|
||||
* @e Shared @e objects: Unsafe.
|
||||
*
|
||||
* @par Concepts:
|
||||
* Error_Source, IO_Object.
|
||||
*/
|
||||
template <typename Protocol, typename Service>
|
||||
class basic_socket
|
||||
: public basic_io_object<Service>,
|
||||
public socket_base
|
||||
{
|
||||
public:
|
||||
/// The native representation of a socket.
|
||||
typedef typename Service::native_type native_type;
|
||||
|
||||
/// The protocol type.
|
||||
typedef Protocol protocol_type;
|
||||
|
||||
/// The endpoint type.
|
||||
typedef typename Protocol::endpoint endpoint_type;
|
||||
|
||||
/// The type used for reporting errors.
|
||||
typedef asio::error error_type;
|
||||
|
||||
/// A basic_socket is always the lowest layer.
|
||||
typedef basic_socket<Protocol, Service> lowest_layer_type;
|
||||
|
||||
/// Construct a basic_socket without opening it.
|
||||
/**
|
||||
* This constructor creates a socket without opening it.
|
||||
*
|
||||
* @param io_service The io_service object that the socket will use to
|
||||
* dispatch handlers for any asynchronous operations performed on the socket.
|
||||
*/
|
||||
explicit basic_socket(asio::io_service& io_service)
|
||||
: basic_io_object<Service>(io_service)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct and open a basic_socket.
|
||||
/**
|
||||
* This constructor creates and opens a socket.
|
||||
*
|
||||
* @param io_service The io_service object that the socket will use to
|
||||
* dispatch handlers for any asynchronous operations performed on the socket.
|
||||
*
|
||||
* @param protocol An object specifying protocol parameters to be used.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
basic_socket(asio::io_service& io_service,
|
||||
const protocol_type& protocol)
|
||||
: basic_io_object<Service>(io_service)
|
||||
{
|
||||
this->service.open(this->implementation, protocol, throw_error());
|
||||
}
|
||||
|
||||
/// Construct a basic_socket, opening it and binding it to the given local
|
||||
/// endpoint.
|
||||
/**
|
||||
* This constructor creates a socket and automatically opens it bound to the
|
||||
* specified endpoint on the local machine. The protocol used is the protocol
|
||||
* associated with the given endpoint.
|
||||
*
|
||||
* @param io_service The io_service object that the socket will use to
|
||||
* dispatch handlers for any asynchronous operations performed on the socket.
|
||||
*
|
||||
* @param endpoint An endpoint on the local machine to which the socket will
|
||||
* be bound.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
basic_socket(asio::io_service& io_service,
|
||||
const endpoint_type& endpoint)
|
||||
: basic_io_object<Service>(io_service)
|
||||
{
|
||||
this->service.open(this->implementation, endpoint.protocol(),
|
||||
throw_error());
|
||||
this->service.bind(this->implementation, endpoint, throw_error());
|
||||
}
|
||||
|
||||
/// Construct a basic_socket on an existing native socket.
|
||||
/**
|
||||
* This constructor creates a socket object to hold an existing native socket.
|
||||
*
|
||||
* @param io_service The io_service object that the socket will use to
|
||||
* dispatch handlers for any asynchronous operations performed on the socket.
|
||||
*
|
||||
* @param protocol An object specifying protocol parameters to be used.
|
||||
*
|
||||
* @param native_socket A native socket.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
basic_socket(asio::io_service& io_service,
|
||||
const protocol_type& protocol, const native_type& native_socket)
|
||||
: basic_io_object<Service>(io_service)
|
||||
{
|
||||
this->service.assign(this->implementation, protocol, native_socket,
|
||||
throw_error());
|
||||
}
|
||||
|
||||
/// Get a reference to the lowest layer.
|
||||
/**
|
||||
* This function returns a reference to the lowest layer in a stack of
|
||||
* layers. Since a basic_socket cannot contain any further layers, it simply
|
||||
* returns a reference to itself.
|
||||
*
|
||||
* @return A reference to the lowest layer in the stack of layers. Ownership
|
||||
* is not transferred to the caller.
|
||||
*/
|
||||
lowest_layer_type& lowest_layer()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Open the socket using the specified protocol.
|
||||
/**
|
||||
* This function opens the socket so that it will use the specified protocol.
|
||||
*
|
||||
* @param protocol An object specifying protocol parameters to be used.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* socket.open(asio::ip::tcp::v4());
|
||||
* @endcode
|
||||
*/
|
||||
void open(const protocol_type& protocol = protocol_type())
|
||||
{
|
||||
this->service.open(this->implementation, protocol, throw_error());
|
||||
}
|
||||
|
||||
/// Open the socket using the specified protocol.
|
||||
/**
|
||||
* This function opens the socket so that it will use the specified protocol.
|
||||
*
|
||||
* @param protocol An object specifying which protocol is to be used.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* asio::error error;
|
||||
* socket.open(asio::ip::tcp::v4(), asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
void open(const protocol_type& protocol, Error_Handler error_handler)
|
||||
{
|
||||
this->service.open(this->implementation, protocol, error_handler);
|
||||
}
|
||||
|
||||
/// Assign an existing native socket to the socket.
|
||||
/*
|
||||
* This function opens the socket to hold an existing native socket.
|
||||
*
|
||||
* @param protocol An object specifying which protocol is to be used.
|
||||
*
|
||||
* @param native_socket A native socket.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
void assign(const protocol_type& protocol, const native_type& native_socket)
|
||||
{
|
||||
this->service.assign(this->implementation, protocol, native_socket,
|
||||
throw_error());
|
||||
}
|
||||
|
||||
/// Assign an existing native socket to the socket.
|
||||
/*
|
||||
* This function opens the socket to hold an existing native socket.
|
||||
*
|
||||
* @param protocol An object specifying which protocol is to be used.
|
||||
*
|
||||
* @param native_socket A native socket.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
void assign(const protocol_type& protocol, const native_type& native_socket,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
this->service.assign(this->implementation, protocol, native_socket,
|
||||
error_handler);
|
||||
}
|
||||
|
||||
/// Close the socket.
|
||||
/**
|
||||
* This function is used to close the socket. Any asynchronous send, receive
|
||||
* or connect operations will be cancelled immediately, and will complete
|
||||
* with the asio::error::operation_aborted error.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
void close()
|
||||
{
|
||||
this->service.close(this->implementation, throw_error());
|
||||
}
|
||||
|
||||
/// Close the socket.
|
||||
/**
|
||||
* This function is used to close the socket. Any asynchronous send, receive
|
||||
* or connect operations will be cancelled immediately, and will complete
|
||||
* with the asio::error::operation_aborted error.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* ...
|
||||
* asio::error error;
|
||||
* socket.close(asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
void close(Error_Handler error_handler)
|
||||
{
|
||||
this->service.close(this->implementation, error_handler);
|
||||
}
|
||||
|
||||
/// Get the native socket representation.
|
||||
/**
|
||||
* This function may be used to obtain the underlying representation of the
|
||||
* socket. This is intended to allow access to native socket functionality
|
||||
* that is not otherwise provided.
|
||||
*/
|
||||
native_type native()
|
||||
{
|
||||
return this->service.native(this->implementation);
|
||||
}
|
||||
|
||||
/// Cancel all asynchronous operations associated with the socket.
|
||||
/**
|
||||
* This function causes all outstanding asynchronous connect, send and receive
|
||||
* operations to finish immediately, and the handlers for cancelled operations
|
||||
* will be passed the asio::error::operation_aborted error.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
void cancel()
|
||||
{
|
||||
this->service.cancel(this->implementation, throw_error());
|
||||
}
|
||||
|
||||
/// Cancel all asynchronous operations associated with the socket.
|
||||
/**
|
||||
* This function causes all outstanding asynchronous connect, send and receive
|
||||
* operations to finish immediately, and the handlers for cancelled operations
|
||||
* will be passed the asio::error::operation_aborted error.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
void cancel(Error_Handler error_handler)
|
||||
{
|
||||
this->service.cancel(this->implementation, error_handler);
|
||||
}
|
||||
|
||||
/// Bind the socket to the given local endpoint.
|
||||
/**
|
||||
* This function binds the socket to the specified endpoint on the local
|
||||
* machine.
|
||||
*
|
||||
* @param endpoint An endpoint on the local machine to which the socket will
|
||||
* be bound.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* socket.open(asio::ip::tcp::v4());
|
||||
* socket.bind(asio::ip::tcp::endpoint(
|
||||
* asio::ip::tcp::v4(), 12345));
|
||||
* @endcode
|
||||
*/
|
||||
void bind(const endpoint_type& endpoint)
|
||||
{
|
||||
this->service.bind(this->implementation, endpoint, throw_error());
|
||||
}
|
||||
|
||||
/// Bind the socket to the given local endpoint.
|
||||
/**
|
||||
* This function binds the socket to the specified endpoint on the local
|
||||
* machine.
|
||||
*
|
||||
* @param endpoint An endpoint on the local machine to which the socket will
|
||||
* be bound.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* socket.open(asio::ip::tcp::v4());
|
||||
* asio::error error;
|
||||
* socket.bind(asio::ip::tcp::endpoint(
|
||||
* asio::ip::tcp::v4(), 12345),
|
||||
* asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
void bind(const endpoint_type& endpoint, Error_Handler error_handler)
|
||||
{
|
||||
this->service.bind(this->implementation, endpoint, error_handler);
|
||||
}
|
||||
|
||||
/// Connect the socket to the specified endpoint.
|
||||
/**
|
||||
* This function is used to connect a socket to the specified remote endpoint.
|
||||
* The function call will block until the connection is successfully made or
|
||||
* an error occurs.
|
||||
*
|
||||
* The socket is automatically opened if it is not already open. If the
|
||||
* connect fails, and the socket was automatically opened, the socket is
|
||||
* returned to the closed state.
|
||||
*
|
||||
* @param peer_endpoint The remote endpoint to which the socket will be
|
||||
* connected.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* asio::ip::tcp::endpoint endpoint(
|
||||
* asio::ip::address::from_string("1.2.3.4"), 12345);
|
||||
* socket.connect(endpoint);
|
||||
* @endcode
|
||||
*/
|
||||
void connect(const endpoint_type& peer_endpoint)
|
||||
{
|
||||
this->service.connect(this->implementation, peer_endpoint, throw_error());
|
||||
}
|
||||
|
||||
/// Connect the socket to the specified endpoint.
|
||||
/**
|
||||
* This function is used to connect a socket to the specified remote endpoint.
|
||||
* The function call will block until the connection is successfully made or
|
||||
* an error occurs.
|
||||
*
|
||||
* The socket is automatically opened if it is not already open. If the
|
||||
* connect fails, and the socket was automatically opened, the socket is
|
||||
* returned to the closed state.
|
||||
*
|
||||
* @param peer_endpoint The remote endpoint to which the socket will be
|
||||
* connected.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* asio::ip::tcp::endpoint endpoint(
|
||||
* asio::ip::address::from_string("1.2.3.4"), 12345);
|
||||
* asio::error error;
|
||||
* socket.connect(endpoint, asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
void connect(const endpoint_type& peer_endpoint, Error_Handler error_handler)
|
||||
{
|
||||
this->service.connect(this->implementation, peer_endpoint, error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous connect.
|
||||
/**
|
||||
* This function is used to asynchronously connect a socket to the specified
|
||||
* remote endpoint. The function call always returns immediately.
|
||||
*
|
||||
* The socket is automatically opened if it is not already open. If the
|
||||
* connect fails, and the socket was automatically opened, the socket is
|
||||
* returned to the closed state.
|
||||
*
|
||||
* @param peer_endpoint The remote endpoint to which the socket will be
|
||||
* connected. Copies will be made of the endpoint object as required.
|
||||
*
|
||||
* @param handler The handler to be called when the connection operation
|
||||
* completes. Copies will be made of the handler as required. The function
|
||||
* signature of the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* void connect_handler(const asio::error& error)
|
||||
* {
|
||||
* if (!error)
|
||||
* {
|
||||
* // Connect succeeded.
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* asio::ip::tcp::endpoint endpoint(
|
||||
* asio::ip::address::from_string("1.2.3.4"), 12345);
|
||||
* socket.async_connect(endpoint, connect_handler);
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Handler>
|
||||
void async_connect(const endpoint_type& peer_endpoint, Handler handler)
|
||||
{
|
||||
this->service.async_connect(this->implementation, peer_endpoint, handler);
|
||||
}
|
||||
|
||||
/// Set an option on the socket.
|
||||
/**
|
||||
* This function is used to set an option on the socket.
|
||||
*
|
||||
* @param option The new option value to be set on the socket.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @sa Socket_Option @n
|
||||
* asio::socket_base::broadcast @n
|
||||
* asio::socket_base::do_not_route @n
|
||||
* asio::socket_base::keep_alive @n
|
||||
* asio::socket_base::linger @n
|
||||
* asio::socket_base::receive_buffer_size @n
|
||||
* asio::socket_base::receive_low_watermark @n
|
||||
* asio::socket_base::reuse_address @n
|
||||
* asio::socket_base::send_buffer_size @n
|
||||
* asio::socket_base::send_low_watermark @n
|
||||
* asio::ip::multicast::join_group @n
|
||||
* asio::ip::multicast::leave_group @n
|
||||
* asio::ip::multicast::enable_loopback @n
|
||||
* asio::ip::multicast::outbound_interface @n
|
||||
* asio::ip::multicast::hops @n
|
||||
* asio::ip::tcp::no_delay
|
||||
*
|
||||
* @par Example:
|
||||
* Setting the IPPROTO_TCP/TCP_NODELAY option:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::no_delay option(true);
|
||||
* socket.set_option(option);
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Socket_Option>
|
||||
void set_option(const Socket_Option& option)
|
||||
{
|
||||
this->service.set_option(this->implementation, option, throw_error());
|
||||
}
|
||||
|
||||
/// Set an option on the socket.
|
||||
/**
|
||||
* This function is used to set an option on the socket.
|
||||
*
|
||||
* @param option The new option value to be set on the socket.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @sa Socket_Option @n
|
||||
* asio::socket_base::broadcast @n
|
||||
* asio::socket_base::do_not_route @n
|
||||
* asio::socket_base::keep_alive @n
|
||||
* asio::socket_base::linger @n
|
||||
* asio::socket_base::receive_buffer_size @n
|
||||
* asio::socket_base::receive_low_watermark @n
|
||||
* asio::socket_base::reuse_address @n
|
||||
* asio::socket_base::send_buffer_size @n
|
||||
* asio::socket_base::send_low_watermark @n
|
||||
* asio::ip::multicast::join_group @n
|
||||
* asio::ip::multicast::leave_group @n
|
||||
* asio::ip::multicast::enable_loopback @n
|
||||
* asio::ip::multicast::outbound_interface @n
|
||||
* asio::ip::multicast::hops @n
|
||||
* asio::ip::tcp::no_delay
|
||||
*
|
||||
* @par Example:
|
||||
* Setting the IPPROTO_TCP/TCP_NODELAY option:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::no_delay option(true);
|
||||
* asio::error error;
|
||||
* socket.set_option(option, asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Socket_Option, typename Error_Handler>
|
||||
void set_option(const Socket_Option& option, Error_Handler error_handler)
|
||||
{
|
||||
this->service.set_option(this->implementation, option, error_handler);
|
||||
}
|
||||
|
||||
/// Get an option from the socket.
|
||||
/**
|
||||
* This function is used to get the current value of an option on the socket.
|
||||
*
|
||||
* @param option The option value to be obtained from the socket.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @sa Socket_Option @n
|
||||
* asio::socket_base::broadcast @n
|
||||
* asio::socket_base::do_not_route @n
|
||||
* asio::socket_base::keep_alive @n
|
||||
* asio::socket_base::linger @n
|
||||
* asio::socket_base::receive_buffer_size @n
|
||||
* asio::socket_base::receive_low_watermark @n
|
||||
* asio::socket_base::reuse_address @n
|
||||
* asio::socket_base::send_buffer_size @n
|
||||
* asio::socket_base::send_low_watermark @n
|
||||
* asio::ip::multicast::join_group @n
|
||||
* asio::ip::multicast::leave_group @n
|
||||
* asio::ip::multicast::enable_loopback @n
|
||||
* asio::ip::multicast::outbound_interface @n
|
||||
* asio::ip::multicast::hops @n
|
||||
* asio::ip::tcp::no_delay
|
||||
*
|
||||
* @par Example:
|
||||
* Getting the value of the SOL_SOCKET/SO_KEEPALIVE option:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::socket::keep_alive option;
|
||||
* socket.get_option(option);
|
||||
* bool is_set = option.get();
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Socket_Option>
|
||||
void get_option(Socket_Option& option) const
|
||||
{
|
||||
this->service.get_option(this->implementation, option, throw_error());
|
||||
}
|
||||
|
||||
/// Get an option from the socket.
|
||||
/**
|
||||
* This function is used to get the current value of an option on the socket.
|
||||
*
|
||||
* @param option The option value to be obtained from the socket.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @sa Socket_Option @n
|
||||
* asio::socket_base::broadcast @n
|
||||
* asio::socket_base::do_not_route @n
|
||||
* asio::socket_base::keep_alive @n
|
||||
* asio::socket_base::linger @n
|
||||
* asio::socket_base::receive_buffer_size @n
|
||||
* asio::socket_base::receive_low_watermark @n
|
||||
* asio::socket_base::reuse_address @n
|
||||
* asio::socket_base::send_buffer_size @n
|
||||
* asio::socket_base::send_low_watermark @n
|
||||
* asio::ip::multicast::join_group @n
|
||||
* asio::ip::multicast::leave_group @n
|
||||
* asio::ip::multicast::enable_loopback @n
|
||||
* asio::ip::multicast::outbound_interface @n
|
||||
* asio::ip::multicast::hops @n
|
||||
* asio::ip::tcp::no_delay
|
||||
*
|
||||
* @par Example:
|
||||
* Getting the value of the SOL_SOCKET/SO_KEEPALIVE option:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::socket::keep_alive option;
|
||||
* asio::error error;
|
||||
* socket.get_option(option, asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* bool is_set = option.get();
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Socket_Option, typename Error_Handler>
|
||||
void get_option(Socket_Option& option, Error_Handler error_handler) const
|
||||
{
|
||||
this->service.get_option(this->implementation, option, error_handler);
|
||||
}
|
||||
|
||||
/// Perform an IO control command on the socket.
|
||||
/**
|
||||
* This function is used to execute an IO control command on the socket.
|
||||
*
|
||||
* @param command The IO control command to be performed on the socket.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @sa IO_Control_Command @n
|
||||
* asio::socket_base::bytes_readable @n
|
||||
* asio::socket_base::non_blocking_io
|
||||
*
|
||||
* @par Example:
|
||||
* Getting the number of bytes ready to read:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::socket::bytes_readable command;
|
||||
* socket.io_control(command);
|
||||
* std::size_t bytes_readable = command.get();
|
||||
* @endcode
|
||||
*/
|
||||
template <typename IO_Control_Command>
|
||||
void io_control(IO_Control_Command& command)
|
||||
{
|
||||
this->service.io_control(this->implementation, command, throw_error());
|
||||
}
|
||||
|
||||
/// Perform an IO control command on the socket.
|
||||
/**
|
||||
* This function is used to execute an IO control command on the socket.
|
||||
*
|
||||
* @param command The IO control command to be performed on the socket.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @sa IO_Control_Command @n
|
||||
* asio::socket_base::bytes_readable @n
|
||||
* asio::socket_base::non_blocking_io
|
||||
*
|
||||
* @par Example:
|
||||
* Getting the number of bytes ready to read:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::socket::bytes_readable command;
|
||||
* asio::error error;
|
||||
* socket.io_control(command, asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* std::size_t bytes_readable = command.get();
|
||||
* @endcode
|
||||
*/
|
||||
template <typename IO_Control_Command, typename Error_Handler>
|
||||
void io_control(IO_Control_Command& command, Error_Handler error_handler)
|
||||
{
|
||||
this->service.io_control(this->implementation, command, error_handler);
|
||||
}
|
||||
|
||||
/// Get the local endpoint of the socket.
|
||||
/**
|
||||
* This function is used to obtain the locally bound endpoint of the socket.
|
||||
*
|
||||
* @returns An object that represents the local endpoint of the socket.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::endpoint endpoint = socket.local_endpoint();
|
||||
* @endcode
|
||||
*/
|
||||
endpoint_type local_endpoint() const
|
||||
{
|
||||
return this->service.local_endpoint(this->implementation, throw_error());
|
||||
}
|
||||
|
||||
/// Get the local endpoint of the socket.
|
||||
/**
|
||||
* This function is used to obtain the locally bound endpoint of the socket.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @returns An object that represents the local endpoint of the socket.
|
||||
* Returns a default-constructed endpoint object if an error occurred and the
|
||||
* error handler did not throw an exception.
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* ...
|
||||
* asio::error error;
|
||||
* asio::ip::tcp::endpoint endpoint
|
||||
* = socket.local_endpoint(asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
endpoint_type local_endpoint(Error_Handler error_handler) const
|
||||
{
|
||||
return this->service.local_endpoint(this->implementation, error_handler);
|
||||
}
|
||||
|
||||
/// Get the remote endpoint of the socket.
|
||||
/**
|
||||
* This function is used to obtain the remote endpoint of the socket.
|
||||
*
|
||||
* @returns An object that represents the remote endpoint of the socket.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::endpoint endpoint = socket.remote_endpoint();
|
||||
* @endcode
|
||||
*/
|
||||
endpoint_type remote_endpoint() const
|
||||
{
|
||||
return this->service.remote_endpoint(this->implementation, throw_error());
|
||||
}
|
||||
|
||||
/// Get the remote endpoint of the socket.
|
||||
/**
|
||||
* This function is used to obtain the remote endpoint of the socket.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @returns An object that represents the remote endpoint of the socket.
|
||||
* Returns a default-constructed endpoint object if an error occurred and the
|
||||
* error handler did not throw an exception.
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* ...
|
||||
* asio::error error;
|
||||
* asio::ip::tcp::endpoint endpoint
|
||||
* = socket.remote_endpoint(asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
endpoint_type remote_endpoint(Error_Handler error_handler) const
|
||||
{
|
||||
return this->service.remote_endpoint(this->implementation, error_handler);
|
||||
}
|
||||
|
||||
/// Disable sends or receives on the socket.
|
||||
/**
|
||||
* This function is used to disable send operations, receive operations, or
|
||||
* both.
|
||||
*
|
||||
* @param what Determines what types of operation will no longer be allowed.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @par Example:
|
||||
* Shutting down the send side of the socket:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* ...
|
||||
* socket.shutdown(asio::ip::tcp::socket::shutdown_send);
|
||||
* @endcode
|
||||
*/
|
||||
void shutdown(shutdown_type what)
|
||||
{
|
||||
this->service.shutdown(this->implementation, what, throw_error());
|
||||
}
|
||||
|
||||
/// Disable sends or receives on the socket.
|
||||
/**
|
||||
* This function is used to disable send operations, receive operations, or
|
||||
* both.
|
||||
*
|
||||
* @param what Determines what types of operation will no longer be allowed.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @par Example:
|
||||
* Shutting down the send side of the socket:
|
||||
* @code
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* ...
|
||||
* asio::error error;
|
||||
* socket.shutdown(asio::ip::tcp::socket::shutdown_send,
|
||||
* asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
void shutdown(shutdown_type what, Error_Handler error_handler)
|
||||
{
|
||||
this->service.shutdown(this->implementation, what, error_handler);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Protected destructor to prevent deletion through this type.
|
||||
~basic_socket()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_BASIC_SOCKET_HPP
|
||||
854
include/libtorrent/asio/basic_socket_acceptor.hpp
Normal file
854
include/libtorrent/asio/basic_socket_acceptor.hpp
Normal file
@ -0,0 +1,854 @@
|
||||
//
|
||||
// basic_socket_acceptor.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_BASIC_SOCKET_ACCEPTOR_HPP
|
||||
#define ASIO_BASIC_SOCKET_ACCEPTOR_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/basic_io_object.hpp"
|
||||
#include "asio/basic_socket.hpp"
|
||||
#include "asio/error.hpp"
|
||||
#include "asio/error_handler.hpp"
|
||||
#include "asio/socket_acceptor_service.hpp"
|
||||
#include "asio/socket_base.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
/// Provides the ability to accept new connections.
|
||||
/**
|
||||
* The basic_socket_acceptor class template is used for accepting new socket
|
||||
* connections.
|
||||
*
|
||||
* @par Thread Safety:
|
||||
* @e Distinct @e objects: Safe.@n
|
||||
* @e Shared @e objects: Unsafe.
|
||||
*
|
||||
* @par Concepts:
|
||||
* Async_Object, Error_Source.
|
||||
*
|
||||
* @par Example:
|
||||
* Opening a socket acceptor with the SO_REUSEADDR option enabled:
|
||||
* @code
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), port);
|
||||
* acceptor.open(endpoint.protocol());
|
||||
* acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true));
|
||||
* acceptor.bind(endpoint);
|
||||
* acceptor.listen();
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Protocol,
|
||||
typename Service = socket_acceptor_service<Protocol> >
|
||||
class basic_socket_acceptor
|
||||
: public basic_io_object<Service>,
|
||||
public socket_base
|
||||
{
|
||||
public:
|
||||
/// The native representation of an acceptor.
|
||||
typedef typename Service::native_type native_type;
|
||||
|
||||
/// The protocol type.
|
||||
typedef Protocol protocol_type;
|
||||
|
||||
/// The endpoint type.
|
||||
typedef typename Protocol::endpoint endpoint_type;
|
||||
|
||||
/// The type used for reporting errors.
|
||||
typedef asio::error error_type;
|
||||
|
||||
/// Construct an acceptor without opening it.
|
||||
/**
|
||||
* This constructor creates an acceptor without opening it to listen for new
|
||||
* connections. The open() function must be called before the acceptor can
|
||||
* accept new socket connections.
|
||||
*
|
||||
* @param io_service The io_service object that the acceptor will use to
|
||||
* dispatch handlers for any asynchronous operations performed on the
|
||||
* acceptor.
|
||||
*/
|
||||
explicit basic_socket_acceptor(asio::io_service& io_service)
|
||||
: basic_io_object<Service>(io_service)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct an open acceptor.
|
||||
/**
|
||||
* This constructor creates an acceptor and automatically opens it.
|
||||
*
|
||||
* @param io_service The io_service object that the acceptor will use to
|
||||
* dispatch handlers for any asynchronous operations performed on the
|
||||
* acceptor.
|
||||
*
|
||||
* @param protocol An object specifying protocol parameters to be used.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
basic_socket_acceptor(asio::io_service& io_service,
|
||||
const protocol_type& protocol)
|
||||
: basic_io_object<Service>(io_service)
|
||||
{
|
||||
this->service.open(this->implementation, protocol, throw_error());
|
||||
}
|
||||
|
||||
/// Construct an acceptor opened on the given endpoint.
|
||||
/**
|
||||
* This constructor creates an acceptor and automatically opens it to listen
|
||||
* for new connections on the specified endpoint.
|
||||
*
|
||||
* @param io_service The io_service object that the acceptor will use to
|
||||
* dispatch handlers for any asynchronous operations performed on the
|
||||
* acceptor.
|
||||
*
|
||||
* @param endpoint An endpoint on the local machine on which the acceptor
|
||||
* will listen for new connections.
|
||||
*
|
||||
* @param reuse_addr Whether the constructor should set the socket option
|
||||
* socket_base::reuse_address.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @note This constructor is equivalent to the following code:
|
||||
* @code
|
||||
* basic_socket_acceptor<Protocol> acceptor(io_service);
|
||||
* acceptor.open(endpoint.protocol());
|
||||
* if (reuse_addr)
|
||||
* acceptor.set_option(socket_base::reuse_address(true));
|
||||
* acceptor.bind(endpoint);
|
||||
* acceptor.listen(listen_backlog);
|
||||
* @endcode
|
||||
*/
|
||||
basic_socket_acceptor(asio::io_service& io_service,
|
||||
const endpoint_type& endpoint, bool reuse_addr = true)
|
||||
: basic_io_object<Service>(io_service)
|
||||
{
|
||||
this->service.open(this->implementation, endpoint.protocol(),
|
||||
throw_error());
|
||||
if (reuse_addr)
|
||||
{
|
||||
this->service.set_option(this->implementation,
|
||||
socket_base::reuse_address(true), throw_error());
|
||||
}
|
||||
this->service.bind(this->implementation, endpoint, throw_error());
|
||||
this->service.listen(this->implementation,
|
||||
socket_base::max_connections, throw_error());
|
||||
}
|
||||
|
||||
/// Construct a basic_socket_acceptor on an existing native acceptor.
|
||||
/**
|
||||
* This constructor creates an acceptor object to hold an existing native
|
||||
* acceptor.
|
||||
*
|
||||
* @param io_service The io_service object that the acceptor will use to
|
||||
* dispatch handlers for any asynchronous operations performed on the
|
||||
* acceptor.
|
||||
*
|
||||
* @param protocol An object specifying protocol parameters to be used.
|
||||
*
|
||||
* @param native_acceptor A native acceptor.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
basic_socket_acceptor(asio::io_service& io_service,
|
||||
const protocol_type& protocol, const native_type& native_acceptor)
|
||||
: basic_io_object<Service>(io_service)
|
||||
{
|
||||
this->service.assign(this->implementation, protocol, native_acceptor,
|
||||
throw_error());
|
||||
}
|
||||
|
||||
/// Open the acceptor using the specified protocol.
|
||||
/**
|
||||
* This function opens the socket acceptor so that it will use the specified
|
||||
* protocol.
|
||||
*
|
||||
* @param protocol An object specifying which protocol is to be used.
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* acceptor.open(asio::ip::tcp::v4());
|
||||
* @endcode
|
||||
*/
|
||||
void open(const protocol_type& protocol = protocol_type())
|
||||
{
|
||||
this->service.open(this->implementation, protocol, throw_error());
|
||||
}
|
||||
|
||||
/// Open the acceptor using the specified protocol.
|
||||
/**
|
||||
* This function opens the socket acceptor so that it will use the specified
|
||||
* protocol.
|
||||
*
|
||||
* @param protocol An object specifying which protocol is to be used.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* asio::error error;
|
||||
* acceptor.open(asio::ip::tcp::v4(),
|
||||
* asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
void open(const protocol_type& protocol, Error_Handler error_handler)
|
||||
{
|
||||
this->service.open(this->implementation, protocol, error_handler);
|
||||
}
|
||||
|
||||
/// Assigns an existing native acceptor to the acceptor.
|
||||
/*
|
||||
* This function opens the acceptor to hold an existing native acceptor.
|
||||
*
|
||||
* @param protocol An object specifying which protocol is to be used.
|
||||
*
|
||||
* @param native_acceptor A native acceptor.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
void assign(const protocol_type& protocol, const native_type& native_acceptor)
|
||||
{
|
||||
this->service.assign(this->implementation, protocol, native_acceptor,
|
||||
throw_error());
|
||||
}
|
||||
|
||||
/// Assigns an existing native acceptor to the acceptor.
|
||||
/*
|
||||
* This function opens the acceptor to hold an existing native acceptor.
|
||||
*
|
||||
* @param protocol An object specifying which protocol is to be used.
|
||||
*
|
||||
* @param native_acceptor A native acceptor.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
void assign(const protocol_type& protocol, const native_type& native_acceptor,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
this->service.assign(this->implementation, protocol, native_acceptor,
|
||||
error_handler);
|
||||
}
|
||||
|
||||
/// Bind the acceptor to the given local endpoint.
|
||||
/**
|
||||
* This function binds the socket acceptor to the specified endpoint on the
|
||||
* local machine.
|
||||
*
|
||||
* @param endpoint An endpoint on the local machine to which the socket
|
||||
* acceptor will be bound.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* acceptor.open(asio::ip::tcp::v4());
|
||||
* acceptor.bind(asio::ip::tcp::endpoint(12345));
|
||||
* @endcode
|
||||
*/
|
||||
void bind(const endpoint_type& endpoint)
|
||||
{
|
||||
this->service.bind(this->implementation, endpoint, throw_error());
|
||||
}
|
||||
|
||||
/// Bind the acceptor to the given local endpoint.
|
||||
/**
|
||||
* This function binds the socket acceptor to the specified endpoint on the
|
||||
* local machine.
|
||||
*
|
||||
* @param endpoint An endpoint on the local machine to which the socket
|
||||
* acceptor will be bound.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* acceptor.open(asio::ip::tcp::v4());
|
||||
* asio::error error;
|
||||
* acceptor.bind(asio::ip::tcp::endpoint(12345),
|
||||
* asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
void bind(const endpoint_type& endpoint, Error_Handler error_handler)
|
||||
{
|
||||
this->service.bind(this->implementation, endpoint, error_handler);
|
||||
}
|
||||
|
||||
/// Place the acceptor into the state where it will listen for new
|
||||
/// connections.
|
||||
/**
|
||||
* This function puts the socket acceptor into the state where it may accept
|
||||
* new connections.
|
||||
*
|
||||
* @param backlog The maximum length of the queue of pending connections.
|
||||
*/
|
||||
void listen(int backlog = socket_base::max_connections)
|
||||
{
|
||||
this->service.listen(this->implementation, backlog, throw_error());
|
||||
}
|
||||
|
||||
/// Place the acceptor into the state where it will listen for new
|
||||
/// connections.
|
||||
/**
|
||||
* This function puts the socket acceptor into the state where it may accept
|
||||
* new connections.
|
||||
*
|
||||
* @param backlog The maximum length of the queue of pending connections.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* ...
|
||||
* asio::error error;
|
||||
* acceptor.listen(asio::socket_base::max_connections,
|
||||
* asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
void listen(int backlog, Error_Handler error_handler)
|
||||
{
|
||||
this->service.listen(this->implementation, backlog, error_handler);
|
||||
}
|
||||
|
||||
/// Close the acceptor.
|
||||
/**
|
||||
* This function is used to close the acceptor. Any asynchronous accept
|
||||
* operations will be cancelled immediately.
|
||||
*
|
||||
* A subsequent call to open() is required before the acceptor can again be
|
||||
* used to again perform socket accept operations.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
void close()
|
||||
{
|
||||
this->service.close(this->implementation, throw_error());
|
||||
}
|
||||
|
||||
/// Close the acceptor.
|
||||
/**
|
||||
* This function is used to close the acceptor. Any asynchronous accept
|
||||
* operations will be cancelled immediately.
|
||||
*
|
||||
* A subsequent call to open() is required before the acceptor can again be
|
||||
* used to again perform socket accept operations.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* ...
|
||||
* asio::error error;
|
||||
* acceptor.close(asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
void close(Error_Handler error_handler)
|
||||
{
|
||||
this->service.close(this->implementation, error_handler);
|
||||
}
|
||||
|
||||
/// Get the native acceptor representation.
|
||||
/**
|
||||
* This function may be used to obtain the underlying representation of the
|
||||
* acceptor. This is intended to allow access to native acceptor functionality
|
||||
* that is not otherwise provided.
|
||||
*/
|
||||
native_type native()
|
||||
{
|
||||
return this->service.native(this->implementation);
|
||||
}
|
||||
|
||||
/// Cancel all asynchronous operations associated with the acceptor.
|
||||
/**
|
||||
* This function causes all outstanding asynchronous connect, send and receive
|
||||
* operations to finish immediately, and the handlers for cancelled operations
|
||||
* will be passed the asio::error::operation_aborted error.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
void cancel()
|
||||
{
|
||||
this->service.cancel(this->implementation, throw_error());
|
||||
}
|
||||
|
||||
/// Cancel all asynchronous operations associated with the acceptor.
|
||||
/**
|
||||
* This function causes all outstanding asynchronous connect, send and receive
|
||||
* operations to finish immediately, and the handlers for cancelled operations
|
||||
* will be passed the asio::error::operation_aborted error.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
void cancel(Error_Handler error_handler)
|
||||
{
|
||||
this->service.cancel(this->implementation, error_handler);
|
||||
}
|
||||
|
||||
/// Set an option on the acceptor.
|
||||
/**
|
||||
* This function is used to set an option on the acceptor.
|
||||
*
|
||||
* @param option The new option value to be set on the acceptor.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @sa Socket_Option @n
|
||||
* asio::socket_base::reuse_address
|
||||
* asio::socket_base::enable_connection_aborted
|
||||
*
|
||||
* @par Example:
|
||||
* Setting the SOL_SOCKET/SO_REUSEADDR option:
|
||||
* @code
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::acceptor::reuse_address option(true);
|
||||
* acceptor.set_option(option);
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Option>
|
||||
void set_option(const Option& option)
|
||||
{
|
||||
this->service.set_option(this->implementation, option, throw_error());
|
||||
}
|
||||
|
||||
/// Set an option on the acceptor.
|
||||
/**
|
||||
* This function is used to set an option on the acceptor.
|
||||
*
|
||||
* @param option The new option value to be set on the acceptor.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @sa Socket_Option @n
|
||||
* asio::socket_base::reuse_address
|
||||
* asio::socket_base::enable_connection_aborted
|
||||
*
|
||||
* @par Example:
|
||||
* Setting the SOL_SOCKET/SO_REUSEADDR option:
|
||||
* @code
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::acceptor::reuse_address option(true);
|
||||
* asio::error error;
|
||||
* acceptor.set_option(option, asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Option, typename Error_Handler>
|
||||
void set_option(const Option& option, Error_Handler error_handler)
|
||||
{
|
||||
this->service.set_option(this->implementation, option, error_handler);
|
||||
}
|
||||
|
||||
/// Get an option from the acceptor.
|
||||
/**
|
||||
* This function is used to get the current value of an option on the
|
||||
* acceptor.
|
||||
*
|
||||
* @param option The option value to be obtained from the acceptor.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @sa Socket_Option @n
|
||||
* asio::socket_base::reuse_address
|
||||
*
|
||||
* @par Example:
|
||||
* Getting the value of the SOL_SOCKET/SO_REUSEADDR option:
|
||||
* @code
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::acceptor::reuse_address option;
|
||||
* acceptor.get_option(option);
|
||||
* bool is_set = option.get();
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Option>
|
||||
void get_option(Option& option)
|
||||
{
|
||||
this->service.get_option(this->implementation, option, throw_error());
|
||||
}
|
||||
|
||||
/// Get an option from the acceptor.
|
||||
/**
|
||||
* This function is used to get the current value of an option on the
|
||||
* acceptor.
|
||||
*
|
||||
* @param option The option value to be obtained from the acceptor.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @sa Socket_Option @n
|
||||
* asio::socket_base::reuse_address
|
||||
*
|
||||
* @par Example:
|
||||
* Getting the value of the SOL_SOCKET/SO_REUSEADDR option:
|
||||
* @code
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::acceptor::reuse_address option;
|
||||
* asio::error error;
|
||||
* acceptor.get_option(option, asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* bool is_set = option.get();
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Option, typename Error_Handler>
|
||||
void get_option(Option& option, Error_Handler error_handler)
|
||||
{
|
||||
this->service.get_option(this->implementation, option, error_handler);
|
||||
}
|
||||
|
||||
/// Get the local endpoint of the acceptor.
|
||||
/**
|
||||
* This function is used to obtain the locally bound endpoint of the acceptor.
|
||||
*
|
||||
* @returns An object that represents the local endpoint of the acceptor.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::endpoint endpoint = acceptor.local_endpoint();
|
||||
* @endcode
|
||||
*/
|
||||
endpoint_type local_endpoint() const
|
||||
{
|
||||
return this->service.local_endpoint(this->implementation, throw_error());
|
||||
}
|
||||
|
||||
/// Get the local endpoint of the acceptor.
|
||||
/**
|
||||
* This function is used to obtain the locally bound endpoint of the acceptor.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @returns An object that represents the local endpoint of the acceptor.
|
||||
* Returns a default-constructed endpoint object if an error occurred and the
|
||||
* error handler did not throw an exception.
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* ...
|
||||
* asio::error error;
|
||||
* asio::ip::tcp::endpoint endpoint
|
||||
* = acceptor.local_endpoint(asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
endpoint_type local_endpoint(Error_Handler error_handler) const
|
||||
{
|
||||
return this->service.local_endpoint(this->implementation, error_handler);
|
||||
}
|
||||
|
||||
/// Accept a new connection.
|
||||
/**
|
||||
* This function is used to accept a new connection from a peer into the
|
||||
* given socket. The function call will block until a new connection has been
|
||||
* accepted successfully or an error occurs.
|
||||
*
|
||||
* @param peer The socket into which the new connection will be accepted.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* acceptor.accept(socket);
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Socket_Service>
|
||||
void accept(basic_socket<protocol_type, Socket_Service>& peer)
|
||||
{
|
||||
this->service.accept(this->implementation, peer, throw_error());
|
||||
}
|
||||
|
||||
/// Accept a new connection.
|
||||
/**
|
||||
* This function is used to accept a new connection from a peer into the
|
||||
* given socket. The function call will block until a new connection has been
|
||||
* accepted successfully or an error occurs.
|
||||
*
|
||||
* @param peer The socket into which the new connection will be accepted.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::soocket socket(io_service);
|
||||
* asio::error error;
|
||||
* acceptor.accept(socket, asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Socket_Service, typename Error_Handler>
|
||||
void accept(basic_socket<protocol_type, Socket_Service>& peer,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
this->service.accept(this->implementation, peer, error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous accept.
|
||||
/**
|
||||
* This function is used to asynchronously accept a new connection into a
|
||||
* socket. The function call always returns immediately.
|
||||
*
|
||||
* @param peer The socket into which the new connection will be accepted.
|
||||
* Ownership of the peer object is retained by the caller, which must
|
||||
* guarantee that it is valid until the handler is called.
|
||||
*
|
||||
* @param handler The handler to be called when the accept operation
|
||||
* completes. Copies will be made of the handler as required. The function
|
||||
* signature of the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* void accept_handler(const asio::error& error)
|
||||
* {
|
||||
* if (!error)
|
||||
* {
|
||||
* // Accept succeeded.
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* acceptor.async_accept(socket, accept_handler);
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Socket_Service, typename Handler>
|
||||
void async_accept(basic_socket<protocol_type, Socket_Service>& peer,
|
||||
Handler handler)
|
||||
{
|
||||
this->service.async_accept(this->implementation, peer, handler);
|
||||
}
|
||||
|
||||
/// Accept a new connection and obtain the endpoint of the peer
|
||||
/**
|
||||
* This function is used to accept a new connection from a peer into the
|
||||
* given socket, and additionally provide the endpoint of the remote peer.
|
||||
* The function call will block until a new connection has been accepted
|
||||
* successfully or an error occurs.
|
||||
*
|
||||
* @param peer The socket into which the new connection will be accepted.
|
||||
*
|
||||
* @param peer_endpoint An endpoint object which will receive the endpoint of
|
||||
* the remote peer.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* asio::ip::tcp::endpoint endpoint;
|
||||
* acceptor.accept_endpoint(socket, endpoint);
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Socket_Service>
|
||||
void accept_endpoint(basic_socket<protocol_type, Socket_Service>& peer,
|
||||
endpoint_type& peer_endpoint)
|
||||
{
|
||||
this->service.accept_endpoint(this->implementation, peer, peer_endpoint,
|
||||
throw_error());
|
||||
}
|
||||
|
||||
/// Accept a new connection and obtain the endpoint of the peer
|
||||
/**
|
||||
* This function is used to accept a new connection from a peer into the
|
||||
* given socket, and additionally provide the endpoint of the remote peer.
|
||||
* The function call will block until a new connection has been accepted
|
||||
* successfully or an error occurs.
|
||||
*
|
||||
* @param peer The socket into which the new connection will be accepted.
|
||||
*
|
||||
* @param peer_endpoint An endpoint object which will receive the endpoint of
|
||||
* the remote peer.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* asio::ip::tcp::acceptor acceptor(io_service);
|
||||
* ...
|
||||
* asio::ip::tcp::socket socket(io_service);
|
||||
* asio::ip::tcp::endpoint endpoint;
|
||||
* asio::error error;
|
||||
* acceptor.accept_endpoint(socket, endpoint,
|
||||
* asio::assign_error(error));
|
||||
* if (error)
|
||||
* {
|
||||
* // An error occurred.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename Socket_Service, typename Error_Handler>
|
||||
void accept_endpoint(basic_socket<protocol_type, Socket_Service>& peer,
|
||||
endpoint_type& peer_endpoint, Error_Handler error_handler)
|
||||
{
|
||||
this->service.accept_endpoint(this->implementation, peer, peer_endpoint,
|
||||
error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous accept.
|
||||
/**
|
||||
* This function is used to asynchronously accept a new connection into a
|
||||
* socket, and additionally obtain the endpoint of the remote peer. The
|
||||
* function call always returns immediately.
|
||||
*
|
||||
* @param peer The socket into which the new connection will be accepted.
|
||||
* Ownership of the peer object is retained by the caller, which must
|
||||
* guarantee that it is valid until the handler is called.
|
||||
*
|
||||
* @param peer_endpoint An endpoint object into which the endpoint of the
|
||||
* remote peer will be written. Ownership of the peer_endpoint object is
|
||||
* retained by the caller, which must guarantee that it is valid until the
|
||||
* handler is called.
|
||||
*
|
||||
* @param handler The handler to be called when the accept operation
|
||||
* completes. Copies will be made of the handler as required. The function
|
||||
* signature of the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*/
|
||||
template <typename Socket_Service, typename Handler>
|
||||
void async_accept_endpoint(basic_socket<protocol_type, Socket_Service>& peer,
|
||||
endpoint_type& peer_endpoint, Handler handler)
|
||||
{
|
||||
this->service.async_accept_endpoint(this->implementation, peer,
|
||||
peer_endpoint, handler);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_BASIC_SOCKET_ACCEPTOR_HPP
|
||||
176
include/libtorrent/asio/basic_socket_iostream.hpp
Normal file
176
include/libtorrent/asio/basic_socket_iostream.hpp
Normal file
@ -0,0 +1,176 @@
|
||||
//
|
||||
// basic_socket_iostream.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_BASIC_SOCKET_IOSTREAM_HPP
|
||||
#define ASIO_BASIC_SOCKET_IOSTREAM_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/preprocessor/arithmetic/inc.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_params.hpp>
|
||||
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
|
||||
#include <boost/utility/base_from_member.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/basic_socket_streambuf.hpp"
|
||||
#include "asio/stream_socket_service.hpp"
|
||||
|
||||
#if !defined(ASIO_SOCKET_IOSTREAM_MAX_ARITY)
|
||||
#define ASIO_SOCKET_IOSTREAM_MAX_ARITY 5
|
||||
#endif // !defined(ASIO_SOCKET_IOSTREAM_MAX_ARITY)
|
||||
|
||||
// A macro that should expand to:
|
||||
// template < typename T1, ..., typename Tn >
|
||||
// explicit basic_socket_iostream( T1 x1, ..., Tn xn )
|
||||
// : basic_iostream<char>(&this->boost::base_from_member<
|
||||
// basic_socket_streambuf<Protocol, Service> >::member)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// rdbuf()->connect ( x1, ..., xn );
|
||||
// }
|
||||
// catch (asio::error&)
|
||||
// {
|
||||
// this->setstate(std::ios_base::failbit);
|
||||
// if (this->exceptions() & std::ios_base::failbit)
|
||||
// throw;
|
||||
// }
|
||||
// }
|
||||
// This macro should only persist within this file.
|
||||
|
||||
#define ASIO_PRIVATE_CTR_DEF( z, n, data ) \
|
||||
template < BOOST_PP_ENUM_PARAMS(n, typename T) > \
|
||||
explicit basic_socket_iostream( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \
|
||||
: std::basic_iostream<char>(&this->boost::base_from_member< \
|
||||
basic_socket_streambuf<Protocol, Service> >::member) \
|
||||
{ \
|
||||
try \
|
||||
{ \
|
||||
rdbuf()->connect( BOOST_PP_ENUM_PARAMS(n, x) ); \
|
||||
} \
|
||||
catch (asio::error&) \
|
||||
{ \
|
||||
this->setstate(std::ios_base::failbit); \
|
||||
if (this->exceptions() & std::ios_base::failbit) \
|
||||
throw; \
|
||||
} \
|
||||
} \
|
||||
/**/
|
||||
|
||||
// A macro that should expand to:
|
||||
// template < typename T1, ..., typename Tn >
|
||||
// void connect( T1 x1, ..., Tn xn )
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// rdbuf()->connect ( x1, ..., xn );
|
||||
// }
|
||||
// catch (asio::error&)
|
||||
// {
|
||||
// this->setstate(std::ios_base::failbit);
|
||||
// if (this->exceptions() & std::ios_base::failbit)
|
||||
// throw;
|
||||
// }
|
||||
// }
|
||||
// This macro should only persist within this file.
|
||||
|
||||
#define ASIO_PRIVATE_CONNECT_DEF( z, n, data ) \
|
||||
template < BOOST_PP_ENUM_PARAMS(n, typename T) > \
|
||||
void connect( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \
|
||||
{ \
|
||||
try \
|
||||
{ \
|
||||
rdbuf()->connect( BOOST_PP_ENUM_PARAMS(n, x) ); \
|
||||
} \
|
||||
catch (asio::error&) \
|
||||
{ \
|
||||
this->setstate(std::ios_base::failbit); \
|
||||
if (this->exceptions() & std::ios_base::failbit) \
|
||||
throw; \
|
||||
} \
|
||||
} \
|
||||
/**/
|
||||
|
||||
namespace asio {
|
||||
|
||||
/// Iostream interface for a socket.
|
||||
template <typename Protocol,
|
||||
typename Service = stream_socket_service<Protocol> >
|
||||
class basic_socket_iostream
|
||||
: public boost::base_from_member<basic_socket_streambuf<Protocol, Service> >,
|
||||
public std::basic_iostream<char>
|
||||
{
|
||||
public:
|
||||
/// Construct a basic_socket_iostream without establishing a connection.
|
||||
basic_socket_iostream()
|
||||
: std::basic_iostream<char>(&this->boost::base_from_member<
|
||||
basic_socket_streambuf<Protocol, Service> >::member)
|
||||
{
|
||||
}
|
||||
|
||||
#if defined(GENERATING_DOCUMENTATION)
|
||||
/// Establish a connection to an endpoint corresponding to a resolver query.
|
||||
/**
|
||||
* This constructor automatically establishes a connection based on the
|
||||
* supplied resolver query parameters. The arguments are used to construct
|
||||
* a resolver query object.
|
||||
*/
|
||||
template <typename T1, ..., typename TN>
|
||||
explicit basic_socket_iostream(T1 t1, ..., TN tn);
|
||||
#else
|
||||
BOOST_PP_REPEAT_FROM_TO(
|
||||
1, BOOST_PP_INC(ASIO_SOCKET_IOSTREAM_MAX_ARITY),
|
||||
ASIO_PRIVATE_CTR_DEF, _ )
|
||||
#endif
|
||||
|
||||
#if defined(GENERATING_DOCUMENTATION)
|
||||
/// Establish a connection to an endpoint corresponding to a resolver query.
|
||||
/**
|
||||
* This function automatically establishes a connection based on the supplied
|
||||
* resolver query parameters. The arguments are used to construct a resolver
|
||||
* query object.
|
||||
*/
|
||||
template <typename T1, ..., typename TN>
|
||||
void connect(T1 t1, ..., TN tn);
|
||||
#else
|
||||
BOOST_PP_REPEAT_FROM_TO(
|
||||
1, BOOST_PP_INC(ASIO_SOCKET_IOSTREAM_MAX_ARITY),
|
||||
ASIO_PRIVATE_CONNECT_DEF, _ )
|
||||
#endif
|
||||
|
||||
/// Close the connection.
|
||||
void close()
|
||||
{
|
||||
rdbuf()->close();
|
||||
}
|
||||
|
||||
/// Return a pointer to the underlying streambuf.
|
||||
basic_socket_streambuf<Protocol, Service>* rdbuf() const
|
||||
{
|
||||
return const_cast<basic_socket_streambuf<Protocol, Service>*>(
|
||||
&this->boost::base_from_member<
|
||||
basic_socket_streambuf<Protocol, Service> >::member);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#undef ASIO_PRIVATE_CTR_DEF
|
||||
#undef ASIO_PRIVATE_CONNECT_DEF
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_BASIC_SOCKET_IOSTREAM_HPP
|
||||
280
include/libtorrent/asio/basic_socket_streambuf.hpp
Normal file
280
include/libtorrent/asio/basic_socket_streambuf.hpp
Normal file
@ -0,0 +1,280 @@
|
||||
//
|
||||
// basic_socket_streambuf.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_BASIC_SOCKET_STREAMBUF_HPP
|
||||
#define ASIO_BASIC_SOCKET_STREAMBUF_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <streambuf>
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/preprocessor/arithmetic/inc.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_params.hpp>
|
||||
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
|
||||
#include <boost/utility/base_from_member.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/basic_socket.hpp"
|
||||
#include "asio/error_handler.hpp"
|
||||
#include "asio/io_service.hpp"
|
||||
#include "asio/stream_socket_service.hpp"
|
||||
|
||||
#if !defined(ASIO_SOCKET_STREAMBUF_MAX_ARITY)
|
||||
#define ASIO_SOCKET_STREAMBUF_MAX_ARITY 5
|
||||
#endif // !defined(ASIO_SOCKET_STREAMBUF_MAX_ARITY)
|
||||
|
||||
// A macro that should expand to:
|
||||
// template < typename T1, ..., typename Tn >
|
||||
// explicit basic_socket_streambuf( T1 x1, ..., Tn xn )
|
||||
// : basic_socket<Protocol, Service>(
|
||||
// boost::base_from_member<io_service>::member)
|
||||
// {
|
||||
// init_buffers();
|
||||
// typedef typename Protocol::resolver_query resolver_query;
|
||||
// resolver_query query( x1, ..., xn );
|
||||
// resolve_and_connect(query);
|
||||
// }
|
||||
// This macro should only persist within this file.
|
||||
|
||||
#define ASIO_PRIVATE_CTR_DEF( z, n, data ) \
|
||||
template < BOOST_PP_ENUM_PARAMS(n, typename T) > \
|
||||
explicit basic_socket_streambuf( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \
|
||||
: basic_socket<Protocol, Service>( \
|
||||
boost::base_from_member<io_service>::member) \
|
||||
{ \
|
||||
init_buffers(); \
|
||||
typedef typename Protocol::resolver_query resolver_query; \
|
||||
resolver_query query( BOOST_PP_ENUM_PARAMS(n, x) ); \
|
||||
resolve_and_connect(query); \
|
||||
} \
|
||||
/**/
|
||||
|
||||
// A macro that should expand to:
|
||||
// template < typename T1, ..., typename Tn >
|
||||
// void connect( T1 x1, ..., Tn xn )
|
||||
// {
|
||||
// this->basic_socket<Protocol, Service>::close();
|
||||
// init_buffers();
|
||||
// typedef typename Protocol::resolver_query resolver_query;
|
||||
// resolver_query query( x1, ..., xn );
|
||||
// resolve_and_connect(query);
|
||||
// }
|
||||
// This macro should only persist within this file.
|
||||
|
||||
#define ASIO_PRIVATE_CONNECT_DEF( z, n, data ) \
|
||||
template < BOOST_PP_ENUM_PARAMS(n, typename T) > \
|
||||
void connect( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \
|
||||
{ \
|
||||
this->basic_socket<Protocol, Service>::close(); \
|
||||
init_buffers(); \
|
||||
typedef typename Protocol::resolver_query resolver_query; \
|
||||
resolver_query query( BOOST_PP_ENUM_PARAMS(n, x) ); \
|
||||
resolve_and_connect(query); \
|
||||
} \
|
||||
/**/
|
||||
|
||||
namespace asio {
|
||||
|
||||
/// Iostream streambuf for a socket.
|
||||
template <typename Protocol,
|
||||
typename Service = stream_socket_service<Protocol> >
|
||||
class basic_socket_streambuf
|
||||
: public std::streambuf,
|
||||
private boost::base_from_member<io_service>,
|
||||
public basic_socket<Protocol, Service>
|
||||
{
|
||||
public:
|
||||
/// The endpoint type.
|
||||
typedef typename Protocol::endpoint endpoint_type;
|
||||
|
||||
/// Construct a basic_socket_streambuf without establishing a connection.
|
||||
basic_socket_streambuf()
|
||||
: basic_socket<Protocol, Service>(
|
||||
boost::base_from_member<asio::io_service>::member)
|
||||
{
|
||||
init_buffers();
|
||||
}
|
||||
|
||||
/// Establish a connection to the specified endpoint.
|
||||
explicit basic_socket_streambuf(const endpoint_type& endpoint)
|
||||
: basic_socket<Protocol, Service>(
|
||||
boost::base_from_member<asio::io_service>::member)
|
||||
{
|
||||
init_buffers();
|
||||
this->basic_socket<Protocol, Service>::connect(endpoint);
|
||||
}
|
||||
|
||||
#if defined(GENERATING_DOCUMENTATION)
|
||||
/// Establish a connection to an endpoint corresponding to a resolver query.
|
||||
/**
|
||||
* This constructor automatically establishes a connection based on the
|
||||
* supplied resolver query parameters. The arguments are used to construct
|
||||
* a resolver query object.
|
||||
*/
|
||||
template <typename T1, ..., typename TN>
|
||||
explicit basic_socket_streambuf(T1 t1, ..., TN tn);
|
||||
#else
|
||||
BOOST_PP_REPEAT_FROM_TO(
|
||||
1, BOOST_PP_INC(ASIO_SOCKET_STREAMBUF_MAX_ARITY),
|
||||
ASIO_PRIVATE_CTR_DEF, _ )
|
||||
#endif
|
||||
|
||||
/// Destructor flushes buffered data.
|
||||
~basic_socket_streambuf()
|
||||
{
|
||||
sync();
|
||||
}
|
||||
|
||||
/// Establish a connection to the specified endpoint.
|
||||
void connect(const endpoint_type& endpoint)
|
||||
{
|
||||
this->basic_socket<Protocol, Service>::close();
|
||||
init_buffers();
|
||||
this->basic_socket<Protocol, Service>::connect(endpoint);
|
||||
}
|
||||
|
||||
#if defined(GENERATING_DOCUMENTATION)
|
||||
/// Establish a connection to an endpoint corresponding to a resolver query.
|
||||
/**
|
||||
* This function automatically establishes a connection based on the supplied
|
||||
* resolver query parameters. The arguments are used to construct a resolver
|
||||
* query object.
|
||||
*/
|
||||
template <typename T1, ..., typename TN>
|
||||
void connect(T1 t1, ..., TN tn);
|
||||
#else
|
||||
BOOST_PP_REPEAT_FROM_TO(
|
||||
1, BOOST_PP_INC(ASIO_SOCKET_STREAMBUF_MAX_ARITY),
|
||||
ASIO_PRIVATE_CONNECT_DEF, _ )
|
||||
#endif
|
||||
|
||||
/// Close the connection.
|
||||
void close()
|
||||
{
|
||||
sync();
|
||||
this->basic_socket<Protocol, Service>::close();
|
||||
init_buffers();
|
||||
}
|
||||
|
||||
protected:
|
||||
int_type underflow()
|
||||
{
|
||||
if (gptr() == egptr())
|
||||
{
|
||||
asio::error error;
|
||||
std::size_t bytes_transferred = this->service.receive(
|
||||
this->implementation,
|
||||
asio::buffer(asio::buffer(get_buffer_) + putback_max),
|
||||
0, asio::assign_error(error));
|
||||
if (error)
|
||||
{
|
||||
if (error != asio::error::eof)
|
||||
throw error;
|
||||
return traits_type::eof();
|
||||
}
|
||||
setg(get_buffer_.begin(), get_buffer_.begin() + putback_max,
|
||||
get_buffer_.begin() + putback_max + bytes_transferred);
|
||||
return traits_type::to_int_type(*gptr());
|
||||
}
|
||||
else
|
||||
{
|
||||
return traits_type::eof();
|
||||
}
|
||||
}
|
||||
|
||||
int_type overflow(int_type c)
|
||||
{
|
||||
if (!traits_type::eq_int_type(c, traits_type::eof()))
|
||||
{
|
||||
if (pptr() == epptr())
|
||||
{
|
||||
asio::const_buffer buffer =
|
||||
asio::buffer(pbase(), pptr() - pbase());
|
||||
while (asio::buffer_size(buffer) > 0)
|
||||
{
|
||||
std::size_t bytes_transferred = this->service.send(
|
||||
this->implementation, asio::buffer(buffer),
|
||||
0, asio::throw_error());
|
||||
buffer = buffer + bytes_transferred;
|
||||
}
|
||||
setp(put_buffer_.begin(), put_buffer_.end());
|
||||
}
|
||||
|
||||
*pptr() = traits_type::to_char_type(c);
|
||||
pbump(1);
|
||||
return c;
|
||||
}
|
||||
|
||||
return traits_type::not_eof(c);
|
||||
}
|
||||
|
||||
int sync()
|
||||
{
|
||||
asio::const_buffer buffer =
|
||||
asio::buffer(pbase(), pptr() - pbase());
|
||||
while (asio::buffer_size(buffer) > 0)
|
||||
{
|
||||
std::size_t bytes_transferred = this->service.send(
|
||||
this->implementation, asio::buffer(buffer),
|
||||
0, asio::throw_error());
|
||||
buffer = buffer + bytes_transferred;
|
||||
}
|
||||
setp(put_buffer_.begin(), put_buffer_.end());
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void init_buffers()
|
||||
{
|
||||
setg(get_buffer_.begin(),
|
||||
get_buffer_.begin() + putback_max,
|
||||
get_buffer_.begin() + putback_max);
|
||||
setp(put_buffer_.begin(), put_buffer_.end());
|
||||
}
|
||||
|
||||
void resolve_and_connect(const typename Protocol::resolver_query& query)
|
||||
{
|
||||
typedef typename Protocol::resolver resolver_type;
|
||||
typedef typename Protocol::resolver_iterator iterator_type;
|
||||
resolver_type resolver(
|
||||
boost::base_from_member<asio::io_service>::member);
|
||||
iterator_type iterator = resolver.resolve(query);
|
||||
asio::error error(asio::error::host_not_found);
|
||||
while (error && iterator != iterator_type())
|
||||
{
|
||||
this->basic_socket<Protocol, Service>::close();
|
||||
this->basic_socket<Protocol, Service>::connect(
|
||||
*iterator, asio::assign_error(error));
|
||||
++iterator;
|
||||
}
|
||||
if (error)
|
||||
throw error;
|
||||
}
|
||||
|
||||
enum { putback_max = 8 };
|
||||
enum { buffer_size = 512 };
|
||||
boost::array<char, buffer_size> get_buffer_;
|
||||
boost::array<char, buffer_size> put_buffer_;
|
||||
};
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#undef ASIO_PRIVATE_CTR_DEF
|
||||
#undef ASIO_PRIVATE_CONNECT_DEF
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_BASIC_SOCKET_STREAMBUF_HPP
|
||||
816
include/libtorrent/asio/basic_stream_socket.hpp
Normal file
816
include/libtorrent/asio/basic_stream_socket.hpp
Normal file
@ -0,0 +1,816 @@
|
||||
//
|
||||
// basic_stream_socket.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_BASIC_STREAM_SOCKET_HPP
|
||||
#define ASIO_BASIC_STREAM_SOCKET_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstddef>
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/basic_socket.hpp"
|
||||
#include "asio/error_handler.hpp"
|
||||
#include "asio/stream_socket_service.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
/// Provides stream-oriented socket functionality.
|
||||
/**
|
||||
* The basic_stream_socket class template provides asynchronous and blocking
|
||||
* stream-oriented socket functionality.
|
||||
*
|
||||
* @par Thread Safety:
|
||||
* @e Distinct @e objects: Safe.@n
|
||||
* @e Shared @e objects: Unsafe.
|
||||
*
|
||||
* @par Concepts:
|
||||
* Async_Read_Stream, Async_Write_Stream, Error_Source, IO_Object, Stream,
|
||||
* Sync_Read_Stream, Sync_Write_Stream.
|
||||
*/
|
||||
template <typename Protocol,
|
||||
typename Service = stream_socket_service<Protocol> >
|
||||
class basic_stream_socket
|
||||
: public basic_socket<Protocol, Service>
|
||||
{
|
||||
public:
|
||||
/// The native representation of a socket.
|
||||
typedef typename Service::native_type native_type;
|
||||
|
||||
/// The protocol type.
|
||||
typedef Protocol protocol_type;
|
||||
|
||||
/// The endpoint type.
|
||||
typedef typename Protocol::endpoint endpoint_type;
|
||||
|
||||
/// Construct a basic_stream_socket without opening it.
|
||||
/**
|
||||
* This constructor creates a stream socket without opening it. The socket
|
||||
* needs to be opened and then connected or accepted before data can be sent
|
||||
* or received on it.
|
||||
*
|
||||
* @param io_service The io_service object that the stream socket will use to
|
||||
* dispatch handlers for any asynchronous operations performed on the socket.
|
||||
*/
|
||||
explicit basic_stream_socket(asio::io_service& io_service)
|
||||
: basic_socket<Protocol, Service>(io_service)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct and open a basic_stream_socket.
|
||||
/**
|
||||
* This constructor creates and opens a stream socket. The socket needs to be
|
||||
* connected or accepted before data can be sent or received on it.
|
||||
*
|
||||
* @param io_service The io_service object that the stream socket will use to
|
||||
* dispatch handlers for any asynchronous operations performed on the socket.
|
||||
*
|
||||
* @param protocol An object specifying protocol parameters to be used.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
basic_stream_socket(asio::io_service& io_service,
|
||||
const protocol_type& protocol)
|
||||
: basic_socket<Protocol, Service>(io_service, protocol)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct a basic_stream_socket, opening it and binding it to the given
|
||||
/// local endpoint.
|
||||
/**
|
||||
* This constructor creates a stream socket and automatically opens it bound
|
||||
* to the specified endpoint on the local machine. The protocol used is the
|
||||
* protocol associated with the given endpoint.
|
||||
*
|
||||
* @param io_service The io_service object that the stream socket will use to
|
||||
* dispatch handlers for any asynchronous operations performed on the socket.
|
||||
*
|
||||
* @param endpoint An endpoint on the local machine to which the stream
|
||||
* socket will be bound.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
basic_stream_socket(asio::io_service& io_service,
|
||||
const endpoint_type& endpoint)
|
||||
: basic_socket<Protocol, Service>(io_service, endpoint)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct a basic_stream_socket on an existing native socket.
|
||||
/**
|
||||
* This constructor creates a stream socket object to hold an existing native
|
||||
* socket.
|
||||
*
|
||||
* @param io_service The io_service object that the stream socket will use to
|
||||
* dispatch handlers for any asynchronous operations performed on the socket.
|
||||
*
|
||||
* @param protocol An object specifying protocol parameters to be used.
|
||||
*
|
||||
* @param native_socket The new underlying socket implementation.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
basic_stream_socket(asio::io_service& io_service,
|
||||
const protocol_type& protocol, const native_type& native_socket)
|
||||
: basic_socket<Protocol, Service>(io_service, protocol, native_socket)
|
||||
{
|
||||
}
|
||||
|
||||
/// Send some data on the socket.
|
||||
/**
|
||||
* This function is used to send data on the stream socket. The function
|
||||
* call will block until one or more bytes of the data has been sent
|
||||
* successfully, or an until error occurs.
|
||||
*
|
||||
* @param buffers One or more data buffers to be sent on the socket.
|
||||
*
|
||||
* @returns The number of bytes sent.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @note The send operation may not transmit all of the data to the peer.
|
||||
* Consider using the @ref write function if you need to ensure that all data
|
||||
* is written before the blocking operation completes.
|
||||
*
|
||||
* @par Example:
|
||||
* To send a single data buffer use the @ref buffer function as follows:
|
||||
* @code
|
||||
* socket.send(asio::buffer(data, size));
|
||||
* @endcode
|
||||
* See the @ref buffer documentation for information on sending multiple
|
||||
* buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Const_Buffers>
|
||||
std::size_t send(const Const_Buffers& buffers)
|
||||
{
|
||||
return this->service.send(this->implementation, buffers, 0, throw_error());
|
||||
}
|
||||
|
||||
/// Send some data on the socket.
|
||||
/**
|
||||
* This function is used to send data on the stream socket. The function
|
||||
* call will block until one or more bytes of the data has been sent
|
||||
* successfully, or an until error occurs.
|
||||
*
|
||||
* @param buffers One or more data buffers to be sent on the socket.
|
||||
*
|
||||
* @param flags Flags specifying how the send call is to be made.
|
||||
*
|
||||
* @returns The number of bytes sent.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @note The send operation may not transmit all of the data to the peer.
|
||||
* Consider using the @ref write function if you need to ensure that all data
|
||||
* is written before the blocking operation completes.
|
||||
*
|
||||
* @par Example:
|
||||
* To send a single data buffer use the @ref buffer function as follows:
|
||||
* @code
|
||||
* socket.send(asio::buffer(data, size), 0);
|
||||
* @endcode
|
||||
* See the @ref buffer documentation for information on sending multiple
|
||||
* buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Const_Buffers>
|
||||
std::size_t send(const Const_Buffers& buffers,
|
||||
socket_base::message_flags flags)
|
||||
{
|
||||
return this->service.send(this->implementation, buffers, flags,
|
||||
throw_error());
|
||||
}
|
||||
|
||||
/// Send some data on the socket.
|
||||
/**
|
||||
* This function is used to send data on the stream socket. The function
|
||||
* call will block until one or more bytes of the data has been sent
|
||||
* successfully, or an until error occurs.
|
||||
*
|
||||
* @param buffers One or more data buffers to be sent on the socket.
|
||||
*
|
||||
* @param flags Flags specifying how the send call is to be made.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation.
|
||||
* ); @endcode
|
||||
*
|
||||
* @returns The number of bytes sent. Returns 0 if an error occurred and the
|
||||
* error handler did not throw an exception.
|
||||
*
|
||||
* @note The send operation may not transmit all of the data to the peer.
|
||||
* Consider using the @ref write function if you need to ensure that all data
|
||||
* is written before the blocking operation completes.
|
||||
*/
|
||||
template <typename Const_Buffers, typename Error_Handler>
|
||||
std::size_t send(const Const_Buffers& buffers,
|
||||
socket_base::message_flags flags,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
return this->service.send(this->implementation, buffers, flags,
|
||||
error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous send.
|
||||
/**
|
||||
* This function is used to asynchronously send data on the stream socket.
|
||||
* The function call always returns immediately.
|
||||
*
|
||||
* @param buffers One or more data buffers to be sent on the socket. Although
|
||||
* the buffers object may be copied as necessary, ownership of the underlying
|
||||
* memory blocks is retained by the caller, which must guarantee that they
|
||||
* remain valid until the handler is called.
|
||||
*
|
||||
* @param handler The handler to be called when the send operation completes.
|
||||
* Copies will be made of the handler as required. The function signature of
|
||||
* the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error, // Result of operation.
|
||||
* std::size_t bytes_transferred // Number of bytes sent.
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*
|
||||
* @note The send operation may not transmit all of the data to the peer.
|
||||
* Consider using the @ref async_write function if you need to ensure that all
|
||||
* data is written before the asynchronous operation completes.
|
||||
*
|
||||
* @par Example:
|
||||
* To send a single data buffer use the @ref buffer function as follows:
|
||||
* @code
|
||||
* socket.async_send(asio::buffer(data, size), handler);
|
||||
* @endcode
|
||||
* See the @ref buffer documentation for information on sending multiple
|
||||
* buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Const_Buffers, typename Handler>
|
||||
void async_send(const Const_Buffers& buffers, Handler handler)
|
||||
{
|
||||
this->service.async_send(this->implementation, buffers, 0, handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous send.
|
||||
/**
|
||||
* This function is used to asynchronously send data on the stream socket.
|
||||
* The function call always returns immediately.
|
||||
*
|
||||
* @param buffers One or more data buffers to be sent on the socket. Although
|
||||
* the buffers object may be copied as necessary, ownership of the underlying
|
||||
* memory blocks is retained by the caller, which must guarantee that they
|
||||
* remain valid until the handler is called.
|
||||
*
|
||||
* @param flags Flags specifying how the send call is to be made.
|
||||
*
|
||||
* @param handler The handler to be called when the send operation completes.
|
||||
* Copies will be made of the handler as required. The function signature of
|
||||
* the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error, // Result of operation.
|
||||
* std::size_t bytes_transferred // Number of bytes sent.
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*
|
||||
* @note The send operation may not transmit all of the data to the peer.
|
||||
* Consider using the @ref async_write function if you need to ensure that all
|
||||
* data is written before the asynchronous operation completes.
|
||||
*
|
||||
* @par Example:
|
||||
* To send a single data buffer use the @ref buffer function as follows:
|
||||
* @code
|
||||
* socket.async_send(asio::buffer(data, size), 0, handler);
|
||||
* @endcode
|
||||
* See the @ref buffer documentation for information on sending multiple
|
||||
* buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Const_Buffers, typename Handler>
|
||||
void async_send(const Const_Buffers& buffers,
|
||||
socket_base::message_flags flags, Handler handler)
|
||||
{
|
||||
this->service.async_send(this->implementation, buffers, flags, handler);
|
||||
}
|
||||
|
||||
/// Receive some data on the socket.
|
||||
/**
|
||||
* This function is used to receive data on the stream socket. The function
|
||||
* call will block until one or more bytes of data has been received
|
||||
* successfully, or until an error occurs.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be received.
|
||||
*
|
||||
* @returns The number of bytes received.
|
||||
*
|
||||
* @throws asio::error Thrown on failure. An error code of
|
||||
* asio::error::eof indicates that the connection was closed by the
|
||||
* peer.
|
||||
*
|
||||
* @note The receive operation may not receive all of the requested number of
|
||||
* bytes. Consider using the @ref read function if you need to ensure that the
|
||||
* requested amount of data is read before the blocking operation completes.
|
||||
*
|
||||
* @par Example:
|
||||
* To receive into a single data buffer use the @ref buffer function as
|
||||
* follows:
|
||||
* @code
|
||||
* socket.receive(asio::buffer(data, size));
|
||||
* @endcode
|
||||
* See the @ref buffer documentation for information on receiving into
|
||||
* multiple buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Mutable_Buffers>
|
||||
std::size_t receive(const Mutable_Buffers& buffers)
|
||||
{
|
||||
return this->service.receive(this->implementation, buffers, 0,
|
||||
throw_error());
|
||||
}
|
||||
|
||||
/// Receive some data on the socket.
|
||||
/**
|
||||
* This function is used to receive data on the stream socket. The function
|
||||
* call will block until one or more bytes of data has been received
|
||||
* successfully, or until an error occurs.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be received.
|
||||
*
|
||||
* @param flags Flags specifying how the receive call is to be made.
|
||||
*
|
||||
* @returns The number of bytes received.
|
||||
*
|
||||
* @throws asio::error Thrown on failure. An error code of
|
||||
* asio::error::eof indicates that the connection was closed by the
|
||||
* peer.
|
||||
*
|
||||
* @note The receive operation may not receive all of the requested number of
|
||||
* bytes. Consider using the @ref read function if you need to ensure that the
|
||||
* requested amount of data is read before the blocking operation completes.
|
||||
*
|
||||
* @par Example:
|
||||
* To receive into a single data buffer use the @ref buffer function as
|
||||
* follows:
|
||||
* @code
|
||||
* socket.receive(asio::buffer(data, size), 0);
|
||||
* @endcode
|
||||
* See the @ref buffer documentation for information on receiving into
|
||||
* multiple buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Mutable_Buffers>
|
||||
std::size_t receive(const Mutable_Buffers& buffers,
|
||||
socket_base::message_flags flags)
|
||||
{
|
||||
return this->service.receive(this->implementation, buffers, flags,
|
||||
throw_error());
|
||||
}
|
||||
|
||||
/// Receive some data on a connected socket.
|
||||
/**
|
||||
* This function is used to receive data on the stream socket. The function
|
||||
* call will block until one or more bytes of data has been received
|
||||
* successfully, or until an error occurs.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be received.
|
||||
*
|
||||
* @param flags Flags specifying how the receive call is to be made.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @returns The number of bytes received. Returns 0 if an error occurred and
|
||||
* the error handler did not throw an exception.
|
||||
*
|
||||
* @note The receive operation may not receive all of the requested number of
|
||||
* bytes. Consider using the @ref read function if you need to ensure that the
|
||||
* requested amount of data is read before the blocking operation completes.
|
||||
*/
|
||||
template <typename Mutable_Buffers, typename Error_Handler>
|
||||
std::size_t receive(const Mutable_Buffers& buffers,
|
||||
socket_base::message_flags flags, Error_Handler error_handler)
|
||||
{
|
||||
return this->service.receive(this->implementation, buffers, flags,
|
||||
error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous receive.
|
||||
/**
|
||||
* This function is used to asynchronously receive data from the stream
|
||||
* socket. The function call always returns immediately.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be received.
|
||||
* Although the buffers object may be copied as necessary, ownership of the
|
||||
* underlying memory blocks is retained by the caller, which must guarantee
|
||||
* that they remain valid until the handler is called.
|
||||
*
|
||||
* @param handler The handler to be called when the receive operation
|
||||
* completes. Copies will be made of the handler as required. The function
|
||||
* signature of the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error, // Result of operation.
|
||||
* std::size_t bytes_transferred // Number of bytes received.
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*
|
||||
* @note The receive operation may not receive all of the requested number of
|
||||
* bytes. Consider using the @ref async_read function if you need to ensure
|
||||
* that the requested amount of data is received before the asynchronous
|
||||
* operation completes.
|
||||
*
|
||||
* @par Example:
|
||||
* To receive into a single data buffer use the @ref buffer function as
|
||||
* follows:
|
||||
* @code
|
||||
* socket.async_receive(asio::buffer(data, size), handler);
|
||||
* @endcode
|
||||
* See the @ref buffer documentation for information on receiving into
|
||||
* multiple buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Mutable_Buffers, typename Handler>
|
||||
void async_receive(const Mutable_Buffers& buffers, Handler handler)
|
||||
{
|
||||
this->service.async_receive(this->implementation, buffers, 0, handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous receive.
|
||||
/**
|
||||
* This function is used to asynchronously receive data from the stream
|
||||
* socket. The function call always returns immediately.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be received.
|
||||
* Although the buffers object may be copied as necessary, ownership of the
|
||||
* underlying memory blocks is retained by the caller, which must guarantee
|
||||
* that they remain valid until the handler is called.
|
||||
*
|
||||
* @param flags Flags specifying how the receive call is to be made.
|
||||
*
|
||||
* @param handler The handler to be called when the receive operation
|
||||
* completes. Copies will be made of the handler as required. The function
|
||||
* signature of the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error, // Result of operation.
|
||||
* std::size_t bytes_transferred // Number of bytes received.
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*
|
||||
* @note The receive operation may not receive all of the requested number of
|
||||
* bytes. Consider using the @ref async_read function if you need to ensure
|
||||
* that the requested amount of data is received before the asynchronous
|
||||
* operation completes.
|
||||
*
|
||||
* @par Example:
|
||||
* To receive into a single data buffer use the @ref buffer function as
|
||||
* follows:
|
||||
* @code
|
||||
* socket.async_receive(asio::buffer(data, size), 0, handler);
|
||||
* @endcode
|
||||
* See the @ref buffer documentation for information on receiving into
|
||||
* multiple buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Mutable_Buffers, typename Handler>
|
||||
void async_receive(const Mutable_Buffers& buffers,
|
||||
socket_base::message_flags flags, Handler handler)
|
||||
{
|
||||
this->service.async_receive(this->implementation, buffers, flags, handler);
|
||||
}
|
||||
|
||||
/// Write some data to the socket.
|
||||
/**
|
||||
* This function is used to write data to the stream socket. The function call
|
||||
* will block until one or more bytes of the data has been written
|
||||
* successfully, or until an error occurs.
|
||||
*
|
||||
* @param buffers One or more data buffers to be written to the socket.
|
||||
*
|
||||
* @returns The number of bytes written.
|
||||
*
|
||||
* @throws asio::error Thrown on failure. An error code of
|
||||
* asio::error::eof indicates that the connection was closed by the
|
||||
* peer.
|
||||
*
|
||||
* @note The write_some operation may not transmit all of the data to the
|
||||
* peer. Consider using the @ref write function if you need to ensure that
|
||||
* all data is written before the blocking operation completes.
|
||||
*
|
||||
* @par Example:
|
||||
* To write a single data buffer use the @ref buffer function as follows:
|
||||
* @code
|
||||
* socket.write_some(asio::buffer(data, size));
|
||||
* @endcode
|
||||
* See the @ref buffer documentation for information on writing multiple
|
||||
* buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Const_Buffers>
|
||||
std::size_t write_some(const Const_Buffers& buffers)
|
||||
{
|
||||
return this->service.send(this->implementation, buffers, 0, throw_error());
|
||||
}
|
||||
|
||||
/// Write some data to the socket.
|
||||
/**
|
||||
* This function is used to write data to the stream socket. The function call
|
||||
* will block until one or more bytes of the data has been written
|
||||
* successfully, or until an error occurs.
|
||||
*
|
||||
* @param buffers One or more data buffers to be written to the socket.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation.
|
||||
* ); @endcode
|
||||
*
|
||||
* @returns The number of bytes written. Returns 0 if an error occurred and
|
||||
* the error handler did not throw an exception.
|
||||
*
|
||||
* @note The write_some operation may not transmit all of the data to the
|
||||
* peer. Consider using the @ref write function if you need to ensure that
|
||||
* all data is written before the blocking operation completes.
|
||||
*/
|
||||
template <typename Const_Buffers, typename Error_Handler>
|
||||
std::size_t write_some(const Const_Buffers& buffers,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
return this->service.send(this->implementation, buffers, 0, error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous write.
|
||||
/**
|
||||
* This function is used to asynchronously write data to the stream socket.
|
||||
* The function call always returns immediately.
|
||||
*
|
||||
* @param buffers One or more data buffers to be written to the socket.
|
||||
* Although the buffers object may be copied as necessary, ownership of the
|
||||
* underlying memory blocks is retained by the caller, which must guarantee
|
||||
* that they remain valid until the handler is called.
|
||||
*
|
||||
* @param handler The handler to be called when the write operation completes.
|
||||
* Copies will be made of the handler as required. The function signature of
|
||||
* the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error, // Result of operation.
|
||||
* std::size_t bytes_transferred // Number of bytes written.
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*
|
||||
* @note The write operation may not transmit all of the data to the peer.
|
||||
* Consider using the @ref async_write function if you need to ensure that all
|
||||
* data is written before the asynchronous operation completes.
|
||||
*
|
||||
* @par Example:
|
||||
* To write a single data buffer use the @ref buffer function as follows:
|
||||
* @code
|
||||
* socket.async_write_some(asio::buffer(data, size), handler);
|
||||
* @endcode
|
||||
* See the @ref buffer documentation for information on writing multiple
|
||||
* buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Const_Buffers, typename Handler>
|
||||
void async_write_some(const Const_Buffers& buffers, Handler handler)
|
||||
{
|
||||
this->service.async_send(this->implementation, buffers, 0, handler);
|
||||
}
|
||||
|
||||
/// Read some data from the socket.
|
||||
/**
|
||||
* This function is used to read data from the stream socket. The function
|
||||
* call will block until one or more bytes of data has been read successfully,
|
||||
* or until an error occurs.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be read.
|
||||
*
|
||||
* @returns The number of bytes read.
|
||||
*
|
||||
* @throws asio::error Thrown on failure. An error code of
|
||||
* asio::error::eof indicates that the connection was closed by the
|
||||
* peer.
|
||||
*
|
||||
* @note The read_some operation may not read all of the requested number of
|
||||
* bytes. Consider using the @ref read function if you need to ensure that
|
||||
* the requested amount of data is read before the blocking operation
|
||||
* completes.
|
||||
*
|
||||
* @par Example:
|
||||
* To read into a single data buffer use the @ref buffer function as follows:
|
||||
* @code
|
||||
* socket.read_some(asio::buffer(data, size));
|
||||
* @endcode
|
||||
* See the @ref buffer documentation for information on reading into multiple
|
||||
* buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Mutable_Buffers>
|
||||
std::size_t read_some(const Mutable_Buffers& buffers)
|
||||
{
|
||||
return this->service.receive(this->implementation, buffers, 0,
|
||||
throw_error());
|
||||
}
|
||||
|
||||
/// Read some data from the socket.
|
||||
/**
|
||||
* This function is used to read data from the stream socket. The function
|
||||
* call will block until one or more bytes of data has been read successfully,
|
||||
* or until an error occurs.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be read.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation.
|
||||
* ); @endcode
|
||||
*
|
||||
* @returns The number of bytes read. Returns 0 if an error occurred and the
|
||||
* error handler did not throw an exception.
|
||||
*
|
||||
* @note The read_some operation may not read all of the requested number of
|
||||
* bytes. Consider using the @ref read function if you need to ensure that
|
||||
* the requested amount of data is read before the blocking operation
|
||||
* completes.
|
||||
*/
|
||||
template <typename Mutable_Buffers, typename Error_Handler>
|
||||
std::size_t read_some(const Mutable_Buffers& buffers,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
return this->service.receive(this->implementation, buffers, 0,
|
||||
error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous read.
|
||||
/**
|
||||
* This function is used to asynchronously read data from the stream socket.
|
||||
* The function call always returns immediately.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be read.
|
||||
* Although the buffers object may be copied as necessary, ownership of the
|
||||
* underlying memory blocks is retained by the caller, which must guarantee
|
||||
* that they remain valid until the handler is called.
|
||||
*
|
||||
* @param handler The handler to be called when the read operation completes.
|
||||
* Copies will be made of the handler as required. The function signature of
|
||||
* the handler must be:
|
||||
* @code void handler(
|
||||
* const asio::error& error, // Result of operation.
|
||||
* std::size_t bytes_transferred // Number of bytes read.
|
||||
* ); @endcode
|
||||
* Regardless of whether the asynchronous operation completes immediately or
|
||||
* not, the handler will not be invoked from within this function. Invocation
|
||||
* of the handler will be performed in a manner equivalent to using
|
||||
* asio::io_service::post().
|
||||
*
|
||||
* @note The read operation may not read all of the requested number of bytes.
|
||||
* Consider using the @ref async_read function if you need to ensure that the
|
||||
* requested amount of data is read before the asynchronous operation
|
||||
* completes.
|
||||
*
|
||||
* @par Example:
|
||||
* To read into a single data buffer use the @ref buffer function as follows:
|
||||
* @code
|
||||
* socket.async_read_some(asio::buffer(data, size), handler);
|
||||
* @endcode
|
||||
* See the @ref buffer documentation for information on reading into multiple
|
||||
* buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Mutable_Buffers, typename Handler>
|
||||
void async_read_some(const Mutable_Buffers& buffers, Handler handler)
|
||||
{
|
||||
this->service.async_receive(this->implementation, buffers, 0, handler);
|
||||
}
|
||||
|
||||
/// Peek at the incoming data on the stream socket.
|
||||
/**
|
||||
* This function is used to peek at the incoming data on the stream socket,
|
||||
* without removing it from the input queue. The function call will block
|
||||
* until data has been read successfully or an error occurs.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be read.
|
||||
*
|
||||
* @returns The number of bytes read.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*
|
||||
* @par Example:
|
||||
* To peek using a single data buffer use the @ref buffer function as
|
||||
* follows:
|
||||
* @code socket.peek(asio::buffer(data, size)); @endcode
|
||||
* See the @ref buffer documentation for information on using multiple
|
||||
* buffers in one go, and how to use it with arrays, boost::array or
|
||||
* std::vector.
|
||||
*/
|
||||
template <typename Mutable_Buffers>
|
||||
std::size_t peek(const Mutable_Buffers& buffers)
|
||||
{
|
||||
return this->service.receive(this->implementation, buffers,
|
||||
socket_base::message_peek, throw_error());
|
||||
}
|
||||
|
||||
/// Peek at the incoming data on the stream socket.
|
||||
/**
|
||||
* This function is used to peek at the incoming data on the stream socket,
|
||||
* without removing it from the input queue. The function call will block
|
||||
* until data has been read successfully or an error occurs.
|
||||
*
|
||||
* @param buffers One or more buffers into which the data will be read.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation.
|
||||
* ); @endcode
|
||||
*
|
||||
* @returns The number of bytes read. Returns 0 if an error occurred and the
|
||||
* error handler did not throw an exception.
|
||||
*/
|
||||
template <typename Mutable_Buffers, typename Error_Handler>
|
||||
std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler)
|
||||
{
|
||||
return this->service.receive(this->implementation, buffers,
|
||||
socket_base::message_peek, error_handler);
|
||||
}
|
||||
|
||||
/// Determine the amount of data that may be read without blocking.
|
||||
/**
|
||||
* This function is used to determine the amount of data, in bytes, that may
|
||||
* be read from the stream socket without blocking.
|
||||
*
|
||||
* @returns The number of bytes of data that can be read without blocking.
|
||||
*
|
||||
* @throws asio::error Thrown on failure.
|
||||
*/
|
||||
std::size_t in_avail()
|
||||
{
|
||||
socket_base::bytes_readable command;
|
||||
this->service.io_control(this->implementation, command, throw_error());
|
||||
return command.get();
|
||||
}
|
||||
|
||||
/// Determine the amount of data that may be read without blocking.
|
||||
/**
|
||||
* This function is used to determine the amount of data, in bytes, that may
|
||||
* be read from the stream socket without blocking.
|
||||
*
|
||||
* @param error_handler A handler to be called when the operation completes,
|
||||
* to indicate whether or not an error has occurred. Copies will be made of
|
||||
* the handler as required. The function signature of the handler must be:
|
||||
* @code void error_handler(
|
||||
* const asio::error& error // Result of operation
|
||||
* ); @endcode
|
||||
*
|
||||
* @returns The number of bytes of data that can be read without blocking.
|
||||
*/
|
||||
template <typename Error_Handler>
|
||||
std::size_t in_avail(Error_Handler error_handler)
|
||||
{
|
||||
socket_base::bytes_readable command;
|
||||
this->service.io_control(this->implementation, command, error_handler);
|
||||
return command.get();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_BASIC_STREAM_SOCKET_HPP
|
||||
200
include/libtorrent/asio/basic_streambuf.hpp
Normal file
200
include/libtorrent/asio/basic_streambuf.hpp
Normal file
@ -0,0 +1,200 @@
|
||||
//
|
||||
// basic_streambuf.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_BASIC_STREAMBUF_HPP
|
||||
#define ASIO_BASIC_STREAMBUF_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <streambuf>
|
||||
#include <vector>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/buffer.hpp"
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
/// Automatically resizable buffer class based on std::streambuf.
|
||||
template <typename Allocator = std::allocator<char> >
|
||||
class basic_streambuf
|
||||
: public std::streambuf,
|
||||
private noncopyable
|
||||
{
|
||||
public:
|
||||
#if defined(GENERATING_DOCUMENTATION)
|
||||
/// The type used to represent the get area as a list of buffers.
|
||||
typedef implementation_defined const_buffers_type;
|
||||
|
||||
/// The type used to represent the put area as a list of buffers.
|
||||
typedef implementation_defined mutable_buffers_type;
|
||||
#else
|
||||
typedef asio::const_buffer_container_1 const_buffers_type;
|
||||
typedef asio::mutable_buffer_container_1 mutable_buffers_type;
|
||||
#endif
|
||||
|
||||
/// Construct a buffer with a specified maximum size.
|
||||
explicit basic_streambuf(
|
||||
std::size_t max_size = (std::numeric_limits<std::size_t>::max)(),
|
||||
const Allocator& allocator = Allocator())
|
||||
: max_size_(max_size),
|
||||
buffer_(allocator)
|
||||
{
|
||||
std::size_t pend = (std::min<std::size_t>)(max_size_, buffer_delta);
|
||||
buffer_.resize((std::max<std::size_t>)(pend, 1));
|
||||
setg(&buffer_[0], &buffer_[0], &buffer_[0]);
|
||||
setp(&buffer_[0], &buffer_[0] + pend);
|
||||
}
|
||||
|
||||
/// Return the size of the get area in characters.
|
||||
std::size_t size() const
|
||||
{
|
||||
return pptr() - gptr();
|
||||
}
|
||||
|
||||
/// Return the maximum size of the buffer.
|
||||
std::size_t max_size() const
|
||||
{
|
||||
return max_size_;
|
||||
}
|
||||
|
||||
/// Get a list of buffers that represents the get area.
|
||||
const_buffers_type data() const
|
||||
{
|
||||
return asio::buffer(asio::const_buffer(gptr(),
|
||||
(pptr() - gptr()) * sizeof(char_type)));
|
||||
}
|
||||
|
||||
/// Get a list of buffers that represents the put area, with the given size.
|
||||
mutable_buffers_type prepare(std::size_t size)
|
||||
{
|
||||
reserve(size);
|
||||
return asio::buffer(asio::mutable_buffer(
|
||||
pptr(), size * sizeof(char_type)));
|
||||
}
|
||||
|
||||
/// Move the start of the put area by the specified number of characters.
|
||||
void commit(std::size_t n)
|
||||
{
|
||||
if (pptr() + n > epptr())
|
||||
n = epptr() - pptr();
|
||||
pbump(static_cast<int>(n));
|
||||
}
|
||||
|
||||
/// Move the start of the get area by the specified number of characters.
|
||||
void consume(std::size_t n)
|
||||
{
|
||||
while (n > 0)
|
||||
{
|
||||
sbumpc();
|
||||
--n;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
enum { buffer_delta = 128 };
|
||||
|
||||
int_type underflow()
|
||||
{
|
||||
if (gptr() < pptr())
|
||||
{
|
||||
setg(&buffer_[0], gptr(), pptr());
|
||||
return traits_type::to_int_type(*gptr());
|
||||
}
|
||||
else
|
||||
{
|
||||
return traits_type::eof();
|
||||
}
|
||||
}
|
||||
|
||||
int_type overflow(int_type c)
|
||||
{
|
||||
if (!traits_type::eq_int_type(c, traits_type::eof()))
|
||||
{
|
||||
if (pptr() == epptr())
|
||||
{
|
||||
std::size_t buffer_size = pptr() - gptr();
|
||||
if (buffer_size < max_size_ && max_size_ - buffer_size < buffer_delta)
|
||||
{
|
||||
reserve(max_size_ - buffer_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
reserve(buffer_delta);
|
||||
}
|
||||
}
|
||||
|
||||
*pptr() = traits_type::to_char_type(c);
|
||||
pbump(1);
|
||||
return c;
|
||||
}
|
||||
|
||||
return traits_type::not_eof(c);
|
||||
}
|
||||
|
||||
void reserve(std::size_t n)
|
||||
{
|
||||
// Get current stream positions as offsets.
|
||||
std::size_t gnext = gptr() - &buffer_[0];
|
||||
std::size_t gend = egptr() - &buffer_[0];
|
||||
std::size_t pnext = pptr() - &buffer_[0];
|
||||
std::size_t pend = epptr() - &buffer_[0];
|
||||
|
||||
// Check if there is already enough space in the put area.
|
||||
if (n <= pend - pnext)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Shift existing contents of get area to start of buffer.
|
||||
if (gnext > 0)
|
||||
{
|
||||
std::rotate(&buffer_[0], &buffer_[0] + gnext, &buffer_[0] + pend);
|
||||
gend -= gnext;
|
||||
pnext -= gnext;
|
||||
}
|
||||
|
||||
// Ensure buffer is large enough to hold at least the specified size.
|
||||
if (n > pend - pnext)
|
||||
{
|
||||
if (n <= max_size_ && pnext <= max_size_ - n)
|
||||
{
|
||||
buffer_.resize((std::max<std::size_t>)(pnext + n, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::length_error("asio::streambuf too long");
|
||||
}
|
||||
}
|
||||
|
||||
// Update stream positions.
|
||||
setg(&buffer_[0], &buffer_[0], &buffer_[0] + gend);
|
||||
setp(&buffer_[0] + pnext, &buffer_[0] + pnext + n);
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t max_size_;
|
||||
std::vector<char_type, Allocator> buffer_;
|
||||
};
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_BASIC_STREAMBUF_HPP
|
||||
639
include/libtorrent/asio/buffer.hpp
Normal file
639
include/libtorrent/asio/buffer.hpp
Normal file
@ -0,0 +1,639 @@
|
||||
//
|
||||
// buffer.hpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_BUFFER_HPP
|
||||
#define ASIO_BUFFER_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstddef>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/type_traits/is_const.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
class mutable_buffer;
|
||||
class const_buffer;
|
||||
|
||||
namespace detail {
|
||||
void* buffer_cast_helper(const mutable_buffer&);
|
||||
const void* buffer_cast_helper(const const_buffer&);
|
||||
std::size_t buffer_size_helper(const mutable_buffer&);
|
||||
std::size_t buffer_size_helper(const const_buffer&);
|
||||
} // namespace detail
|
||||
|
||||
/// Holds a buffer that can be modified.
|
||||
/**
|
||||
* The mutable_buffer class provides a safe representation of a buffer that can
|
||||
* be modified. It does not own the underlying data, and so is cheap to copy or
|
||||
* assign.
|
||||
*/
|
||||
class mutable_buffer
|
||||
{
|
||||
public:
|
||||
/// Construct an empty buffer.
|
||||
mutable_buffer()
|
||||
: data_(0),
|
||||
size_(0)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct a buffer to represent a given memory range.
|
||||
mutable_buffer(void* data, std::size_t size)
|
||||
: data_(data),
|
||||
size_(size)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
friend void* asio::detail::buffer_cast_helper(
|
||||
const mutable_buffer& b);
|
||||
friend std::size_t asio::detail::buffer_size_helper(
|
||||
const mutable_buffer& b);
|
||||
|
||||
void* data_;
|
||||
std::size_t size_;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline void* buffer_cast_helper(const mutable_buffer& b)
|
||||
{
|
||||
return b.data_;
|
||||
}
|
||||
|
||||
inline std::size_t buffer_size_helper(const mutable_buffer& b)
|
||||
{
|
||||
return b.size_;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// Cast a non-modifiable buffer to a specified pointer to POD type.
|
||||
/**
|
||||
* @relates mutable_buffer
|
||||
*/
|
||||
template <typename Pointer_To_Pod_Type>
|
||||
inline Pointer_To_Pod_Type buffer_cast(const mutable_buffer& b)
|
||||
{
|
||||
return static_cast<Pointer_To_Pod_Type>(detail::buffer_cast_helper(b));
|
||||
}
|
||||
|
||||
/// Get the number of bytes in a non-modifiable buffer.
|
||||
/**
|
||||
* @relates mutable_buffer
|
||||
*/
|
||||
inline std::size_t buffer_size(const mutable_buffer& b)
|
||||
{
|
||||
return detail::buffer_size_helper(b);
|
||||
}
|
||||
|
||||
/// Create a new modifiable buffer that is offset from the start of another.
|
||||
/**
|
||||
* @relates mutable_buffer
|
||||
*/
|
||||
inline mutable_buffer operator+(const mutable_buffer& b, std::size_t start)
|
||||
{
|
||||
if (start > buffer_size(b))
|
||||
return mutable_buffer();
|
||||
char* new_data = buffer_cast<char*>(b) + start;
|
||||
std::size_t new_size = buffer_size(b) - start;
|
||||
return mutable_buffer(new_data, new_size);
|
||||
}
|
||||
|
||||
/// Create a new modifiable buffer that is offset from the start of another.
|
||||
/**
|
||||
* @relates mutable_buffer
|
||||
*/
|
||||
inline mutable_buffer operator+(std::size_t start, const mutable_buffer& b)
|
||||
{
|
||||
if (start > buffer_size(b))
|
||||
return mutable_buffer();
|
||||
char* new_data = buffer_cast<char*>(b) + start;
|
||||
std::size_t new_size = buffer_size(b) - start;
|
||||
return mutable_buffer(new_data, new_size);
|
||||
}
|
||||
|
||||
/// Adapts a single modifiable buffer so that it meets the requirements of the
|
||||
/// Mutable_Buffers concept.
|
||||
class mutable_buffer_container_1
|
||||
: public mutable_buffer
|
||||
{
|
||||
public:
|
||||
/// The type for each element in the list of buffers.
|
||||
typedef mutable_buffer value_type;
|
||||
|
||||
/// A random-access iterator type that may be used to read elements.
|
||||
typedef const mutable_buffer* const_iterator;
|
||||
|
||||
/// Construct to represent a single modifiable buffer.
|
||||
explicit mutable_buffer_container_1(const mutable_buffer& b)
|
||||
: mutable_buffer(b)
|
||||
{
|
||||
}
|
||||
|
||||
/// Get a random-access iterator to the first element.
|
||||
const_iterator begin() const
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Get a random-access iterator for one past the last element.
|
||||
const_iterator end() const
|
||||
{
|
||||
return begin() + 1;
|
||||
}
|
||||
};
|
||||
|
||||
/// Holds a buffer that cannot be modified.
|
||||
/**
|
||||
* The const_buffer class provides a safe representation of a buffer that cannot
|
||||
* be modified. It does not own the underlying data, and so is cheap to copy or
|
||||
* assign.
|
||||
*/
|
||||
class const_buffer
|
||||
{
|
||||
public:
|
||||
/// Construct an empty buffer.
|
||||
const_buffer()
|
||||
: data_(0),
|
||||
size_(0)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct a buffer to represent a given memory range.
|
||||
const_buffer(const void* data, std::size_t size)
|
||||
: data_(data),
|
||||
size_(size)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct a non-modifiable buffer from a modifiable one.
|
||||
const_buffer(const mutable_buffer& b)
|
||||
: data_(asio::detail::buffer_cast_helper(b)),
|
||||
size_(asio::detail::buffer_size_helper(b))
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
friend const void* asio::detail::buffer_cast_helper(
|
||||
const const_buffer& b);
|
||||
friend std::size_t asio::detail::buffer_size_helper(
|
||||
const const_buffer& b);
|
||||
|
||||
const void* data_;
|
||||
std::size_t size_;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline const void* buffer_cast_helper(const const_buffer& b)
|
||||
{
|
||||
return b.data_;
|
||||
}
|
||||
|
||||
inline std::size_t buffer_size_helper(const const_buffer& b)
|
||||
{
|
||||
return b.size_;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// Cast a non-modifiable buffer to a specified pointer to POD type.
|
||||
/**
|
||||
* @relates const_buffer
|
||||
*/
|
||||
template <typename Pointer_To_Pod_Type>
|
||||
inline Pointer_To_Pod_Type buffer_cast(const const_buffer& b)
|
||||
{
|
||||
return static_cast<Pointer_To_Pod_Type>(detail::buffer_cast_helper(b));
|
||||
}
|
||||
|
||||
/// Get the number of bytes in a non-modifiable buffer.
|
||||
/**
|
||||
* @relates const_buffer
|
||||
*/
|
||||
inline std::size_t buffer_size(const const_buffer& b)
|
||||
{
|
||||
return detail::buffer_size_helper(b);
|
||||
}
|
||||
|
||||
/// Create a new non-modifiable buffer that is offset from the start of another.
|
||||
/**
|
||||
* @relates const_buffer
|
||||
*/
|
||||
inline const_buffer operator+(const const_buffer& b, std::size_t start)
|
||||
{
|
||||
if (start > buffer_size(b))
|
||||
return const_buffer();
|
||||
const char* new_data = buffer_cast<const char*>(b) + start;
|
||||
std::size_t new_size = buffer_size(b) - start;
|
||||
return const_buffer(new_data, new_size);
|
||||
}
|
||||
|
||||
/// Create a new non-modifiable buffer that is offset from the start of another.
|
||||
/**
|
||||
* @relates const_buffer
|
||||
*/
|
||||
inline const_buffer operator+(std::size_t start, const const_buffer& b)
|
||||
{
|
||||
if (start > buffer_size(b))
|
||||
return const_buffer();
|
||||
const char* new_data = buffer_cast<const char*>(b) + start;
|
||||
std::size_t new_size = buffer_size(b) - start;
|
||||
return const_buffer(new_data, new_size);
|
||||
}
|
||||
|
||||
/// Adapts a single non-modifiable buffer so that it meets the requirements of
|
||||
/// the Const_Buffers concept.
|
||||
class const_buffer_container_1
|
||||
: public const_buffer
|
||||
{
|
||||
public:
|
||||
/// The type for each element in the list of buffers.
|
||||
typedef const_buffer value_type;
|
||||
|
||||
/// A random-access iterator type that may be used to read elements.
|
||||
typedef const const_buffer* const_iterator;
|
||||
|
||||
/// Construct to represent a single non-modifiable buffer.
|
||||
explicit const_buffer_container_1(const const_buffer& b)
|
||||
: const_buffer(b)
|
||||
{
|
||||
}
|
||||
|
||||
/// Get a random-access iterator to the first element.
|
||||
const_iterator begin() const
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Get a random-access iterator for one past the last element.
|
||||
const_iterator end() const
|
||||
{
|
||||
return begin() + 1;
|
||||
}
|
||||
};
|
||||
|
||||
/** @defgroup buffer asio::buffer
|
||||
*
|
||||
* @brief The asio::buffer function is used to create a buffer object to
|
||||
* represent raw memory, an array of POD elements, or a vector of POD elements.
|
||||
*
|
||||
* The simplest use case involves reading or writing a single buffer of a
|
||||
* specified size:
|
||||
*
|
||||
* @code sock.write(asio::buffer(data, size)); @endcode
|
||||
*
|
||||
* In the above example, the return value of asio::buffer meets the
|
||||
* requirements of the Const_Buffers concept so that it may be directly passed
|
||||
* to the socket's write function. A buffer created for modifiable memory also
|
||||
* meets the requirements of the Mutable_Buffers concept.
|
||||
*
|
||||
* An individual buffer may be created from a builtin array, std::vector or
|
||||
* boost::array of POD elements. This helps prevent buffer overruns by
|
||||
* automatically determining the size of the buffer:
|
||||
*
|
||||
* @code char d1[128];
|
||||
* size_t bytes_transferred = sock.read(asio::buffer(d1));
|
||||
*
|
||||
* std::vector<char> d2(128);
|
||||
* bytes_transferred = sock.read(asio::buffer(d2));
|
||||
*
|
||||
* boost::array<char, 128> d3;
|
||||
* bytes_transferred = sock.read(asio::buffer(d3)); @endcode
|
||||
*
|
||||
* To read or write using multiple buffers (i.e. scatter-gather I/O), multiple
|
||||
* buffer objects may be assigned into a container that supports the
|
||||
* Mutable_Buffers (for read) or Const_Buffers (for write) concepts:
|
||||
*
|
||||
* @code
|
||||
* char d1[128];
|
||||
* std::vector<char> d2(128);
|
||||
* boost::array<char, 128> d3;
|
||||
*
|
||||
* boost::array<mutable_buffer, 3> bufs1 = {
|
||||
* asio::buffer(d1),
|
||||
* asio::buffer(d2),
|
||||
* asio::buffer(d3) };
|
||||
* bytes_transferred = sock.read(bufs1);
|
||||
*
|
||||
* std::vector<const_buffer> bufs2;
|
||||
* bufs2.push_back(asio::buffer(d1));
|
||||
* bufs2.push_back(asio::buffer(d2));
|
||||
* bufs2.push_back(asio::buffer(d3));
|
||||
* bytes_transferred = sock.write(bufs2); @endcode
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/// Create a new modifiable buffer from an existing buffer.
|
||||
inline mutable_buffer_container_1 buffer(const mutable_buffer& b)
|
||||
{
|
||||
return mutable_buffer_container_1(b);
|
||||
}
|
||||
|
||||
/// Create a new modifiable buffer from an existing buffer.
|
||||
inline mutable_buffer_container_1 buffer(const mutable_buffer& b,
|
||||
std::size_t max_size_in_bytes)
|
||||
{
|
||||
return mutable_buffer_container_1(
|
||||
mutable_buffer(buffer_cast<void*>(b),
|
||||
buffer_size(b) < max_size_in_bytes
|
||||
? buffer_size(b) : max_size_in_bytes));
|
||||
}
|
||||
|
||||
/// Create a new non-modifiable buffer from an existing buffer.
|
||||
inline const_buffer_container_1 buffer(const const_buffer& b)
|
||||
{
|
||||
return const_buffer_container_1(b);
|
||||
}
|
||||
|
||||
/// Create a new non-modifiable buffer from an existing buffer.
|
||||
inline const_buffer_container_1 buffer(const const_buffer& b,
|
||||
std::size_t max_size_in_bytes)
|
||||
{
|
||||
return const_buffer_container_1(
|
||||
const_buffer(buffer_cast<const void*>(b),
|
||||
buffer_size(b) < max_size_in_bytes
|
||||
? buffer_size(b) : max_size_in_bytes));
|
||||
}
|
||||
|
||||
/// Create a new modifiable buffer that represents the given memory range.
|
||||
inline mutable_buffer_container_1 buffer(void* data, std::size_t size_in_bytes)
|
||||
{
|
||||
return mutable_buffer_container_1(mutable_buffer(data, size_in_bytes));
|
||||
}
|
||||
|
||||
/// Create a new non-modifiable buffer that represents the given memory range.
|
||||
inline const_buffer_container_1 buffer(const void* data,
|
||||
std::size_t size_in_bytes)
|
||||
{
|
||||
return const_buffer_container_1(const_buffer(data, size_in_bytes));
|
||||
}
|
||||
|
||||
/// Create a new modifiable buffer that represents the given POD array.
|
||||
template <typename Pod_Type, std::size_t N>
|
||||
inline mutable_buffer_container_1 buffer(Pod_Type (&data)[N])
|
||||
{
|
||||
return mutable_buffer_container_1(mutable_buffer(data, N * sizeof(Pod_Type)));
|
||||
}
|
||||
|
||||
/// Create a new modifiable buffer that represents the given POD array.
|
||||
template <typename Pod_Type, std::size_t N>
|
||||
inline mutable_buffer_container_1 buffer(Pod_Type (&data)[N],
|
||||
std::size_t max_size_in_bytes)
|
||||
{
|
||||
return mutable_buffer_container_1(
|
||||
mutable_buffer(data,
|
||||
N * sizeof(Pod_Type) < max_size_in_bytes
|
||||
? N * sizeof(Pod_Type) : max_size_in_bytes));
|
||||
}
|
||||
|
||||
/// Create a new non-modifiable buffer that represents the given POD array.
|
||||
template <typename Pod_Type, std::size_t N>
|
||||
inline const_buffer_container_1 buffer(const Pod_Type (&data)[N])
|
||||
{
|
||||
return const_buffer_container_1(const_buffer(data, N * sizeof(Pod_Type)));
|
||||
}
|
||||
|
||||
/// Create a new non-modifiable buffer that represents the given POD array.
|
||||
template <typename Pod_Type, std::size_t N>
|
||||
inline const_buffer_container_1 buffer(const Pod_Type (&data)[N],
|
||||
std::size_t max_size_in_bytes)
|
||||
{
|
||||
return const_buffer_container_1(
|
||||
const_buffer(data,
|
||||
N * sizeof(Pod_Type) < max_size_in_bytes
|
||||
? N * sizeof(Pod_Type) : max_size_in_bytes));
|
||||
}
|
||||
|
||||
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
|
||||
|
||||
// Borland C++ thinks the overloads:
|
||||
//
|
||||
// unspecified buffer(boost::array<Pod_Type, N>& array ...);
|
||||
//
|
||||
// and
|
||||
//
|
||||
// unspecified buffer(boost::array<const Pod_Type, N>& array ...);
|
||||
//
|
||||
// are ambiguous. This will be worked around by using a buffer_types traits
|
||||
// class that contains typedefs for the appropriate buffer and container
|
||||
// classes, based on whether Pod_Type is const or non-const.
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <bool IsConst>
|
||||
struct buffer_types_base;
|
||||
|
||||
template <>
|
||||
struct buffer_types_base<false>
|
||||
{
|
||||
typedef mutable_buffer buffer_type;
|
||||
typedef mutable_buffer_container_1 container_type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct buffer_types_base<true>
|
||||
{
|
||||
typedef const_buffer buffer_type;
|
||||
typedef const_buffer_container_1 container_type;
|
||||
};
|
||||
|
||||
template <typename Pod_Type>
|
||||
struct buffer_types
|
||||
: public buffer_types_base<boost::is_const<Pod_Type>::value>
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename Pod_Type, std::size_t N>
|
||||
inline typename detail::buffer_types<Pod_Type>::container_type
|
||||
buffer(boost::array<Pod_Type, N>& data)
|
||||
{
|
||||
typedef typename asio::detail::buffer_types<Pod_Type>::buffer_type
|
||||
buffer_type;
|
||||
typedef typename asio::detail::buffer_types<Pod_Type>::container_type
|
||||
container_type;
|
||||
return container_type(
|
||||
buffer_type(data.c_array(), data.size() * sizeof(Pod_Type)));
|
||||
}
|
||||
|
||||
template <typename Pod_Type, std::size_t N>
|
||||
inline typename detail::buffer_types<Pod_Type>::container_type
|
||||
buffer(boost::array<Pod_Type, N>& data, std::size_t max_size_in_bytes)
|
||||
{
|
||||
typedef typename asio::detail::buffer_types<Pod_Type>::buffer_type
|
||||
buffer_type;
|
||||
typedef typename asio::detail::buffer_types<Pod_Type>::container_type
|
||||
container_type;
|
||||
return container_type(
|
||||
buffer_type(data.c_array(),
|
||||
data.size() * sizeof(Pod_Type) < max_size_in_bytes
|
||||
? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
|
||||
}
|
||||
|
||||
#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
|
||||
|
||||
/// Create a new modifiable buffer that represents the given POD array.
|
||||
template <typename Pod_Type, std::size_t N>
|
||||
inline mutable_buffer_container_1 buffer(boost::array<Pod_Type, N>& data)
|
||||
{
|
||||
return mutable_buffer_container_1(
|
||||
mutable_buffer(data.c_array(), data.size() * sizeof(Pod_Type)));
|
||||
}
|
||||
|
||||
/// Create a new modifiable buffer that represents the given POD array.
|
||||
template <typename Pod_Type, std::size_t N>
|
||||
inline mutable_buffer_container_1 buffer(boost::array<Pod_Type, N>& data,
|
||||
std::size_t max_size_in_bytes)
|
||||
{
|
||||
return mutable_buffer_container_1(
|
||||
mutable_buffer(data.c_array(),
|
||||
data.size() * sizeof(Pod_Type) < max_size_in_bytes
|
||||
? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
|
||||
}
|
||||
|
||||
/// Create a new non-modifiable buffer that represents the given POD array.
|
||||
template <typename Pod_Type, std::size_t N>
|
||||
inline const_buffer_container_1 buffer(boost::array<const Pod_Type, N>& data)
|
||||
{
|
||||
return const_buffer_container_1(
|
||||
const_buffer(data.data(), data.size() * sizeof(Pod_Type)));
|
||||
}
|
||||
|
||||
/// Create a new non-modifiable buffer that represents the given POD array.
|
||||
template <typename Pod_Type, std::size_t N>
|
||||
inline const_buffer_container_1 buffer(boost::array<const Pod_Type, N>& data,
|
||||
std::size_t max_size_in_bytes)
|
||||
{
|
||||
return const_buffer_container_1(
|
||||
const_buffer(data.data(),
|
||||
data.size() * sizeof(Pod_Type) < max_size_in_bytes
|
||||
? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
|
||||
}
|
||||
|
||||
#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
|
||||
|
||||
/// Create a new non-modifiable buffer that represents the given POD array.
|
||||
template <typename Pod_Type, std::size_t N>
|
||||
inline const_buffer_container_1 buffer(const boost::array<Pod_Type, N>& data)
|
||||
{
|
||||
return const_buffer_container_1(
|
||||
const_buffer(data.data(), data.size() * sizeof(Pod_Type)));
|
||||
}
|
||||
|
||||
/// Create a new non-modifiable buffer that represents the given POD array.
|
||||
template <typename Pod_Type, std::size_t N>
|
||||
inline const_buffer_container_1 buffer(const boost::array<Pod_Type, N>& data,
|
||||
std::size_t max_size_in_bytes)
|
||||
{
|
||||
return const_buffer_container_1(
|
||||
const_buffer(data.data(),
|
||||
data.size() * sizeof(Pod_Type) < max_size_in_bytes
|
||||
? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
|
||||
}
|
||||
|
||||
/// Create a new modifiable buffer that represents the given POD vector.
|
||||
/**
|
||||
* @note The buffer is invalidated by any vector operation that would also
|
||||
* invalidate iterators.
|
||||
*/
|
||||
template <typename Pod_Type, typename Allocator>
|
||||
inline mutable_buffer_container_1 buffer(std::vector<Pod_Type, Allocator>& data)
|
||||
{
|
||||
return mutable_buffer_container_1(
|
||||
mutable_buffer(&data[0], data.size() * sizeof(Pod_Type)));
|
||||
}
|
||||
|
||||
/// Create a new modifiable buffer that represents the given POD vector.
|
||||
/**
|
||||
* @note The buffer is invalidated by any vector operation that would also
|
||||
* invalidate iterators.
|
||||
*/
|
||||
template <typename Pod_Type, typename Allocator>
|
||||
inline mutable_buffer_container_1 buffer(std::vector<Pod_Type, Allocator>& data,
|
||||
std::size_t max_size_in_bytes)
|
||||
{
|
||||
return mutable_buffer_container_1(
|
||||
mutable_buffer(&data[0],
|
||||
data.size() * sizeof(Pod_Type) < max_size_in_bytes
|
||||
? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
|
||||
}
|
||||
|
||||
/// Create a new non-modifiable buffer that represents the given POD vector.
|
||||
/**
|
||||
* @note The buffer is invalidated by any vector operation that would also
|
||||
* invalidate iterators.
|
||||
*/
|
||||
template <typename Pod_Type, typename Allocator>
|
||||
inline const_buffer_container_1 buffer(
|
||||
const std::vector<Pod_Type, Allocator>& data)
|
||||
{
|
||||
return const_buffer_container_1(
|
||||
const_buffer(&data[0], data.size() * sizeof(Pod_Type)));
|
||||
}
|
||||
|
||||
/// Create a new non-modifiable buffer that represents the given POD vector.
|
||||
/**
|
||||
* @note The buffer is invalidated by any vector operation that would also
|
||||
* invalidate iterators.
|
||||
*/
|
||||
template <typename Pod_Type, typename Allocator>
|
||||
inline const_buffer_container_1 buffer(
|
||||
const std::vector<Pod_Type, Allocator>& data, std::size_t max_size_in_bytes)
|
||||
{
|
||||
return const_buffer_container_1(
|
||||
const_buffer(&data[0],
|
||||
data.size() * sizeof(Pod_Type) < max_size_in_bytes
|
||||
? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
|
||||
}
|
||||
|
||||
/// Create a new non-modifiable buffer that represents the given string.
|
||||
/**
|
||||
* @note The buffer is invalidated by any non-const operation called on the
|
||||
* given string object.
|
||||
*/
|
||||
inline const_buffer_container_1 buffer(const std::string& data)
|
||||
{
|
||||
return const_buffer_container_1(const_buffer(data.data(), data.size()));
|
||||
}
|
||||
|
||||
/// Create a new non-modifiable buffer that represents the given string.
|
||||
/**
|
||||
* @note The buffer is invalidated by any non-const operation called on the
|
||||
* given string object.
|
||||
*/
|
||||
inline const_buffer_container_1 buffer(const std::string& data,
|
||||
std::size_t max_size_in_bytes)
|
||||
{
|
||||
return const_buffer_container_1(
|
||||
const_buffer(data.data(),
|
||||
data.size() < max_size_in_bytes
|
||||
? data.size() : max_size_in_bytes));
|
||||
}
|
||||
|
||||
/*@}*/
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_BUFFER_HPP
|
||||
407
include/libtorrent/asio/buffered_read_stream.hpp
Normal file
407
include/libtorrent/asio/buffered_read_stream.hpp
Normal file
@ -0,0 +1,407 @@
|
||||
//
|
||||
// buffered_read_stream.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_BUFFERED_READ_STREAM_HPP
|
||||
#define ASIO_BUFFERED_READ_STREAM_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/type_traits.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/buffered_read_stream_fwd.hpp"
|
||||
#include "asio/buffer.hpp"
|
||||
#include "asio/error.hpp"
|
||||
#include "asio/io_service.hpp"
|
||||
#include "asio/detail/bind_handler.hpp"
|
||||
#include "asio/detail/buffer_resize_guard.hpp"
|
||||
#include "asio/detail/buffered_stream_storage.hpp"
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
/// Adds buffering to the read-related operations of a stream.
|
||||
/**
|
||||
* The buffered_read_stream class template can be used to add buffering to the
|
||||
* synchronous and asynchronous read operations of a stream.
|
||||
*
|
||||
* @par Thread Safety:
|
||||
* @e Distinct @e objects: Safe.@n
|
||||
* @e Shared @e objects: Unsafe.
|
||||
*
|
||||
* @par Concepts:
|
||||
* Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream,
|
||||
* Sync_Read_Stream, Sync_Write_Stream.
|
||||
*/
|
||||
template <typename Stream>
|
||||
class buffered_read_stream
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
/// The type of the next layer.
|
||||
typedef typename boost::remove_reference<Stream>::type next_layer_type;
|
||||
|
||||
/// The type of the lowest layer.
|
||||
typedef typename next_layer_type::lowest_layer_type lowest_layer_type;
|
||||
|
||||
/// The type used for reporting errors.
|
||||
typedef typename next_layer_type::error_type error_type;
|
||||
|
||||
#if defined(GENERATING_DOCUMENTATION)
|
||||
/// The default buffer size.
|
||||
static const std::size_t default_buffer_size = implementation_defined;
|
||||
#else
|
||||
BOOST_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024);
|
||||
#endif
|
||||
|
||||
/// Construct, passing the specified argument to initialise the next layer.
|
||||
template <typename Arg>
|
||||
explicit buffered_read_stream(Arg& a)
|
||||
: next_layer_(a),
|
||||
storage_(default_buffer_size)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct, passing the specified argument to initialise the next layer.
|
||||
template <typename Arg>
|
||||
buffered_read_stream(Arg& a, std::size_t buffer_size)
|
||||
: next_layer_(a),
|
||||
storage_(buffer_size)
|
||||
{
|
||||
}
|
||||
|
||||
/// Get a reference to the next layer.
|
||||
next_layer_type& next_layer()
|
||||
{
|
||||
return next_layer_;
|
||||
}
|
||||
|
||||
/// Get a reference to the lowest layer.
|
||||
lowest_layer_type& lowest_layer()
|
||||
{
|
||||
return next_layer_.lowest_layer();
|
||||
}
|
||||
|
||||
/// Get the io_service associated with the object.
|
||||
asio::io_service& io_service()
|
||||
{
|
||||
return next_layer_.io_service();
|
||||
}
|
||||
|
||||
/// Close the stream.
|
||||
void close()
|
||||
{
|
||||
next_layer_.close();
|
||||
}
|
||||
|
||||
/// Close the stream.
|
||||
template <typename Error_Handler>
|
||||
void close(Error_Handler error_handler)
|
||||
{
|
||||
next_layer_.close(error_handler);
|
||||
}
|
||||
|
||||
/// Write the given data to the stream. Returns the number of bytes written.
|
||||
/// Throws an exception on failure.
|
||||
template <typename Const_Buffers>
|
||||
std::size_t write_some(const Const_Buffers& buffers)
|
||||
{
|
||||
return next_layer_.write_some(buffers);
|
||||
}
|
||||
|
||||
/// Write the given data to the stream. Returns the number of bytes written,
|
||||
/// or 0 if an error occurred and the error handler did not throw.
|
||||
template <typename Const_Buffers, typename Error_Handler>
|
||||
std::size_t write_some(const Const_Buffers& buffers,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
return next_layer_.write_some(buffers, error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous write. The data being written must be valid for the
|
||||
/// lifetime of the asynchronous operation.
|
||||
template <typename Const_Buffers, typename Handler>
|
||||
void async_write_some(const Const_Buffers& buffers, Handler handler)
|
||||
{
|
||||
next_layer_.async_write_some(buffers, handler);
|
||||
}
|
||||
|
||||
/// Fill the buffer with some data. Returns the number of bytes placed in the
|
||||
/// buffer as a result of the operation. Throws an exception on failure.
|
||||
std::size_t fill()
|
||||
{
|
||||
detail::buffer_resize_guard<detail::buffered_stream_storage>
|
||||
resize_guard(storage_);
|
||||
std::size_t previous_size = storage_.size();
|
||||
storage_.resize(storage_.capacity());
|
||||
storage_.resize(previous_size + next_layer_.read_some(buffer(
|
||||
storage_.data() + previous_size,
|
||||
storage_.size() - previous_size)));
|
||||
resize_guard.commit();
|
||||
return storage_.size() - previous_size;
|
||||
}
|
||||
|
||||
/// Fill the buffer with some data. Returns the number of bytes placed in the
|
||||
/// buffer as a result of the operation, or 0 if an error occurred and the
|
||||
/// error handler did not throw.
|
||||
template <typename Error_Handler>
|
||||
std::size_t fill(Error_Handler error_handler)
|
||||
{
|
||||
detail::buffer_resize_guard<detail::buffered_stream_storage>
|
||||
resize_guard(storage_);
|
||||
std::size_t previous_size = storage_.size();
|
||||
storage_.resize(storage_.capacity());
|
||||
storage_.resize(previous_size + next_layer_.read_some(buffer(
|
||||
storage_.data() + previous_size,
|
||||
storage_.size() - previous_size),
|
||||
error_handler));
|
||||
resize_guard.commit();
|
||||
return storage_.size() - previous_size;
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
class fill_handler
|
||||
{
|
||||
public:
|
||||
fill_handler(asio::io_service& io_service,
|
||||
detail::buffered_stream_storage& storage,
|
||||
std::size_t previous_size, Handler handler)
|
||||
: io_service_(io_service),
|
||||
storage_(storage),
|
||||
previous_size_(previous_size),
|
||||
handler_(handler)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Error>
|
||||
void operator()(const Error& e, std::size_t bytes_transferred)
|
||||
{
|
||||
storage_.resize(previous_size_ + bytes_transferred);
|
||||
io_service_.dispatch(detail::bind_handler(
|
||||
handler_, e, bytes_transferred));
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_service& io_service_;
|
||||
detail::buffered_stream_storage& storage_;
|
||||
std::size_t previous_size_;
|
||||
Handler handler_;
|
||||
};
|
||||
|
||||
/// Start an asynchronous fill.
|
||||
template <typename Handler>
|
||||
void async_fill(Handler handler)
|
||||
{
|
||||
std::size_t previous_size = storage_.size();
|
||||
storage_.resize(storage_.capacity());
|
||||
next_layer_.async_read_some(
|
||||
buffer(
|
||||
storage_.data() + previous_size,
|
||||
storage_.size() - previous_size),
|
||||
fill_handler<Handler>(io_service(), storage_, previous_size, handler));
|
||||
}
|
||||
|
||||
/// Read some data from the stream. Returns the number of bytes read. Throws
|
||||
/// an exception on failure.
|
||||
template <typename Mutable_Buffers>
|
||||
std::size_t read_some(const Mutable_Buffers& buffers)
|
||||
{
|
||||
if (storage_.empty())
|
||||
fill();
|
||||
return copy(buffers);
|
||||
}
|
||||
|
||||
/// Read some data from the stream. Returns the number of bytes read or 0 if
|
||||
/// an error occurred and the error handler did not throw an exception.
|
||||
template <typename Mutable_Buffers, typename Error_Handler>
|
||||
std::size_t read_some(const Mutable_Buffers& buffers,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
if (storage_.empty() && !fill(error_handler))
|
||||
return 0;
|
||||
return copy(buffers);
|
||||
}
|
||||
|
||||
template <typename Mutable_Buffers, typename Handler>
|
||||
class read_some_handler
|
||||
{
|
||||
public:
|
||||
read_some_handler(asio::io_service& io_service,
|
||||
detail::buffered_stream_storage& storage,
|
||||
const Mutable_Buffers& buffers, Handler handler)
|
||||
: io_service_(io_service),
|
||||
storage_(storage),
|
||||
buffers_(buffers),
|
||||
handler_(handler)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(const error_type& e, std::size_t)
|
||||
{
|
||||
if (e || storage_.empty())
|
||||
{
|
||||
std::size_t length = 0;
|
||||
io_service_.dispatch(detail::bind_handler(handler_, e, length));
|
||||
}
|
||||
else
|
||||
{
|
||||
using namespace std; // For memcpy.
|
||||
|
||||
std::size_t bytes_avail = storage_.size();
|
||||
std::size_t bytes_copied = 0;
|
||||
|
||||
typename Mutable_Buffers::const_iterator iter = buffers_.begin();
|
||||
typename Mutable_Buffers::const_iterator end = buffers_.end();
|
||||
for (; iter != end && bytes_avail > 0; ++iter)
|
||||
{
|
||||
std::size_t max_length = buffer_size(*iter);
|
||||
std::size_t length = (max_length < bytes_avail)
|
||||
? max_length : bytes_avail;
|
||||
memcpy(buffer_cast<void*>(*iter),
|
||||
storage_.data() + bytes_copied, length);
|
||||
bytes_copied += length;
|
||||
bytes_avail -= length;
|
||||
}
|
||||
|
||||
storage_.consume(bytes_copied);
|
||||
io_service_.dispatch(detail::bind_handler(handler_, e, bytes_copied));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_service& io_service_;
|
||||
detail::buffered_stream_storage& storage_;
|
||||
Mutable_Buffers buffers_;
|
||||
Handler handler_;
|
||||
};
|
||||
|
||||
/// Start an asynchronous read. The buffer into which the data will be read
|
||||
/// must be valid for the lifetime of the asynchronous operation.
|
||||
template <typename Mutable_Buffers, typename Handler>
|
||||
void async_read_some(const Mutable_Buffers& buffers, Handler handler)
|
||||
{
|
||||
if (storage_.empty())
|
||||
{
|
||||
async_fill(read_some_handler<Mutable_Buffers, Handler>(
|
||||
io_service(), storage_, buffers, handler));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::size_t length = copy(buffers);
|
||||
io_service().post(detail::bind_handler(handler, 0, length));
|
||||
}
|
||||
}
|
||||
|
||||
/// Peek at the incoming data on the stream. Returns the number of bytes read.
|
||||
/// Throws an exception on failure.
|
||||
template <typename Mutable_Buffers>
|
||||
std::size_t peek(const Mutable_Buffers& buffers)
|
||||
{
|
||||
if (storage_.empty())
|
||||
fill();
|
||||
return peek_copy(buffers);
|
||||
}
|
||||
|
||||
/// Peek at the incoming data on the stream. Returns the number of bytes read,
|
||||
/// or 0 if an error occurred and the error handler did not throw.
|
||||
template <typename Mutable_Buffers, typename Error_Handler>
|
||||
std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler)
|
||||
{
|
||||
if (storage_.empty() && !fill(error_handler))
|
||||
return 0;
|
||||
return peek_copy(buffers);
|
||||
}
|
||||
|
||||
/// Determine the amount of data that may be read without blocking.
|
||||
std::size_t in_avail()
|
||||
{
|
||||
return storage_.size();
|
||||
}
|
||||
|
||||
/// Determine the amount of data that may be read without blocking.
|
||||
template <typename Error_Handler>
|
||||
std::size_t in_avail(Error_Handler error_handler)
|
||||
{
|
||||
return storage_.size();
|
||||
}
|
||||
|
||||
private:
|
||||
/// Copy data out of the internal buffer to the specified target buffer.
|
||||
/// Returns the number of bytes copied.
|
||||
template <typename Mutable_Buffers>
|
||||
std::size_t copy(const Mutable_Buffers& buffers)
|
||||
{
|
||||
using namespace std; // For memcpy.
|
||||
|
||||
std::size_t bytes_avail = storage_.size();
|
||||
std::size_t bytes_copied = 0;
|
||||
|
||||
typename Mutable_Buffers::const_iterator iter = buffers.begin();
|
||||
typename Mutable_Buffers::const_iterator end = buffers.end();
|
||||
for (; iter != end && bytes_avail > 0; ++iter)
|
||||
{
|
||||
std::size_t max_length = buffer_size(*iter);
|
||||
std::size_t length = (max_length < bytes_avail)
|
||||
? max_length : bytes_avail;
|
||||
memcpy(buffer_cast<void*>(*iter), storage_.data() + bytes_copied, length);
|
||||
bytes_copied += length;
|
||||
bytes_avail -= length;
|
||||
}
|
||||
|
||||
storage_.consume(bytes_copied);
|
||||
return bytes_copied;
|
||||
}
|
||||
|
||||
/// Copy data from the internal buffer to the specified target buffer, without
|
||||
/// removing the data from the internal buffer. Returns the number of bytes
|
||||
/// copied.
|
||||
template <typename Mutable_Buffers>
|
||||
std::size_t peek_copy(const Mutable_Buffers& buffers)
|
||||
{
|
||||
using namespace std; // For memcpy.
|
||||
|
||||
std::size_t bytes_avail = storage_.size();
|
||||
std::size_t bytes_copied = 0;
|
||||
|
||||
typename Mutable_Buffers::const_iterator iter = buffers.begin();
|
||||
typename Mutable_Buffers::const_iterator end = buffers.end();
|
||||
for (; iter != end && bytes_avail > 0; ++iter)
|
||||
{
|
||||
std::size_t max_length = buffer_size(*iter);
|
||||
std::size_t length = (max_length < bytes_avail)
|
||||
? max_length : bytes_avail;
|
||||
memcpy(buffer_cast<void*>(*iter), storage_.data() + bytes_copied, length);
|
||||
bytes_copied += length;
|
||||
bytes_avail -= length;
|
||||
}
|
||||
|
||||
return bytes_copied;
|
||||
}
|
||||
|
||||
/// The next layer.
|
||||
Stream next_layer_;
|
||||
|
||||
// The data in the buffer.
|
||||
detail::buffered_stream_storage storage_;
|
||||
};
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_BUFFERED_READ_STREAM_HPP
|
||||
29
include/libtorrent/asio/buffered_read_stream_fwd.hpp
Normal file
29
include/libtorrent/asio/buffered_read_stream_fwd.hpp
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// buffered_read_stream_fwd.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_BUFFERED_READ_STREAM_FWD_HPP
|
||||
#define ASIO_BUFFERED_READ_STREAM_FWD_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
template <typename Stream>
|
||||
class buffered_read_stream;
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_BUFFERED_READ_STREAM_FWD_HPP
|
||||
249
include/libtorrent/asio/buffered_stream.hpp
Normal file
249
include/libtorrent/asio/buffered_stream.hpp
Normal file
@ -0,0 +1,249 @@
|
||||
//
|
||||
// buffered_stream.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_BUFFERED_STREAM_HPP
|
||||
#define ASIO_BUFFERED_STREAM_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstddef>
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/buffered_read_stream.hpp"
|
||||
#include "asio/buffered_write_stream.hpp"
|
||||
#include "asio/buffered_stream_fwd.hpp"
|
||||
#include "asio/error.hpp"
|
||||
#include "asio/io_service.hpp"
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
/// Adds buffering to the read- and write-related operations of a stream.
|
||||
/**
|
||||
* The buffered_stream class template can be used to add buffering to the
|
||||
* synchronous and asynchronous read and write operations of a stream.
|
||||
*
|
||||
* @par Thread Safety:
|
||||
* @e Distinct @e objects: Safe.@n
|
||||
* @e Shared @e objects: Unsafe.
|
||||
*
|
||||
* @par Concepts:
|
||||
* Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream,
|
||||
* Sync_Read_Stream, Sync_Write_Stream.
|
||||
*/
|
||||
template <typename Stream>
|
||||
class buffered_stream
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
/// The type of the next layer.
|
||||
typedef typename boost::remove_reference<Stream>::type next_layer_type;
|
||||
|
||||
/// The type of the lowest layer.
|
||||
typedef typename next_layer_type::lowest_layer_type lowest_layer_type;
|
||||
|
||||
/// The type used for reporting errors.
|
||||
typedef typename next_layer_type::error_type error_type;
|
||||
|
||||
/// Construct, passing the specified argument to initialise the next layer.
|
||||
template <typename Arg>
|
||||
explicit buffered_stream(Arg& a)
|
||||
: inner_stream_impl_(a),
|
||||
stream_impl_(inner_stream_impl_)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct, passing the specified argument to initialise the next layer.
|
||||
template <typename Arg>
|
||||
explicit buffered_stream(Arg& a, std::size_t read_buffer_size,
|
||||
std::size_t write_buffer_size)
|
||||
: inner_stream_impl_(a, write_buffer_size),
|
||||
stream_impl_(inner_stream_impl_, read_buffer_size)
|
||||
{
|
||||
}
|
||||
|
||||
/// Get a reference to the next layer.
|
||||
next_layer_type& next_layer()
|
||||
{
|
||||
return stream_impl_.next_layer().next_layer();
|
||||
}
|
||||
|
||||
/// Get a reference to the lowest layer.
|
||||
lowest_layer_type& lowest_layer()
|
||||
{
|
||||
return stream_impl_.lowest_layer();
|
||||
}
|
||||
|
||||
/// Get the io_service associated with the object.
|
||||
asio::io_service& io_service()
|
||||
{
|
||||
return stream_impl_.io_service();
|
||||
}
|
||||
|
||||
/// Close the stream.
|
||||
void close()
|
||||
{
|
||||
stream_impl_.close();
|
||||
}
|
||||
|
||||
/// Close the stream.
|
||||
template <typename Error_Handler>
|
||||
void close(Error_Handler error_handler)
|
||||
{
|
||||
stream_impl_.close(error_handler);
|
||||
}
|
||||
|
||||
/// Flush all data from the buffer to the next layer. Returns the number of
|
||||
/// bytes written to the next layer on the last write operation. Throws an
|
||||
/// exception on failure.
|
||||
std::size_t flush()
|
||||
{
|
||||
return stream_impl_.next_layer().flush();
|
||||
}
|
||||
|
||||
/// Flush all data from the buffer to the next layer. Returns the number of
|
||||
/// bytes written to the next layer on the last write operation, or 0 if an
|
||||
/// error occurred and the error handler did not throw.
|
||||
template <typename Error_Handler>
|
||||
std::size_t flush(Error_Handler error_handler)
|
||||
{
|
||||
return stream_impl_.next_layer().flush(error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous flush.
|
||||
template <typename Handler>
|
||||
void async_flush(Handler handler)
|
||||
{
|
||||
return stream_impl_.next_layer().async_flush(handler);
|
||||
}
|
||||
|
||||
/// Write the given data to the stream. Returns the number of bytes written.
|
||||
/// Throws an exception on failure.
|
||||
template <typename Const_Buffers>
|
||||
std::size_t write_some(const Const_Buffers& buffers)
|
||||
{
|
||||
return stream_impl_.write_some(buffers);
|
||||
}
|
||||
|
||||
/// Write the given data to the stream. Returns the number of bytes written,
|
||||
/// or 0 if an error occurred and the error handler did not throw.
|
||||
template <typename Const_Buffers, typename Error_Handler>
|
||||
std::size_t write_some(const Const_Buffers& buffers,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
return stream_impl_.write_some(buffers, error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous write. The data being written must be valid for the
|
||||
/// lifetime of the asynchronous operation.
|
||||
template <typename Const_Buffers, typename Handler>
|
||||
void async_write_some(const Const_Buffers& buffers, Handler handler)
|
||||
{
|
||||
stream_impl_.async_write_some(buffers, handler);
|
||||
}
|
||||
|
||||
/// Fill the buffer with some data. Returns the number of bytes placed in the
|
||||
/// buffer as a result of the operation. Throws an exception on failure.
|
||||
std::size_t fill()
|
||||
{
|
||||
return stream_impl_.fill();
|
||||
}
|
||||
|
||||
/// Fill the buffer with some data. Returns the number of bytes placed in the
|
||||
/// buffer as a result of the operation, or 0 if an error occurred and the
|
||||
/// error handler did not throw.
|
||||
template <typename Error_Handler>
|
||||
std::size_t fill(Error_Handler error_handler)
|
||||
{
|
||||
return stream_impl_.fill(error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous fill.
|
||||
template <typename Handler>
|
||||
void async_fill(Handler handler)
|
||||
{
|
||||
stream_impl_.async_fill(handler);
|
||||
}
|
||||
|
||||
/// Read some data from the stream. Returns the number of bytes read. Throws
|
||||
/// an exception on failure.
|
||||
template <typename Mutable_Buffers>
|
||||
std::size_t read_some(const Mutable_Buffers& buffers)
|
||||
{
|
||||
return stream_impl_.read_some(buffers);
|
||||
}
|
||||
|
||||
/// Read some data from the stream. Returns the number of bytes read or 0 if
|
||||
/// an error occurred and the error handler did not throw an exception.
|
||||
template <typename Mutable_Buffers, typename Error_Handler>
|
||||
std::size_t read_some(const Mutable_Buffers& buffers,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
return stream_impl_.read_some(buffers, error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous read. The buffer into which the data will be read
|
||||
/// must be valid for the lifetime of the asynchronous operation.
|
||||
template <typename Mutable_Buffers, typename Handler>
|
||||
void async_read_some(const Mutable_Buffers& buffers, Handler handler)
|
||||
{
|
||||
stream_impl_.async_read_some(buffers, handler);
|
||||
}
|
||||
|
||||
/// Peek at the incoming data on the stream. Returns the number of bytes read.
|
||||
/// Throws an exception on failure.
|
||||
template <typename Mutable_Buffers>
|
||||
std::size_t peek(const Mutable_Buffers& buffers)
|
||||
{
|
||||
return stream_impl_.peek(buffers);
|
||||
}
|
||||
|
||||
/// Peek at the incoming data on the stream. Returns the number of bytes read,
|
||||
/// or 0 if an error occurred and the error handler did not throw.
|
||||
template <typename Mutable_Buffers, typename Error_Handler>
|
||||
std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler)
|
||||
{
|
||||
return stream_impl_.peek(buffers, error_handler);
|
||||
}
|
||||
|
||||
/// Determine the amount of data that may be read without blocking.
|
||||
std::size_t in_avail()
|
||||
{
|
||||
return stream_impl_.in_avail();
|
||||
}
|
||||
|
||||
/// Determine the amount of data that may be read without blocking.
|
||||
template <typename Error_Handler>
|
||||
std::size_t in_avail(Error_Handler error_handler)
|
||||
{
|
||||
return stream_impl_.in_avail(error_handler);
|
||||
}
|
||||
|
||||
private:
|
||||
// The buffered write stream.
|
||||
typedef buffered_write_stream<Stream> write_stream_type;
|
||||
write_stream_type inner_stream_impl_;
|
||||
|
||||
// The buffered read stream.
|
||||
typedef buffered_read_stream<write_stream_type&> read_stream_type;
|
||||
read_stream_type stream_impl_;
|
||||
};
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_BUFFERED_STREAM_HPP
|
||||
29
include/libtorrent/asio/buffered_stream_fwd.hpp
Normal file
29
include/libtorrent/asio/buffered_stream_fwd.hpp
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// buffered_stream_fwd.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_BUFFERED_STREAM_FWD_HPP
|
||||
#define ASIO_BUFFERED_STREAM_FWD_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
template <typename Stream>
|
||||
class buffered_stream;
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_BUFFERED_STREAM_FWD_HPP
|
||||
362
include/libtorrent/asio/buffered_write_stream.hpp
Normal file
362
include/libtorrent/asio/buffered_write_stream.hpp
Normal file
@ -0,0 +1,362 @@
|
||||
//
|
||||
// buffered_write_stream.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_BUFFERED_WRITE_STREAM_HPP
|
||||
#define ASIO_BUFFERED_WRITE_STREAM_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/type_traits.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/buffered_write_stream_fwd.hpp"
|
||||
#include "asio/buffer.hpp"
|
||||
#include "asio/completion_condition.hpp"
|
||||
#include "asio/error.hpp"
|
||||
#include "asio/io_service.hpp"
|
||||
#include "asio/write.hpp"
|
||||
#include "asio/detail/bind_handler.hpp"
|
||||
#include "asio/detail/buffered_stream_storage.hpp"
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
/// Adds buffering to the write-related operations of a stream.
|
||||
/**
|
||||
* The buffered_write_stream class template can be used to add buffering to the
|
||||
* synchronous and asynchronous write operations of a stream.
|
||||
*
|
||||
* @par Thread Safety:
|
||||
* @e Distinct @e objects: Safe.@n
|
||||
* @e Shared @e objects: Unsafe.
|
||||
*
|
||||
* @par Concepts:
|
||||
* Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream,
|
||||
* Sync_Read_Stream, Sync_Write_Stream.
|
||||
*/
|
||||
template <typename Stream>
|
||||
class buffered_write_stream
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
/// The type of the next layer.
|
||||
typedef typename boost::remove_reference<Stream>::type next_layer_type;
|
||||
|
||||
/// The type of the lowest layer.
|
||||
typedef typename next_layer_type::lowest_layer_type lowest_layer_type;
|
||||
|
||||
/// The type used for reporting errors.
|
||||
typedef typename next_layer_type::error_type error_type;
|
||||
|
||||
#if defined(GENERATING_DOCUMENTATION)
|
||||
/// The default buffer size.
|
||||
static const std::size_t default_buffer_size = implementation_defined;
|
||||
#else
|
||||
BOOST_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024);
|
||||
#endif
|
||||
|
||||
/// Construct, passing the specified argument to initialise the next layer.
|
||||
template <typename Arg>
|
||||
explicit buffered_write_stream(Arg& a)
|
||||
: next_layer_(a),
|
||||
storage_(default_buffer_size)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct, passing the specified argument to initialise the next layer.
|
||||
template <typename Arg>
|
||||
buffered_write_stream(Arg& a, std::size_t buffer_size)
|
||||
: next_layer_(a),
|
||||
storage_(buffer_size)
|
||||
{
|
||||
}
|
||||
|
||||
/// Get a reference to the next layer.
|
||||
next_layer_type& next_layer()
|
||||
{
|
||||
return next_layer_;
|
||||
}
|
||||
|
||||
/// Get a reference to the lowest layer.
|
||||
lowest_layer_type& lowest_layer()
|
||||
{
|
||||
return next_layer_.lowest_layer();
|
||||
}
|
||||
|
||||
/// Get the io_service associated with the object.
|
||||
asio::io_service& io_service()
|
||||
{
|
||||
return next_layer_.io_service();
|
||||
}
|
||||
|
||||
/// Close the stream.
|
||||
void close()
|
||||
{
|
||||
next_layer_.close();
|
||||
}
|
||||
|
||||
/// Close the stream.
|
||||
template <typename Error_Handler>
|
||||
void close(Error_Handler error_handler)
|
||||
{
|
||||
next_layer_.close(error_handler);
|
||||
}
|
||||
|
||||
/// Flush all data from the buffer to the next layer. Returns the number of
|
||||
/// bytes written to the next layer on the last write operation. Throws an
|
||||
/// exception on failure.
|
||||
std::size_t flush()
|
||||
{
|
||||
std::size_t bytes_written = write(next_layer_,
|
||||
buffer(storage_.data(), storage_.size()));
|
||||
storage_.consume(bytes_written);
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
/// Flush all data from the buffer to the next layer. Returns the number of
|
||||
/// bytes written to the next layer on the last write operation, or 0 if an
|
||||
/// error occurred and the error handler did not throw.
|
||||
template <typename Error_Handler>
|
||||
std::size_t flush(Error_Handler error_handler)
|
||||
{
|
||||
std::size_t bytes_written = write(next_layer_,
|
||||
buffer(storage_.data(), storage_.size()),
|
||||
transfer_all(), error_handler);
|
||||
storage_.consume(bytes_written);
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
class flush_handler
|
||||
{
|
||||
public:
|
||||
flush_handler(asio::io_service& io_service,
|
||||
detail::buffered_stream_storage& storage, Handler handler)
|
||||
: io_service_(io_service),
|
||||
storage_(storage),
|
||||
handler_(handler)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(const error_type& e, std::size_t bytes_written)
|
||||
{
|
||||
storage_.consume(bytes_written);
|
||||
io_service_.dispatch(detail::bind_handler(handler_, e, bytes_written));
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_service& io_service_;
|
||||
detail::buffered_stream_storage& storage_;
|
||||
Handler handler_;
|
||||
};
|
||||
|
||||
/// Start an asynchronous flush.
|
||||
template <typename Handler>
|
||||
void async_flush(Handler handler)
|
||||
{
|
||||
async_write(next_layer_, buffer(storage_.data(), storage_.size()),
|
||||
flush_handler<Handler>(io_service(), storage_, handler));
|
||||
}
|
||||
|
||||
/// Write the given data to the stream. Returns the number of bytes written.
|
||||
/// Throws an exception on failure.
|
||||
template <typename Const_Buffers>
|
||||
std::size_t write_some(const Const_Buffers& buffers)
|
||||
{
|
||||
if (storage_.size() == storage_.capacity())
|
||||
flush();
|
||||
return copy(buffers);
|
||||
}
|
||||
|
||||
/// Write the given data to the stream. Returns the number of bytes written,
|
||||
/// or 0 if an error occurred and the error handler did not throw.
|
||||
template <typename Const_Buffers, typename Error_Handler>
|
||||
std::size_t write_some(const Const_Buffers& buffers,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
if (storage_.size() == storage_.capacity() && !flush(error_handler))
|
||||
return 0;
|
||||
return copy(buffers);
|
||||
}
|
||||
|
||||
template <typename Const_Buffers, typename Handler>
|
||||
class write_some_handler
|
||||
{
|
||||
public:
|
||||
write_some_handler(asio::io_service& io_service,
|
||||
detail::buffered_stream_storage& storage,
|
||||
const Const_Buffers& buffers, Handler handler)
|
||||
: io_service_(io_service),
|
||||
storage_(storage),
|
||||
buffers_(buffers),
|
||||
handler_(handler)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(const error_type& e, std::size_t)
|
||||
{
|
||||
if (e)
|
||||
{
|
||||
std::size_t length = 0;
|
||||
io_service_.dispatch(detail::bind_handler(handler_, e, length));
|
||||
}
|
||||
else
|
||||
{
|
||||
using namespace std; // For memcpy.
|
||||
|
||||
std::size_t orig_size = storage_.size();
|
||||
std::size_t space_avail = storage_.capacity() - orig_size;
|
||||
std::size_t bytes_copied = 0;
|
||||
|
||||
typename Const_Buffers::const_iterator iter = buffers_.begin();
|
||||
typename Const_Buffers::const_iterator end = buffers_.end();
|
||||
for (; iter != end && space_avail > 0; ++iter)
|
||||
{
|
||||
std::size_t bytes_avail = buffer_size(*iter);
|
||||
std::size_t length = (bytes_avail < space_avail)
|
||||
? bytes_avail : space_avail;
|
||||
storage_.resize(orig_size + bytes_copied + length);
|
||||
memcpy(storage_.data() + orig_size + bytes_copied,
|
||||
buffer_cast<const void*>(*iter), length);
|
||||
bytes_copied += length;
|
||||
space_avail -= length;
|
||||
}
|
||||
|
||||
io_service_.dispatch(detail::bind_handler(handler_, e, bytes_copied));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_service& io_service_;
|
||||
detail::buffered_stream_storage& storage_;
|
||||
Const_Buffers buffers_;
|
||||
Handler handler_;
|
||||
};
|
||||
|
||||
/// Start an asynchronous write. The data being written must be valid for the
|
||||
/// lifetime of the asynchronous operation.
|
||||
template <typename Const_Buffers, typename Handler>
|
||||
void async_write_some(const Const_Buffers& buffers, Handler handler)
|
||||
{
|
||||
if (storage_.size() == storage_.capacity())
|
||||
{
|
||||
async_flush(write_some_handler<Const_Buffers, Handler>(
|
||||
io_service(), storage_, buffers, handler));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::size_t bytes_copied = copy(buffers);
|
||||
io_service().post(detail::bind_handler(handler, 0, bytes_copied));
|
||||
}
|
||||
}
|
||||
|
||||
/// Read some data from the stream. Returns the number of bytes read. Throws
|
||||
/// an exception on failure.
|
||||
template <typename Mutable_Buffers>
|
||||
std::size_t read_some(const Mutable_Buffers& buffers)
|
||||
{
|
||||
return next_layer_.read_some(buffers);
|
||||
}
|
||||
|
||||
/// Read some data from the stream. Returns the number of bytes read or 0 if
|
||||
/// an error occurred and the error handler did not throw an exception.
|
||||
template <typename Mutable_Buffers, typename Error_Handler>
|
||||
std::size_t read_some(const Mutable_Buffers& buffers,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
return next_layer_.read_some(buffers, error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous read. The buffer into which the data will be read
|
||||
/// must be valid for the lifetime of the asynchronous operation.
|
||||
template <typename Mutable_Buffers, typename Handler>
|
||||
void async_read_some(const Mutable_Buffers& buffers, Handler handler)
|
||||
{
|
||||
next_layer_.async_read_some(buffers, handler);
|
||||
}
|
||||
|
||||
/// Peek at the incoming data on the stream. Returns the number of bytes read.
|
||||
/// Throws an exception on failure.
|
||||
template <typename Mutable_Buffers>
|
||||
std::size_t peek(const Mutable_Buffers& buffers)
|
||||
{
|
||||
return next_layer_.peek(buffers);
|
||||
}
|
||||
|
||||
/// Peek at the incoming data on the stream. Returns the number of bytes read,
|
||||
/// or 0 if an error occurred and the error handler did not throw.
|
||||
template <typename Mutable_Buffers, typename Error_Handler>
|
||||
std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler)
|
||||
{
|
||||
return next_layer_.peek(buffers, error_handler);
|
||||
}
|
||||
|
||||
/// Determine the amount of data that may be read without blocking.
|
||||
std::size_t in_avail()
|
||||
{
|
||||
return next_layer_.in_avail();
|
||||
}
|
||||
|
||||
/// Determine the amount of data that may be read without blocking.
|
||||
template <typename Error_Handler>
|
||||
std::size_t in_avail(Error_Handler error_handler)
|
||||
{
|
||||
return next_layer_.in_avail(error_handler);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Copy data into the internal buffer from the specified source buffer.
|
||||
/// Returns the number of bytes copied.
|
||||
template <typename Const_Buffers>
|
||||
std::size_t copy(const Const_Buffers& buffers)
|
||||
{
|
||||
using namespace std; // For memcpy.
|
||||
|
||||
std::size_t orig_size = storage_.size();
|
||||
std::size_t space_avail = storage_.capacity() - orig_size;
|
||||
std::size_t bytes_copied = 0;
|
||||
|
||||
typename Const_Buffers::const_iterator iter = buffers.begin();
|
||||
typename Const_Buffers::const_iterator end = buffers.end();
|
||||
for (; iter != end && space_avail > 0; ++iter)
|
||||
{
|
||||
std::size_t bytes_avail = buffer_size(*iter);
|
||||
std::size_t length = (bytes_avail < space_avail)
|
||||
? bytes_avail : space_avail;
|
||||
storage_.resize(orig_size + bytes_copied + length);
|
||||
memcpy(storage_.data() + orig_size + bytes_copied,
|
||||
buffer_cast<const void*>(*iter), length);
|
||||
bytes_copied += length;
|
||||
space_avail -= length;
|
||||
}
|
||||
|
||||
return bytes_copied;
|
||||
}
|
||||
|
||||
/// The next layer.
|
||||
Stream next_layer_;
|
||||
|
||||
// The data in the buffer.
|
||||
detail::buffered_stream_storage storage_;
|
||||
};
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_BUFFERED_WRITE_STREAM_HPP
|
||||
29
include/libtorrent/asio/buffered_write_stream_fwd.hpp
Normal file
29
include/libtorrent/asio/buffered_write_stream_fwd.hpp
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// buffered_write_stream_fwd.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_BUFFERED_WRITE_STREAM_FWD_HPP
|
||||
#define ASIO_BUFFERED_WRITE_STREAM_FWD_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
template <typename Stream>
|
||||
class buffered_write_stream;
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_BUFFERED_WRITE_STREAM_FWD_HPP
|
||||
101
include/libtorrent/asio/completion_condition.hpp
Normal file
101
include/libtorrent/asio/completion_condition.hpp
Normal file
@ -0,0 +1,101 @@
|
||||
//
|
||||
// completion_condition.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_COMPLETION_CONDITION_HPP
|
||||
#define ASIO_COMPLETION_CONDITION_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstddef>
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class transfer_all_t
|
||||
{
|
||||
public:
|
||||
typedef bool result_type;
|
||||
|
||||
template <typename Error>
|
||||
bool operator()(const Error& err, std::size_t)
|
||||
{
|
||||
return !!err;
|
||||
}
|
||||
};
|
||||
|
||||
class transfer_at_least_t
|
||||
{
|
||||
public:
|
||||
typedef bool result_type;
|
||||
|
||||
explicit transfer_at_least_t(std::size_t minimum)
|
||||
: minimum_(minimum)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Error>
|
||||
bool operator()(const Error& err, std::size_t bytes_transferred)
|
||||
{
|
||||
return !!err || bytes_transferred >= minimum_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t minimum_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @defgroup completion_condition Completion Condition Function Objects
|
||||
*
|
||||
* Function objects used for determining when a read or write operation should
|
||||
* complete.
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/// Return a completion condition function object that indicates that a read or
|
||||
/// write operation should continue until all of the data has been transferred,
|
||||
/// or until an error occurs.
|
||||
#if defined(GENERATING_DOCUMENTATION)
|
||||
unspecified transfer_all();
|
||||
#else
|
||||
inline detail::transfer_all_t transfer_all()
|
||||
{
|
||||
return detail::transfer_all_t();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Return a completion condition function object that indicates that a read or
|
||||
/// write operation should continue until a minimum number of bytes has been
|
||||
/// transferred, or until an error occurs.
|
||||
#if defined(GENERATING_DOCUMENTATION)
|
||||
unspecified transfer_at_least(std::size_t minimum);
|
||||
#else
|
||||
inline detail::transfer_at_least_t transfer_at_least(std::size_t minimum)
|
||||
{
|
||||
return detail::transfer_at_least_t(minimum);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*@}*/
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_COMPLETION_CONDITION_HPP
|
||||
295
include/libtorrent/asio/datagram_socket_service.hpp
Normal file
295
include/libtorrent/asio/datagram_socket_service.hpp
Normal file
@ -0,0 +1,295 @@
|
||||
//
|
||||
// datagram_socket_service.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DATAGRAM_SOCKET_SERVICE_HPP
|
||||
#define ASIO_DATAGRAM_SOCKET_SERVICE_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstddef>
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/io_service.hpp"
|
||||
#include "asio/detail/epoll_reactor.hpp"
|
||||
#include "asio/detail/kqueue_reactor.hpp"
|
||||
#include "asio/detail/select_reactor.hpp"
|
||||
#include "asio/detail/reactive_socket_service.hpp"
|
||||
#include "asio/detail/win_iocp_socket_service.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
/// Default service implementation for a datagram socket.
|
||||
template <typename Protocol>
|
||||
class datagram_socket_service
|
||||
: public asio::io_service::service
|
||||
{
|
||||
public:
|
||||
/// The protocol type.
|
||||
typedef Protocol protocol_type;
|
||||
|
||||
/// The endpoint type.
|
||||
typedef typename Protocol::endpoint endpoint_type;
|
||||
|
||||
private:
|
||||
// The type of the platform-specific implementation.
|
||||
#if defined(ASIO_HAS_IOCP)
|
||||
typedef detail::win_iocp_socket_service<Protocol> service_impl_type;
|
||||
#elif defined(ASIO_HAS_EPOLL)
|
||||
typedef detail::reactive_socket_service<
|
||||
Protocol, detail::epoll_reactor<false> > service_impl_type;
|
||||
#elif defined(ASIO_HAS_KQUEUE)
|
||||
typedef detail::reactive_socket_service<
|
||||
Protocol, detail::kqueue_reactor<false> > service_impl_type;
|
||||
#else
|
||||
typedef detail::reactive_socket_service<
|
||||
Protocol, detail::select_reactor<false> > service_impl_type;
|
||||
#endif
|
||||
|
||||
public:
|
||||
/// The type of a datagram socket.
|
||||
#if defined(GENERATING_DOCUMENTATION)
|
||||
typedef implementation_defined implementation_type;
|
||||
#else
|
||||
typedef typename service_impl_type::implementation_type implementation_type;
|
||||
#endif
|
||||
|
||||
/// The native socket type.
|
||||
#if defined(GENERATING_DOCUMENTATION)
|
||||
typedef implementation_defined native_type;
|
||||
#else
|
||||
typedef typename service_impl_type::native_type native_type;
|
||||
#endif
|
||||
|
||||
/// Construct a new datagram socket service for the specified io_service.
|
||||
explicit datagram_socket_service(asio::io_service& io_service)
|
||||
: asio::io_service::service(io_service),
|
||||
service_impl_(asio::use_service<service_impl_type>(io_service))
|
||||
{
|
||||
}
|
||||
|
||||
/// Destroy all user-defined handler objects owned by the service.
|
||||
void shutdown_service()
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct a new datagram socket implementation.
|
||||
void construct(implementation_type& impl)
|
||||
{
|
||||
service_impl_.construct(impl);
|
||||
}
|
||||
|
||||
/// Destroy a datagram socket implementation.
|
||||
void destroy(implementation_type& impl)
|
||||
{
|
||||
service_impl_.destroy(impl);
|
||||
}
|
||||
|
||||
// Open a new datagram socket implementation.
|
||||
template <typename Error_Handler>
|
||||
void open(implementation_type& impl, const protocol_type& protocol,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
if (protocol.type() == SOCK_DGRAM)
|
||||
service_impl_.open(impl, protocol, error_handler);
|
||||
else
|
||||
error_handler(asio::error(asio::error::invalid_argument));
|
||||
}
|
||||
|
||||
/// Assign an existing native socket to a datagram socket.
|
||||
template <typename Error_Handler>
|
||||
void assign(implementation_type& impl, const protocol_type& protocol,
|
||||
const native_type& native_socket, Error_Handler error_handler)
|
||||
{
|
||||
service_impl_.assign(impl, protocol, native_socket, error_handler);
|
||||
}
|
||||
|
||||
/// Close a datagram socket implementation.
|
||||
template <typename Error_Handler>
|
||||
void close(implementation_type& impl, Error_Handler error_handler)
|
||||
{
|
||||
service_impl_.close(impl, error_handler);
|
||||
}
|
||||
|
||||
/// Get the native socket implementation.
|
||||
native_type native(implementation_type& impl)
|
||||
{
|
||||
return service_impl_.native(impl);
|
||||
}
|
||||
|
||||
/// Cancel all asynchronous operations associated with the socket.
|
||||
template <typename Error_Handler>
|
||||
void cancel(implementation_type& impl, Error_Handler error_handler)
|
||||
{
|
||||
service_impl_.cancel(impl, error_handler);
|
||||
}
|
||||
|
||||
// Bind the datagram socket to the specified local endpoint.
|
||||
template <typename Error_Handler>
|
||||
void bind(implementation_type& impl, const endpoint_type& endpoint,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
service_impl_.bind(impl, endpoint, error_handler);
|
||||
}
|
||||
|
||||
/// Connect the datagram socket to the specified endpoint.
|
||||
template <typename Error_Handler>
|
||||
void connect(implementation_type& impl, const endpoint_type& peer_endpoint,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
service_impl_.connect(impl, peer_endpoint, error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous connect.
|
||||
template <typename Handler>
|
||||
void async_connect(implementation_type& impl,
|
||||
const endpoint_type& peer_endpoint, Handler handler)
|
||||
{
|
||||
service_impl_.async_connect(impl, peer_endpoint, handler);
|
||||
}
|
||||
|
||||
/// Set a socket option.
|
||||
template <typename Option, typename Error_Handler>
|
||||
void set_option(implementation_type& impl, const Option& option,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
service_impl_.set_option(impl, option, error_handler);
|
||||
}
|
||||
|
||||
/// Get a socket option.
|
||||
template <typename Option, typename Error_Handler>
|
||||
void get_option(const implementation_type& impl, Option& option,
|
||||
Error_Handler error_handler) const
|
||||
{
|
||||
service_impl_.get_option(impl, option, error_handler);
|
||||
}
|
||||
|
||||
/// Perform an IO control command on the socket.
|
||||
template <typename IO_Control_Command, typename Error_Handler>
|
||||
void io_control(implementation_type& impl, IO_Control_Command& command,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
service_impl_.io_control(impl, command, error_handler);
|
||||
}
|
||||
|
||||
/// Get the local endpoint.
|
||||
template <typename Error_Handler>
|
||||
endpoint_type local_endpoint(const implementation_type& impl,
|
||||
Error_Handler error_handler) const
|
||||
{
|
||||
endpoint_type endpoint;
|
||||
service_impl_.get_local_endpoint(impl, endpoint, error_handler);
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
/// Get the remote endpoint.
|
||||
template <typename Error_Handler>
|
||||
endpoint_type remote_endpoint(const implementation_type& impl,
|
||||
Error_Handler error_handler) const
|
||||
{
|
||||
endpoint_type endpoint;
|
||||
service_impl_.get_remote_endpoint(impl, endpoint, error_handler);
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
/// Disable sends or receives on the socket.
|
||||
template <typename Error_Handler>
|
||||
void shutdown(implementation_type& impl, socket_base::shutdown_type what,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
service_impl_.shutdown(impl, what, error_handler);
|
||||
}
|
||||
|
||||
/// Send the given data to the peer.
|
||||
template <typename Const_Buffers, typename Error_Handler>
|
||||
std::size_t send(implementation_type& impl, const Const_Buffers& buffers,
|
||||
socket_base::message_flags flags, Error_Handler error_handler)
|
||||
{
|
||||
return service_impl_.send(impl, buffers, flags, error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous send.
|
||||
template <typename Const_Buffers, typename Handler>
|
||||
void async_send(implementation_type& impl, const Const_Buffers& buffers,
|
||||
socket_base::message_flags flags, Handler handler)
|
||||
{
|
||||
service_impl_.async_send(impl, buffers, flags, handler);
|
||||
}
|
||||
|
||||
/// Send a datagram to the specified endpoint.
|
||||
template <typename Const_Buffers, typename Error_Handler>
|
||||
std::size_t send_to(implementation_type& impl, const Const_Buffers& buffers,
|
||||
const endpoint_type& destination, socket_base::message_flags flags,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
return service_impl_.send_to(impl, buffers, destination, flags,
|
||||
error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous send.
|
||||
template <typename Const_Buffers, typename Handler>
|
||||
void async_send_to(implementation_type& impl, const Const_Buffers& buffers,
|
||||
const endpoint_type& destination, socket_base::message_flags flags,
|
||||
Handler handler)
|
||||
{
|
||||
service_impl_.async_send_to(impl, buffers, destination, flags, handler);
|
||||
}
|
||||
|
||||
/// Receive some data from the peer.
|
||||
template <typename Mutable_Buffers, typename Error_Handler>
|
||||
std::size_t receive(implementation_type& impl, const Mutable_Buffers& buffers,
|
||||
socket_base::message_flags flags, Error_Handler error_handler)
|
||||
{
|
||||
return service_impl_.receive(impl, buffers, flags, error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous receive.
|
||||
template <typename Mutable_Buffers, typename Handler>
|
||||
void async_receive(implementation_type& impl, const Mutable_Buffers& buffers,
|
||||
socket_base::message_flags flags, Handler handler)
|
||||
{
|
||||
service_impl_.async_receive(impl, buffers, flags, handler);
|
||||
}
|
||||
|
||||
/// Receive a datagram with the endpoint of the sender.
|
||||
template <typename Mutable_Buffers, typename Error_Handler>
|
||||
std::size_t receive_from(implementation_type& impl,
|
||||
const Mutable_Buffers& buffers, endpoint_type& sender_endpoint,
|
||||
socket_base::message_flags flags, Error_Handler error_handler)
|
||||
{
|
||||
return service_impl_.receive_from(impl, buffers, sender_endpoint, flags,
|
||||
error_handler);
|
||||
}
|
||||
|
||||
/// Start an asynchronous receive that will get the endpoint of the sender.
|
||||
template <typename Mutable_Buffers, typename Handler>
|
||||
void async_receive_from(implementation_type& impl,
|
||||
const Mutable_Buffers& buffers, endpoint_type& sender_endpoint,
|
||||
socket_base::message_flags flags, Handler handler)
|
||||
{
|
||||
service_impl_.async_receive_from(impl, buffers, sender_endpoint, flags,
|
||||
handler);
|
||||
}
|
||||
|
||||
private:
|
||||
// The service that provides the platform-specific implementation.
|
||||
service_impl_type& service_impl_;
|
||||
};
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DATAGRAM_SOCKET_SERVICE_HPP
|
||||
37
include/libtorrent/asio/deadline_timer.hpp
Normal file
37
include/libtorrent/asio/deadline_timer.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
//
|
||||
// deadline_timer.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DEADLINE_TIMER_HPP
|
||||
#define ASIO_DEADLINE_TIMER_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/socket_types.hpp" // Must come before posix_time.
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/basic_deadline_timer.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
/// Typedef for the typical usage of timer.
|
||||
typedef basic_deadline_timer<boost::posix_time::ptime> deadline_timer;
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DEADLINE_TIMER_HPP
|
||||
152
include/libtorrent/asio/deadline_timer_service.hpp
Normal file
152
include/libtorrent/asio/deadline_timer_service.hpp
Normal file
@ -0,0 +1,152 @@
|
||||
//
|
||||
// deadline_timer_service.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DEADLINE_TIMER_SERVICE_HPP
|
||||
#define ASIO_DEADLINE_TIMER_SERVICE_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstddef>
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/io_service.hpp"
|
||||
#include "asio/time_traits.hpp"
|
||||
#include "asio/detail/deadline_timer_service.hpp"
|
||||
#include "asio/detail/epoll_reactor.hpp"
|
||||
#include "asio/detail/kqueue_reactor.hpp"
|
||||
#include "asio/detail/select_reactor.hpp"
|
||||
|
||||
namespace asio {
|
||||
|
||||
/// Default service implementation for a timer.
|
||||
template <typename Time_Type,
|
||||
typename Time_Traits = asio::time_traits<Time_Type> >
|
||||
class deadline_timer_service
|
||||
: public asio::io_service::service
|
||||
{
|
||||
public:
|
||||
/// The time traits type.
|
||||
typedef Time_Traits traits_type;
|
||||
|
||||
/// The time type.
|
||||
typedef typename traits_type::time_type time_type;
|
||||
|
||||
/// The duration type.
|
||||
typedef typename traits_type::duration_type duration_type;
|
||||
|
||||
private:
|
||||
// The type of the platform-specific implementation.
|
||||
#if defined(ASIO_HAS_IOCP)
|
||||
typedef detail::deadline_timer_service<
|
||||
traits_type, detail::select_reactor<true> > service_impl_type;
|
||||
#elif defined(ASIO_HAS_EPOLL)
|
||||
typedef detail::deadline_timer_service<
|
||||
traits_type, detail::epoll_reactor<false> > service_impl_type;
|
||||
#elif defined(ASIO_HAS_KQUEUE)
|
||||
typedef detail::deadline_timer_service<
|
||||
traits_type, detail::kqueue_reactor<false> > service_impl_type;
|
||||
#else
|
||||
typedef detail::deadline_timer_service<
|
||||
traits_type, detail::select_reactor<false> > service_impl_type;
|
||||
#endif
|
||||
|
||||
public:
|
||||
/// The implementation type of the deadline timer.
|
||||
#if defined(GENERATING_DOCUMENTATION)
|
||||
typedef implementation_defined implementation_type;
|
||||
#else
|
||||
typedef typename service_impl_type::implementation_type implementation_type;
|
||||
#endif
|
||||
|
||||
/// Construct a new timer service for the specified io_service.
|
||||
explicit deadline_timer_service(asio::io_service& io_service)
|
||||
: asio::io_service::service(io_service),
|
||||
service_impl_(asio::use_service<service_impl_type>(io_service))
|
||||
{
|
||||
}
|
||||
|
||||
/// Destroy all user-defined handler objects owned by the service.
|
||||
void shutdown_service()
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct a new timer implementation.
|
||||
void construct(implementation_type& impl)
|
||||
{
|
||||
service_impl_.construct(impl);
|
||||
}
|
||||
|
||||
/// Destroy a timer implementation.
|
||||
void destroy(implementation_type& impl)
|
||||
{
|
||||
service_impl_.destroy(impl);
|
||||
}
|
||||
|
||||
/// Cancel any asynchronous wait operations associated with the timer.
|
||||
std::size_t cancel(implementation_type& impl)
|
||||
{
|
||||
return service_impl_.cancel(impl);
|
||||
}
|
||||
|
||||
/// Get the expiry time for the timer as an absolute time.
|
||||
time_type expires_at(const implementation_type& impl) const
|
||||
{
|
||||
return service_impl_.expires_at(impl);
|
||||
}
|
||||
|
||||
/// Set the expiry time for the timer as an absolute time.
|
||||
std::size_t expires_at(implementation_type& impl,
|
||||
const time_type& expiry_time)
|
||||
{
|
||||
return service_impl_.expires_at(impl, expiry_time);
|
||||
}
|
||||
|
||||
/// Get the expiry time for the timer relative to now.
|
||||
duration_type expires_from_now(const implementation_type& impl) const
|
||||
{
|
||||
return service_impl_.expires_from_now(impl);
|
||||
}
|
||||
|
||||
/// Set the expiry time for the timer relative to now.
|
||||
std::size_t expires_from_now(implementation_type& impl,
|
||||
const duration_type& expiry_time)
|
||||
{
|
||||
return service_impl_.expires_from_now(impl, expiry_time);
|
||||
}
|
||||
|
||||
// Perform a blocking wait on the timer.
|
||||
void wait(implementation_type& impl)
|
||||
{
|
||||
service_impl_.wait(impl);
|
||||
}
|
||||
|
||||
// Start an asynchronous wait on the timer.
|
||||
template <typename Handler>
|
||||
void async_wait(implementation_type& impl, Handler handler)
|
||||
{
|
||||
service_impl_.async_wait(impl, handler);
|
||||
}
|
||||
|
||||
private:
|
||||
// The service that provides the platform-specific implementation.
|
||||
service_impl_type& service_impl_;
|
||||
};
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DEADLINE_TIMER_SERVICE_HPP
|
||||
349
include/libtorrent/asio/detail/bind_handler.hpp
Normal file
349
include/libtorrent/asio/detail/bind_handler.hpp
Normal file
@ -0,0 +1,349 @@
|
||||
//
|
||||
// bind_handler.hpp
|
||||
// ~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_BIND_HANDLER_HPP
|
||||
#define ASIO_DETAIL_BIND_HANDLER_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/handler_alloc_helpers.hpp"
|
||||
#include "asio/detail/handler_invoke_helpers.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
template <typename Handler, typename Arg1>
|
||||
class binder1
|
||||
{
|
||||
public:
|
||||
binder1(const Handler& handler, const Arg1& arg1)
|
||||
: handler_(handler),
|
||||
arg1_(arg1)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
handler_(arg1_);
|
||||
}
|
||||
|
||||
void operator()() const
|
||||
{
|
||||
handler_(arg1_);
|
||||
}
|
||||
|
||||
//private:
|
||||
Handler handler_;
|
||||
Arg1 arg1_;
|
||||
};
|
||||
|
||||
template <typename Handler, typename Arg1>
|
||||
inline void* asio_handler_allocate(std::size_t size,
|
||||
binder1<Handler, Arg1>* this_handler)
|
||||
{
|
||||
return asio_handler_alloc_helpers::allocate(
|
||||
size, &this_handler->handler_);
|
||||
}
|
||||
|
||||
template <typename Handler, typename Arg1>
|
||||
inline void asio_handler_deallocate(void* pointer, std::size_t size,
|
||||
binder1<Handler, Arg1>* this_handler)
|
||||
{
|
||||
asio_handler_alloc_helpers::deallocate(
|
||||
pointer, size, &this_handler->handler_);
|
||||
}
|
||||
|
||||
template <typename Function, typename Handler, typename Arg1>
|
||||
inline void asio_handler_invoke(const Function& function,
|
||||
binder1<Handler, Arg1>* this_handler)
|
||||
{
|
||||
asio_handler_invoke_helpers::invoke(
|
||||
function, &this_handler->handler_);
|
||||
}
|
||||
|
||||
template <typename Handler, typename Arg1>
|
||||
inline binder1<Handler, Arg1> bind_handler(const Handler& handler,
|
||||
const Arg1& arg1)
|
||||
{
|
||||
return binder1<Handler, Arg1>(handler, arg1);
|
||||
}
|
||||
|
||||
template <typename Handler, typename Arg1, typename Arg2>
|
||||
class binder2
|
||||
{
|
||||
public:
|
||||
binder2(const Handler& handler, const Arg1& arg1, const Arg2& arg2)
|
||||
: handler_(handler),
|
||||
arg1_(arg1),
|
||||
arg2_(arg2)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
handler_(arg1_, arg2_);
|
||||
}
|
||||
|
||||
void operator()() const
|
||||
{
|
||||
handler_(arg1_, arg2_);
|
||||
}
|
||||
|
||||
//private:
|
||||
Handler handler_;
|
||||
Arg1 arg1_;
|
||||
Arg2 arg2_;
|
||||
};
|
||||
|
||||
template <typename Handler, typename Arg1, typename Arg2>
|
||||
inline void* asio_handler_allocate(std::size_t size,
|
||||
binder2<Handler, Arg1, Arg2>* this_handler)
|
||||
{
|
||||
return asio_handler_alloc_helpers::allocate(
|
||||
size, &this_handler->handler_);
|
||||
}
|
||||
|
||||
template <typename Handler, typename Arg1, typename Arg2>
|
||||
inline void asio_handler_deallocate(void* pointer, std::size_t size,
|
||||
binder2<Handler, Arg1, Arg2>* this_handler)
|
||||
{
|
||||
asio_handler_alloc_helpers::deallocate(
|
||||
pointer, size, &this_handler->handler_);
|
||||
}
|
||||
|
||||
template <typename Function, typename Handler, typename Arg1, typename Arg2>
|
||||
inline void asio_handler_invoke(const Function& function,
|
||||
binder2<Handler, Arg1, Arg2>* this_handler)
|
||||
{
|
||||
asio_handler_invoke_helpers::invoke(
|
||||
function, &this_handler->handler_);
|
||||
}
|
||||
|
||||
template <typename Handler, typename Arg1, typename Arg2>
|
||||
inline binder2<Handler, Arg1, Arg2> bind_handler(const Handler& handler,
|
||||
const Arg1& arg1, const Arg2& arg2)
|
||||
{
|
||||
return binder2<Handler, Arg1, Arg2>(handler, arg1, arg2);
|
||||
}
|
||||
|
||||
template <typename Handler, typename Arg1, typename Arg2, typename Arg3>
|
||||
class binder3
|
||||
{
|
||||
public:
|
||||
binder3(const Handler& handler, const Arg1& arg1, const Arg2& arg2,
|
||||
const Arg3& arg3)
|
||||
: handler_(handler),
|
||||
arg1_(arg1),
|
||||
arg2_(arg2),
|
||||
arg3_(arg3)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
handler_(arg1_, arg2_, arg3_);
|
||||
}
|
||||
|
||||
void operator()() const
|
||||
{
|
||||
handler_(arg1_, arg2_, arg3_);
|
||||
}
|
||||
|
||||
//private:
|
||||
Handler handler_;
|
||||
Arg1 arg1_;
|
||||
Arg2 arg2_;
|
||||
Arg3 arg3_;
|
||||
};
|
||||
|
||||
template <typename Handler, typename Arg1, typename Arg2, typename Arg3>
|
||||
inline void* asio_handler_allocate(std::size_t size,
|
||||
binder3<Handler, Arg1, Arg2, Arg3>* this_handler)
|
||||
{
|
||||
return asio_handler_alloc_helpers::allocate(
|
||||
size, &this_handler->handler_);
|
||||
}
|
||||
|
||||
template <typename Handler, typename Arg1, typename Arg2, typename Arg3>
|
||||
inline void asio_handler_deallocate(void* pointer, std::size_t size,
|
||||
binder3<Handler, Arg1, Arg2, Arg3>* this_handler)
|
||||
{
|
||||
asio_handler_alloc_helpers::deallocate(
|
||||
pointer, size, &this_handler->handler_);
|
||||
}
|
||||
|
||||
template <typename Function, typename Handler, typename Arg1, typename Arg2,
|
||||
typename Arg3>
|
||||
inline void asio_handler_invoke(const Function& function,
|
||||
binder3<Handler, Arg1, Arg2, Arg3>* this_handler)
|
||||
{
|
||||
asio_handler_invoke_helpers::invoke(
|
||||
function, &this_handler->handler_);
|
||||
}
|
||||
|
||||
template <typename Handler, typename Arg1, typename Arg2, typename Arg3>
|
||||
inline binder3<Handler, Arg1, Arg2, Arg3> bind_handler(const Handler& handler,
|
||||
const Arg1& arg1, const Arg2& arg2, const Arg3& arg3)
|
||||
{
|
||||
return binder3<Handler, Arg1, Arg2, Arg3>(handler, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
|
||||
typename Arg4>
|
||||
class binder4
|
||||
{
|
||||
public:
|
||||
binder4(const Handler& handler, const Arg1& arg1, const Arg2& arg2,
|
||||
const Arg3& arg3, const Arg4& arg4)
|
||||
: handler_(handler),
|
||||
arg1_(arg1),
|
||||
arg2_(arg2),
|
||||
arg3_(arg3),
|
||||
arg4_(arg4)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
handler_(arg1_, arg2_, arg3_, arg4_);
|
||||
}
|
||||
|
||||
void operator()() const
|
||||
{
|
||||
handler_(arg1_, arg2_, arg3_, arg4_);
|
||||
}
|
||||
|
||||
//private:
|
||||
Handler handler_;
|
||||
Arg1 arg1_;
|
||||
Arg2 arg2_;
|
||||
Arg3 arg3_;
|
||||
Arg4 arg4_;
|
||||
};
|
||||
|
||||
template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
|
||||
typename Arg4>
|
||||
inline void* asio_handler_allocate(std::size_t size,
|
||||
binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler)
|
||||
{
|
||||
return asio_handler_alloc_helpers::allocate(
|
||||
size, &this_handler->handler_);
|
||||
}
|
||||
|
||||
template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
|
||||
typename Arg4>
|
||||
inline void asio_handler_deallocate(void* pointer, std::size_t size,
|
||||
binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler)
|
||||
{
|
||||
asio_handler_alloc_helpers::deallocate(
|
||||
pointer, size, &this_handler->handler_);
|
||||
}
|
||||
|
||||
template <typename Function, typename Handler, typename Arg1, typename Arg2,
|
||||
typename Arg3, typename Arg4>
|
||||
inline void asio_handler_invoke(const Function& function,
|
||||
binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler)
|
||||
{
|
||||
asio_handler_invoke_helpers::invoke(
|
||||
function, &this_handler->handler_);
|
||||
}
|
||||
|
||||
template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
|
||||
typename Arg4>
|
||||
inline binder4<Handler, Arg1, Arg2, Arg3, Arg4> bind_handler(
|
||||
const Handler& handler, const Arg1& arg1, const Arg2& arg2,
|
||||
const Arg3& arg3, const Arg4& arg4)
|
||||
{
|
||||
return binder4<Handler, Arg1, Arg2, Arg3, Arg4>(handler, arg1, arg2, arg3,
|
||||
arg4);
|
||||
}
|
||||
|
||||
template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
|
||||
typename Arg4, typename Arg5>
|
||||
class binder5
|
||||
{
|
||||
public:
|
||||
binder5(const Handler& handler, const Arg1& arg1, const Arg2& arg2,
|
||||
const Arg3& arg3, const Arg4& arg4, const Arg5& arg5)
|
||||
: handler_(handler),
|
||||
arg1_(arg1),
|
||||
arg2_(arg2),
|
||||
arg3_(arg3),
|
||||
arg4_(arg4),
|
||||
arg5_(arg5)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
handler_(arg1_, arg2_, arg3_, arg4_, arg5_);
|
||||
}
|
||||
|
||||
void operator()() const
|
||||
{
|
||||
handler_(arg1_, arg2_, arg3_, arg4_, arg5_);
|
||||
}
|
||||
|
||||
//private:
|
||||
Handler handler_;
|
||||
Arg1 arg1_;
|
||||
Arg2 arg2_;
|
||||
Arg3 arg3_;
|
||||
Arg4 arg4_;
|
||||
Arg5 arg5_;
|
||||
};
|
||||
|
||||
template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
|
||||
typename Arg4, typename Arg5>
|
||||
inline void* asio_handler_allocate(std::size_t size,
|
||||
binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler)
|
||||
{
|
||||
return asio_handler_alloc_helpers::allocate(
|
||||
size, &this_handler->handler_);
|
||||
}
|
||||
|
||||
template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
|
||||
typename Arg4, typename Arg5>
|
||||
inline void asio_handler_deallocate(void* pointer, std::size_t size,
|
||||
binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler)
|
||||
{
|
||||
asio_handler_alloc_helpers::deallocate(
|
||||
pointer, size, &this_handler->handler_);
|
||||
}
|
||||
|
||||
template <typename Function, typename Handler, typename Arg1, typename Arg2,
|
||||
typename Arg3, typename Arg4, typename Arg5>
|
||||
inline void asio_handler_invoke(const Function& function,
|
||||
binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler)
|
||||
{
|
||||
asio_handler_invoke_helpers::invoke(
|
||||
function, &this_handler->handler_);
|
||||
}
|
||||
|
||||
template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
|
||||
typename Arg4, typename Arg5>
|
||||
inline binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5> bind_handler(
|
||||
const Handler& handler, const Arg1& arg1, const Arg2& arg2,
|
||||
const Arg3& arg3, const Arg4& arg4, const Arg5& arg5)
|
||||
{
|
||||
return binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>(handler, arg1, arg2,
|
||||
arg3, arg4, arg5);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_BIND_HANDLER_HPP
|
||||
70
include/libtorrent/asio/detail/buffer_resize_guard.hpp
Normal file
70
include/libtorrent/asio/detail/buffer_resize_guard.hpp
Normal file
@ -0,0 +1,70 @@
|
||||
//
|
||||
// buffer_resize_guard.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP
|
||||
#define ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <limits>
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
// Helper class to manage buffer resizing in an exception safe way.
|
||||
template <typename Buffer>
|
||||
class buffer_resize_guard
|
||||
{
|
||||
public:
|
||||
// Constructor.
|
||||
buffer_resize_guard(Buffer& buffer)
|
||||
: buffer_(buffer),
|
||||
old_size_(buffer.size())
|
||||
{
|
||||
}
|
||||
|
||||
// Destructor rolls back the buffer resize unless commit was called.
|
||||
~buffer_resize_guard()
|
||||
{
|
||||
if (old_size_
|
||||
!= std::numeric_limits<size_t>::max BOOST_PREVENT_MACRO_SUBSTITUTION())
|
||||
{
|
||||
buffer_.resize(old_size_);
|
||||
}
|
||||
}
|
||||
|
||||
// Commit the resize transaction.
|
||||
void commit()
|
||||
{
|
||||
old_size_
|
||||
= std::numeric_limits<size_t>::max BOOST_PREVENT_MACRO_SUBSTITUTION();
|
||||
}
|
||||
|
||||
private:
|
||||
// The buffer being managed.
|
||||
Buffer& buffer_;
|
||||
|
||||
// The size of the buffer at the time the guard was constructed.
|
||||
size_t old_size_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP
|
||||
127
include/libtorrent/asio/detail/buffered_stream_storage.hpp
Normal file
127
include/libtorrent/asio/detail/buffered_stream_storage.hpp
Normal file
@ -0,0 +1,127 @@
|
||||
//
|
||||
// buffered_stream_storage.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP
|
||||
#define ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
class buffered_stream_storage
|
||||
{
|
||||
public:
|
||||
// The type of the bytes stored in the buffer.
|
||||
typedef unsigned char byte_type;
|
||||
|
||||
// The type used for offsets into the buffer.
|
||||
typedef std::size_t size_type;
|
||||
|
||||
// Constructor.
|
||||
explicit buffered_stream_storage(std::size_t capacity)
|
||||
: begin_offset_(0),
|
||||
end_offset_(0),
|
||||
buffer_(capacity)
|
||||
{
|
||||
}
|
||||
|
||||
/// Clear the buffer.
|
||||
void clear()
|
||||
{
|
||||
begin_offset_ = 0;
|
||||
end_offset_ = 0;
|
||||
}
|
||||
|
||||
// Return a pointer to the beginning of the unread data.
|
||||
byte_type* data()
|
||||
{
|
||||
return &buffer_[0] + begin_offset_;
|
||||
}
|
||||
|
||||
// Return a pointer to the beginning of the unread data.
|
||||
const byte_type* data() const
|
||||
{
|
||||
return &buffer_[0] + begin_offset_;
|
||||
}
|
||||
|
||||
// Is there no unread data in the buffer.
|
||||
bool empty() const
|
||||
{
|
||||
return begin_offset_ == end_offset_;
|
||||
}
|
||||
|
||||
// Return the amount of unread data the is in the buffer.
|
||||
size_type size() const
|
||||
{
|
||||
return end_offset_ - begin_offset_;
|
||||
}
|
||||
|
||||
// Resize the buffer to the specified length.
|
||||
void resize(size_type length)
|
||||
{
|
||||
assert(length <= capacity());
|
||||
if (begin_offset_ + length <= capacity())
|
||||
{
|
||||
end_offset_ = begin_offset_ + length;
|
||||
}
|
||||
else
|
||||
{
|
||||
using namespace std; // For memmove.
|
||||
memmove(&buffer_[0], &buffer_[0] + begin_offset_, size());
|
||||
end_offset_ = length;
|
||||
begin_offset_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the maximum size for data in the buffer.
|
||||
size_type capacity() const
|
||||
{
|
||||
return buffer_.size();
|
||||
}
|
||||
|
||||
// Consume multiple bytes from the beginning of the buffer.
|
||||
void consume(size_type count)
|
||||
{
|
||||
assert(begin_offset_ + count <= end_offset_);
|
||||
begin_offset_ += count;
|
||||
if (empty())
|
||||
clear();
|
||||
}
|
||||
|
||||
private:
|
||||
// The offset to the beginning of the unread data.
|
||||
size_type begin_offset_;
|
||||
|
||||
// The offset to the end of the unread data.
|
||||
size_type end_offset_;
|
||||
|
||||
// The data in the buffer.
|
||||
std::vector<byte_type> buffer_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP
|
||||
90
include/libtorrent/asio/detail/call_stack.hpp
Normal file
90
include/libtorrent/asio/detail/call_stack.hpp
Normal file
@ -0,0 +1,90 @@
|
||||
//
|
||||
// call_stack.hpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_CALL_STACK_HPP
|
||||
#define ASIO_DETAIL_CALL_STACK_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
#include "asio/detail/tss_ptr.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
// Helper class to determine whether or not the current thread is inside an
|
||||
// invocation of io_service::run() for a specified io_service object.
|
||||
template <typename Owner>
|
||||
class call_stack
|
||||
{
|
||||
public:
|
||||
// Context class automatically pushes an owner on to the stack.
|
||||
class context
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
// Push the owner on to the stack.
|
||||
explicit context(Owner* d)
|
||||
: owner_(d),
|
||||
next_(call_stack<Owner>::top_)
|
||||
{
|
||||
call_stack<Owner>::top_ = this;
|
||||
}
|
||||
|
||||
// Pop the owner from the stack.
|
||||
~context()
|
||||
{
|
||||
call_stack<Owner>::top_ = next_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class call_stack<Owner>;
|
||||
|
||||
// The owner associated with the context.
|
||||
Owner* owner_;
|
||||
|
||||
// The next element in the stack.
|
||||
context* next_;
|
||||
};
|
||||
|
||||
friend class context;
|
||||
|
||||
// Determine whether the specified owner is on the stack.
|
||||
static bool contains(Owner* d)
|
||||
{
|
||||
context* elem = top_;
|
||||
while (elem)
|
||||
{
|
||||
if (elem->owner_ == d)
|
||||
return true;
|
||||
elem = elem->next_;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
// The top of the stack of calls for the current thread.
|
||||
static tss_ptr<context> top_;
|
||||
};
|
||||
|
||||
template <typename Owner>
|
||||
tss_ptr<typename call_stack<Owner>::context>
|
||||
call_stack<Owner>::top_;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_CALL_STACK_HPP
|
||||
150
include/libtorrent/asio/detail/const_buffers_iterator.hpp
Normal file
150
include/libtorrent/asio/detail/const_buffers_iterator.hpp
Normal file
@ -0,0 +1,150 @@
|
||||
//
|
||||
// const_buffers_iterator.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_CONST_BUFFERS_ITERATOR_HPP
|
||||
#define ASIO_DETAIL_CONST_BUFFERS_ITERATOR_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstddef>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/buffer.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
// A proxy iterator for a sub-range in a list of buffers.
|
||||
template <typename Const_Buffers>
|
||||
class const_buffers_iterator
|
||||
: public boost::iterator_facade<const_buffers_iterator<Const_Buffers>,
|
||||
const char, boost::bidirectional_traversal_tag>
|
||||
{
|
||||
public:
|
||||
// Default constructor creates an iterator in an undefined state.
|
||||
const_buffers_iterator()
|
||||
{
|
||||
}
|
||||
|
||||
// Create an iterator for the specified position.
|
||||
const_buffers_iterator(const Const_Buffers& buffers, std::size_t position)
|
||||
: begin_(buffers.begin()),
|
||||
current_(buffers.begin()),
|
||||
end_(buffers.end()),
|
||||
position_(0)
|
||||
{
|
||||
while (current_ != end_)
|
||||
{
|
||||
current_buffer_ = *current_;
|
||||
std::size_t buffer_size = asio::buffer_size(current_buffer_);
|
||||
if (position - position_ < buffer_size)
|
||||
{
|
||||
current_buffer_position_ = position - position_;
|
||||
position_ = position;
|
||||
return;
|
||||
}
|
||||
position_ += buffer_size;
|
||||
++current_;
|
||||
}
|
||||
current_buffer_ = asio::const_buffer();
|
||||
current_buffer_position_ = 0;
|
||||
}
|
||||
|
||||
std::size_t position() const
|
||||
{
|
||||
return position_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class boost::iterator_core_access;
|
||||
|
||||
void increment()
|
||||
{
|
||||
if (current_ == end_)
|
||||
return;
|
||||
|
||||
++position_;
|
||||
|
||||
++current_buffer_position_;
|
||||
if (current_buffer_position_ != asio::buffer_size(current_buffer_))
|
||||
return;
|
||||
|
||||
++current_;
|
||||
current_buffer_position_ = 0;
|
||||
while (current_ != end_)
|
||||
{
|
||||
current_buffer_ = *current_;
|
||||
if (asio::buffer_size(current_buffer_) > 0)
|
||||
return;
|
||||
++current_;
|
||||
}
|
||||
}
|
||||
|
||||
void decrement()
|
||||
{
|
||||
if (position_ == 0)
|
||||
return;
|
||||
|
||||
--position_;
|
||||
|
||||
if (current_buffer_position_ != 0)
|
||||
{
|
||||
--current_buffer_position_;
|
||||
return;
|
||||
}
|
||||
|
||||
typename Const_Buffers::const_iterator iter = current_;
|
||||
while (iter != begin_)
|
||||
{
|
||||
--iter;
|
||||
asio::const_buffer buffer = *iter;
|
||||
std::size_t buffer_size = asio::buffer_size(buffer);
|
||||
if (buffer_size > 0)
|
||||
{
|
||||
current_ = iter;
|
||||
current_buffer_ = buffer;
|
||||
current_buffer_position_ = buffer_size - 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool equal(const const_buffers_iterator& other) const
|
||||
{
|
||||
return position_ == other.position_;
|
||||
}
|
||||
|
||||
const char& dereference() const
|
||||
{
|
||||
return asio::buffer_cast<const char*>(
|
||||
current_buffer_)[current_buffer_position_];
|
||||
}
|
||||
|
||||
asio::const_buffer current_buffer_;
|
||||
std::size_t current_buffer_position_;
|
||||
typename Const_Buffers::const_iterator begin_;
|
||||
typename Const_Buffers::const_iterator current_;
|
||||
typename Const_Buffers::const_iterator end_;
|
||||
std::size_t position_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_CONST_BUFFERS_ITERATOR_HPP
|
||||
196
include/libtorrent/asio/detail/consuming_buffers.hpp
Normal file
196
include/libtorrent/asio/detail/consuming_buffers.hpp
Normal file
@ -0,0 +1,196 @@
|
||||
//
|
||||
// consuming_buffers.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_CONSUMING_BUFFERS_HPP
|
||||
#define ASIO_DETAIL_CONSUMING_BUFFERS_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
// A proxy iterator for a sub-range in a list of buffers.
|
||||
template <typename Buffer, typename Buffer_Iterator>
|
||||
class consuming_buffers_iterator
|
||||
: public boost::iterator_facade<
|
||||
consuming_buffers_iterator<Buffer, Buffer_Iterator>,
|
||||
const Buffer,
|
||||
boost::forward_traversal_tag>
|
||||
{
|
||||
public:
|
||||
// Default constructor creates an end iterator.
|
||||
consuming_buffers_iterator()
|
||||
: at_end_(true)
|
||||
{
|
||||
}
|
||||
|
||||
// Construct with a buffer for the first entry and an iterator
|
||||
// range for the remaining entries.
|
||||
consuming_buffers_iterator(bool at_end, const Buffer& first,
|
||||
Buffer_Iterator begin_remainder, Buffer_Iterator end_remainder)
|
||||
: at_end_(at_end),
|
||||
first_(first),
|
||||
begin_remainder_(begin_remainder),
|
||||
end_remainder_(end_remainder)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
friend class boost::iterator_core_access;
|
||||
|
||||
void increment()
|
||||
{
|
||||
if (!at_end_)
|
||||
{
|
||||
if (begin_remainder_ == end_remainder_)
|
||||
at_end_ = true;
|
||||
else
|
||||
first_ = *begin_remainder_++;
|
||||
}
|
||||
}
|
||||
|
||||
bool equal(const consuming_buffers_iterator& other) const
|
||||
{
|
||||
if (at_end_ && other.at_end_)
|
||||
return true;
|
||||
return !at_end_ && !other.at_end_
|
||||
&& buffer_cast<const void*>(first_)
|
||||
== buffer_cast<const void*>(other.first_)
|
||||
&& buffer_size(first_) == buffer_size(other.first_)
|
||||
&& begin_remainder_ == other.begin_remainder_
|
||||
&& end_remainder_ == other.end_remainder_;
|
||||
}
|
||||
|
||||
const Buffer& dereference() const
|
||||
{
|
||||
return first_;
|
||||
}
|
||||
|
||||
bool at_end_;
|
||||
Buffer first_;
|
||||
Buffer_Iterator begin_remainder_;
|
||||
Buffer_Iterator end_remainder_;
|
||||
};
|
||||
|
||||
// A proxy for a sub-range in a list of buffers.
|
||||
template <typename Buffer, typename Buffers>
|
||||
class consuming_buffers
|
||||
{
|
||||
public:
|
||||
// The type for each element in the list of buffers.
|
||||
typedef Buffer value_type;
|
||||
|
||||
// A forward-only iterator type that may be used to read elements.
|
||||
typedef consuming_buffers_iterator<Buffer, typename Buffers::const_iterator>
|
||||
const_iterator;
|
||||
|
||||
// Construct to represent the entire list of buffers.
|
||||
consuming_buffers(const Buffers& buffers)
|
||||
: buffers_(buffers),
|
||||
at_end_(buffers_.begin() == buffers_.end()),
|
||||
first_(*buffers_.begin()),
|
||||
begin_remainder_(buffers_.begin())
|
||||
{
|
||||
if (!at_end_)
|
||||
++begin_remainder_;
|
||||
}
|
||||
|
||||
// Copy constructor.
|
||||
consuming_buffers(const consuming_buffers& other)
|
||||
: buffers_(other.buffers_),
|
||||
at_end_(other.at_end_),
|
||||
first_(other.first_),
|
||||
begin_remainder_(buffers_.begin())
|
||||
{
|
||||
typename Buffers::const_iterator first = other.buffers_.begin();
|
||||
typename Buffers::const_iterator second = other.begin_remainder_;
|
||||
std::advance(begin_remainder_, std::distance(first, second));
|
||||
}
|
||||
|
||||
// Assignment operator.
|
||||
consuming_buffers& operator=(const consuming_buffers& other)
|
||||
{
|
||||
buffers_ = other.buffers_;
|
||||
at_end_ = other.at_end_;
|
||||
first_ = other.first_;
|
||||
begin_remainder_ = buffers_.begin();
|
||||
typename Buffers::const_iterator first = other.buffers_.begin();
|
||||
typename Buffers::const_iterator second = other.begin_remainder_;
|
||||
std::advance(begin_remainder_, std::distance(first, second));
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Get a forward-only iterator to the first element.
|
||||
const_iterator begin() const
|
||||
{
|
||||
return const_iterator(at_end_, first_, begin_remainder_, buffers_.end());
|
||||
}
|
||||
|
||||
// Get a forward-only iterator for one past the last element.
|
||||
const_iterator end() const
|
||||
{
|
||||
return const_iterator();
|
||||
}
|
||||
|
||||
// Consume the specified number of bytes from the buffers.
|
||||
void consume(std::size_t size)
|
||||
{
|
||||
// Remove buffers from the start until the specified size is reached.
|
||||
while (size > 0 && !at_end_)
|
||||
{
|
||||
if (buffer_size(first_) <= size)
|
||||
{
|
||||
size -= buffer_size(first_);
|
||||
if (begin_remainder_ == buffers_.end())
|
||||
at_end_ = true;
|
||||
else
|
||||
first_ = *begin_remainder_++;
|
||||
}
|
||||
else
|
||||
{
|
||||
first_ = first_ + size;
|
||||
size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any more empty buffers at the start.
|
||||
while (!at_end_ && buffer_size(first_) == 0)
|
||||
{
|
||||
if (begin_remainder_ == buffers_.end())
|
||||
at_end_ = true;
|
||||
else
|
||||
first_ = *begin_remainder_++;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Buffers buffers_;
|
||||
bool at_end_;
|
||||
Buffer first_;
|
||||
typename Buffers::const_iterator begin_remainder_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_CONSUMING_BUFFERS_HPP
|
||||
182
include/libtorrent/asio/detail/deadline_timer_service.hpp
Normal file
182
include/libtorrent/asio/detail/deadline_timer_service.hpp
Normal file
@ -0,0 +1,182 @@
|
||||
//
|
||||
// deadline_timer_service.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP
|
||||
#define ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstddef>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/error.hpp"
|
||||
#include "asio/io_service.hpp"
|
||||
#include "asio/detail/bind_handler.hpp"
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
#include "asio/detail/socket_ops.hpp"
|
||||
#include "asio/detail/socket_types.hpp"
|
||||
#include "asio/detail/timer_queue.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
template <typename Time_Traits, typename Timer_Scheduler>
|
||||
class deadline_timer_service
|
||||
: public asio::io_service::service
|
||||
{
|
||||
public:
|
||||
// The time type.
|
||||
typedef typename Time_Traits::time_type time_type;
|
||||
|
||||
// The duration type.
|
||||
typedef typename Time_Traits::duration_type duration_type;
|
||||
|
||||
// The implementation type of the timer. This type is dependent on the
|
||||
// underlying implementation of the timer service.
|
||||
struct implementation_type
|
||||
: private asio::detail::noncopyable
|
||||
{
|
||||
time_type expiry;
|
||||
bool might_have_pending_waits;
|
||||
};
|
||||
|
||||
// Constructor.
|
||||
deadline_timer_service(asio::io_service& io_service)
|
||||
: asio::io_service::service(io_service),
|
||||
scheduler_(asio::use_service<Timer_Scheduler>(io_service))
|
||||
{
|
||||
scheduler_.add_timer_queue(timer_queue_);
|
||||
}
|
||||
|
||||
// Destroy all user-defined handler objects owned by the service.
|
||||
void shutdown_service()
|
||||
{
|
||||
}
|
||||
|
||||
// Construct a new timer implementation.
|
||||
void construct(implementation_type& impl)
|
||||
{
|
||||
impl.expiry = time_type();
|
||||
impl.might_have_pending_waits = false;
|
||||
}
|
||||
|
||||
// Destroy a timer implementation.
|
||||
void destroy(implementation_type& impl)
|
||||
{
|
||||
cancel(impl);
|
||||
}
|
||||
|
||||
// Cancel any asynchronous wait operations associated with the timer.
|
||||
std::size_t cancel(implementation_type& impl)
|
||||
{
|
||||
if (!impl.might_have_pending_waits)
|
||||
return 0;
|
||||
std::size_t count = scheduler_.cancel_timer(timer_queue_, &impl);
|
||||
impl.might_have_pending_waits = false;
|
||||
return count;
|
||||
}
|
||||
|
||||
// Get the expiry time for the timer as an absolute time.
|
||||
time_type expires_at(const implementation_type& impl) const
|
||||
{
|
||||
return impl.expiry;
|
||||
}
|
||||
|
||||
// Set the expiry time for the timer as an absolute time.
|
||||
std::size_t expires_at(implementation_type& impl,
|
||||
const time_type& expiry_time)
|
||||
{
|
||||
std::size_t count = cancel(impl);
|
||||
impl.expiry = expiry_time;
|
||||
return count;
|
||||
}
|
||||
|
||||
// Get the expiry time for the timer relative to now.
|
||||
duration_type expires_from_now(const implementation_type& impl) const
|
||||
{
|
||||
return Time_Traits::subtract(expires_at(impl), Time_Traits::now());
|
||||
}
|
||||
|
||||
// Set the expiry time for the timer relative to now.
|
||||
std::size_t expires_from_now(implementation_type& impl,
|
||||
const duration_type& expiry_time)
|
||||
{
|
||||
return expires_at(impl, Time_Traits::add(Time_Traits::now(), expiry_time));
|
||||
}
|
||||
|
||||
// Perform a blocking wait on the timer.
|
||||
void wait(implementation_type& impl)
|
||||
{
|
||||
time_type now = Time_Traits::now();
|
||||
while (Time_Traits::less_than(now, impl.expiry))
|
||||
{
|
||||
boost::posix_time::time_duration timeout =
|
||||
Time_Traits::to_posix_duration(Time_Traits::subtract(impl.expiry, now));
|
||||
::timeval tv;
|
||||
tv.tv_sec = timeout.total_seconds();
|
||||
tv.tv_usec = timeout.total_microseconds() % 1000000;
|
||||
socket_ops::select(0, 0, 0, 0, &tv);
|
||||
now = Time_Traits::now();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
class wait_handler
|
||||
{
|
||||
public:
|
||||
wait_handler(asio::io_service& io_service, Handler handler)
|
||||
: io_service_(io_service),
|
||||
work_(io_service),
|
||||
handler_(handler)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(int result)
|
||||
{
|
||||
asio::error e(result);
|
||||
io_service_.post(detail::bind_handler(handler_, e));
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_service& io_service_;
|
||||
asio::io_service::work work_;
|
||||
Handler handler_;
|
||||
};
|
||||
|
||||
// Start an asynchronous wait on the timer.
|
||||
template <typename Handler>
|
||||
void async_wait(implementation_type& impl, Handler handler)
|
||||
{
|
||||
impl.might_have_pending_waits = true;
|
||||
scheduler_.schedule_timer(timer_queue_, impl.expiry,
|
||||
wait_handler<Handler>(io_service(), handler), &impl);
|
||||
}
|
||||
|
||||
private:
|
||||
// The queue of timers.
|
||||
timer_queue<Time_Traits> timer_queue_;
|
||||
|
||||
// The object that schedules and executes timers. Usually a reactor.
|
||||
Timer_Scheduler& scheduler_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP
|
||||
593
include/libtorrent/asio/detail/epoll_reactor.hpp
Normal file
593
include/libtorrent/asio/detail/epoll_reactor.hpp
Normal file
@ -0,0 +1,593 @@
|
||||
//
|
||||
// epoll_reactor.hpp
|
||||
// ~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_EPOLL_REACTOR_HPP
|
||||
#define ASIO_DETAIL_EPOLL_REACTOR_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/epoll_reactor_fwd.hpp"
|
||||
|
||||
#if defined(ASIO_HAS_EPOLL)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <sys/epoll.h>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/io_service.hpp"
|
||||
#include "asio/system_exception.hpp"
|
||||
#include "asio/detail/bind_handler.hpp"
|
||||
#include "asio/detail/hash_map.hpp"
|
||||
#include "asio/detail/mutex.hpp"
|
||||
#include "asio/detail/task_io_service.hpp"
|
||||
#include "asio/detail/thread.hpp"
|
||||
#include "asio/detail/reactor_op_queue.hpp"
|
||||
#include "asio/detail/select_interrupter.hpp"
|
||||
#include "asio/detail/signal_blocker.hpp"
|
||||
#include "asio/detail/socket_types.hpp"
|
||||
#include "asio/detail/timer_queue.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
template <bool Own_Thread>
|
||||
class epoll_reactor
|
||||
: public asio::io_service::service
|
||||
{
|
||||
public:
|
||||
// Constructor.
|
||||
epoll_reactor(asio::io_service& io_service)
|
||||
: asio::io_service::service(io_service),
|
||||
mutex_(),
|
||||
epoll_fd_(do_epoll_create()),
|
||||
wait_in_progress_(false),
|
||||
interrupter_(),
|
||||
read_op_queue_(),
|
||||
write_op_queue_(),
|
||||
except_op_queue_(),
|
||||
pending_cancellations_(),
|
||||
stop_thread_(false),
|
||||
thread_(0),
|
||||
shutdown_(false)
|
||||
{
|
||||
// Start the reactor's internal thread only if needed.
|
||||
if (Own_Thread)
|
||||
{
|
||||
asio::detail::signal_blocker sb;
|
||||
thread_ = new asio::detail::thread(
|
||||
bind_handler(&epoll_reactor::call_run_thread, this));
|
||||
}
|
||||
|
||||
// Add the interrupter's descriptor to epoll.
|
||||
epoll_event ev = { 0, { 0 } };
|
||||
ev.events = EPOLLIN | EPOLLERR;
|
||||
ev.data.fd = interrupter_.read_descriptor();
|
||||
epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev);
|
||||
}
|
||||
|
||||
// Destructor.
|
||||
~epoll_reactor()
|
||||
{
|
||||
shutdown_service();
|
||||
close(epoll_fd_);
|
||||
}
|
||||
|
||||
// Destroy all user-defined handler objects owned by the service.
|
||||
void shutdown_service()
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
shutdown_ = true;
|
||||
stop_thread_ = true;
|
||||
lock.unlock();
|
||||
|
||||
if (thread_)
|
||||
{
|
||||
interrupter_.interrupt();
|
||||
thread_->join();
|
||||
delete thread_;
|
||||
thread_ = 0;
|
||||
}
|
||||
|
||||
read_op_queue_.destroy_operations();
|
||||
write_op_queue_.destroy_operations();
|
||||
except_op_queue_.destroy_operations();
|
||||
|
||||
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
|
||||
timer_queues_[i]->destroy_timers();
|
||||
timer_queues_.clear();
|
||||
}
|
||||
|
||||
// Register a socket with the reactor. Returns 0 on success, system error
|
||||
// code on failure.
|
||||
int register_descriptor(socket_type descriptor)
|
||||
{
|
||||
// No need to lock according to epoll documentation.
|
||||
|
||||
epoll_event ev = { 0, { 0 } };
|
||||
ev.events = 0;
|
||||
ev.data.fd = descriptor;
|
||||
int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev);
|
||||
if (result != 0)
|
||||
return errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Start a new read operation. The handler object will be invoked when the
|
||||
// given descriptor is ready to be read, or an error has occurred.
|
||||
template <typename Handler>
|
||||
void start_read_op(socket_type descriptor, Handler handler)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
|
||||
if (shutdown_)
|
||||
return;
|
||||
|
||||
if (!read_op_queue_.has_operation(descriptor))
|
||||
if (handler(0))
|
||||
return;
|
||||
|
||||
if (read_op_queue_.enqueue_operation(descriptor, handler))
|
||||
{
|
||||
epoll_event ev = { 0, { 0 } };
|
||||
ev.events = EPOLLIN | EPOLLERR | EPOLLHUP;
|
||||
if (write_op_queue_.has_operation(descriptor))
|
||||
ev.events |= EPOLLOUT;
|
||||
if (except_op_queue_.has_operation(descriptor))
|
||||
ev.events |= EPOLLPRI;
|
||||
ev.data.fd = descriptor;
|
||||
|
||||
int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
|
||||
if (result != 0)
|
||||
{
|
||||
int error = errno;
|
||||
read_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start a new write operation. The handler object will be invoked when the
|
||||
// given descriptor is ready to be written, or an error has occurred.
|
||||
template <typename Handler>
|
||||
void start_write_op(socket_type descriptor, Handler handler)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
|
||||
if (shutdown_)
|
||||
return;
|
||||
|
||||
if (!write_op_queue_.has_operation(descriptor))
|
||||
if (handler(0))
|
||||
return;
|
||||
|
||||
if (write_op_queue_.enqueue_operation(descriptor, handler))
|
||||
{
|
||||
epoll_event ev = { 0, { 0 } };
|
||||
ev.events = EPOLLOUT | EPOLLERR | EPOLLHUP;
|
||||
if (read_op_queue_.has_operation(descriptor))
|
||||
ev.events |= EPOLLIN;
|
||||
if (except_op_queue_.has_operation(descriptor))
|
||||
ev.events |= EPOLLPRI;
|
||||
ev.data.fd = descriptor;
|
||||
|
||||
int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
|
||||
if (result != 0)
|
||||
{
|
||||
int error = errno;
|
||||
write_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start a new exception operation. The handler object will be invoked when
|
||||
// the given descriptor has exception information, or an error has occurred.
|
||||
template <typename Handler>
|
||||
void start_except_op(socket_type descriptor, Handler handler)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
|
||||
if (shutdown_)
|
||||
return;
|
||||
|
||||
if (except_op_queue_.enqueue_operation(descriptor, handler))
|
||||
{
|
||||
epoll_event ev = { 0, { 0 } };
|
||||
ev.events = EPOLLPRI | EPOLLERR | EPOLLHUP;
|
||||
if (read_op_queue_.has_operation(descriptor))
|
||||
ev.events |= EPOLLIN;
|
||||
if (write_op_queue_.has_operation(descriptor))
|
||||
ev.events |= EPOLLOUT;
|
||||
ev.data.fd = descriptor;
|
||||
|
||||
int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
|
||||
if (result != 0)
|
||||
{
|
||||
int error = errno;
|
||||
except_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start new write and exception operations. The handler object will be
|
||||
// invoked when the given descriptor is ready for writing or has exception
|
||||
// information available, or an error has occurred.
|
||||
template <typename Handler>
|
||||
void start_write_and_except_ops(socket_type descriptor, Handler handler)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
|
||||
if (shutdown_)
|
||||
return;
|
||||
|
||||
bool need_mod = write_op_queue_.enqueue_operation(descriptor, handler);
|
||||
need_mod = except_op_queue_.enqueue_operation(descriptor, handler)
|
||||
&& need_mod;
|
||||
if (need_mod)
|
||||
{
|
||||
epoll_event ev = { 0, { 0 } };
|
||||
ev.events = EPOLLOUT | EPOLLPRI | EPOLLERR | EPOLLHUP;
|
||||
if (read_op_queue_.has_operation(descriptor))
|
||||
ev.events |= EPOLLIN;
|
||||
ev.data.fd = descriptor;
|
||||
|
||||
int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
|
||||
if (result != 0)
|
||||
{
|
||||
int error = errno;
|
||||
write_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
except_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel all operations associated with the given descriptor. The
|
||||
// handlers associated with the descriptor will be invoked with the
|
||||
// operation_aborted error.
|
||||
void cancel_ops(socket_type descriptor)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
cancel_ops_unlocked(descriptor);
|
||||
}
|
||||
|
||||
// Enqueue cancellation of all operations associated with the given
|
||||
// descriptor. The handlers associated with the descriptor will be invoked
|
||||
// with the operation_aborted error. This function does not acquire the
|
||||
// epoll_reactor's mutex, and so should only be used from within a reactor
|
||||
// handler.
|
||||
void enqueue_cancel_ops_unlocked(socket_type descriptor)
|
||||
{
|
||||
pending_cancellations_.push_back(descriptor);
|
||||
}
|
||||
|
||||
// Cancel any operations that are running against the descriptor and remove
|
||||
// its registration from the reactor.
|
||||
void close_descriptor(socket_type descriptor)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
|
||||
// Remove the descriptor from epoll.
|
||||
epoll_event ev = { 0, { 0 } };
|
||||
epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, descriptor, &ev);
|
||||
|
||||
// Cancel any outstanding operations associated with the descriptor.
|
||||
cancel_ops_unlocked(descriptor);
|
||||
}
|
||||
|
||||
// Add a new timer queue to the reactor.
|
||||
template <typename Time_Traits>
|
||||
void add_timer_queue(timer_queue<Time_Traits>& timer_queue)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
timer_queues_.push_back(&timer_queue);
|
||||
}
|
||||
|
||||
// Schedule a timer in the given timer queue to expire at the specified
|
||||
// absolute time. The handler object will be invoked when the timer expires.
|
||||
template <typename Time_Traits, typename Handler>
|
||||
void schedule_timer(timer_queue<Time_Traits>& timer_queue,
|
||||
const typename Time_Traits::time_type& time, Handler handler, void* token)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
if (!shutdown_)
|
||||
if (timer_queue.enqueue_timer(time, handler, token))
|
||||
interrupter_.interrupt();
|
||||
}
|
||||
|
||||
// Cancel the timer associated with the given token. Returns the number of
|
||||
// handlers that have been posted or dispatched.
|
||||
template <typename Time_Traits>
|
||||
std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
return timer_queue.cancel_timer(token);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class task_io_service<epoll_reactor<Own_Thread> >;
|
||||
|
||||
// Run epoll once until interrupted or events are ready to be dispatched.
|
||||
void run(bool block)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
|
||||
// Dispatch any operation cancellations that were made while the select
|
||||
// loop was not running.
|
||||
read_op_queue_.dispatch_cancellations();
|
||||
write_op_queue_.dispatch_cancellations();
|
||||
except_op_queue_.dispatch_cancellations();
|
||||
|
||||
// Check if the thread is supposed to stop.
|
||||
if (stop_thread_)
|
||||
{
|
||||
// Clean up operations. We must not hold the lock since the operations may
|
||||
// make calls back into this reactor.
|
||||
lock.unlock();
|
||||
read_op_queue_.cleanup_operations();
|
||||
write_op_queue_.cleanup_operations();
|
||||
except_op_queue_.cleanup_operations();
|
||||
return;
|
||||
}
|
||||
|
||||
// We can return immediately if there's no work to do and the reactor is
|
||||
// not supposed to block.
|
||||
if (!block && read_op_queue_.empty() && write_op_queue_.empty()
|
||||
&& except_op_queue_.empty() && all_timer_queues_are_empty())
|
||||
{
|
||||
// Clean up operations. We must not hold the lock since the operations may
|
||||
// make calls back into this reactor.
|
||||
lock.unlock();
|
||||
read_op_queue_.cleanup_operations();
|
||||
write_op_queue_.cleanup_operations();
|
||||
except_op_queue_.cleanup_operations();
|
||||
return;
|
||||
}
|
||||
|
||||
int timeout = block ? get_timeout() : 0;
|
||||
wait_in_progress_ = true;
|
||||
lock.unlock();
|
||||
|
||||
// Block on the epoll descriptor.
|
||||
epoll_event events[128];
|
||||
int num_events = epoll_wait(epoll_fd_, events, 128, timeout);
|
||||
|
||||
lock.lock();
|
||||
wait_in_progress_ = false;
|
||||
|
||||
// Block signals while dispatching operations.
|
||||
asio::detail::signal_blocker sb;
|
||||
|
||||
// Dispatch the waiting events.
|
||||
for (int i = 0; i < num_events; ++i)
|
||||
{
|
||||
int descriptor = events[i].data.fd;
|
||||
if (descriptor == interrupter_.read_descriptor())
|
||||
{
|
||||
interrupter_.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (events[i].events & (EPOLLERR | EPOLLHUP))
|
||||
{
|
||||
except_op_queue_.dispatch_all_operations(descriptor, 0);
|
||||
read_op_queue_.dispatch_all_operations(descriptor, 0);
|
||||
write_op_queue_.dispatch_all_operations(descriptor, 0);
|
||||
|
||||
epoll_event ev = { 0, { 0 } };
|
||||
ev.events = 0;
|
||||
ev.data.fd = descriptor;
|
||||
epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool more_reads = false;
|
||||
bool more_writes = false;
|
||||
bool more_except = false;
|
||||
|
||||
// Exception operations must be processed first to ensure that any
|
||||
// out-of-band data is read before normal data.
|
||||
if (events[i].events & EPOLLPRI)
|
||||
more_except = except_op_queue_.dispatch_operation(descriptor, 0);
|
||||
else
|
||||
more_except = except_op_queue_.has_operation(descriptor);
|
||||
|
||||
if (events[i].events & EPOLLIN)
|
||||
more_reads = read_op_queue_.dispatch_operation(descriptor, 0);
|
||||
else
|
||||
more_reads = read_op_queue_.has_operation(descriptor);
|
||||
|
||||
if (events[i].events & EPOLLOUT)
|
||||
more_writes = write_op_queue_.dispatch_operation(descriptor, 0);
|
||||
else
|
||||
more_writes = write_op_queue_.has_operation(descriptor);
|
||||
|
||||
epoll_event ev = { 0, { 0 } };
|
||||
ev.events = EPOLLERR | EPOLLHUP;
|
||||
if (more_reads)
|
||||
ev.events |= EPOLLIN;
|
||||
if (more_writes)
|
||||
ev.events |= EPOLLOUT;
|
||||
if (more_except)
|
||||
ev.events |= EPOLLPRI;
|
||||
ev.data.fd = descriptor;
|
||||
int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
|
||||
if (result != 0)
|
||||
{
|
||||
int error = errno;
|
||||
read_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
write_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
except_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
read_op_queue_.dispatch_cancellations();
|
||||
write_op_queue_.dispatch_cancellations();
|
||||
except_op_queue_.dispatch_cancellations();
|
||||
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
|
||||
timer_queues_[i]->dispatch_timers();
|
||||
|
||||
// Issue any pending cancellations.
|
||||
for (size_t i = 0; i < pending_cancellations_.size(); ++i)
|
||||
cancel_ops_unlocked(pending_cancellations_[i]);
|
||||
pending_cancellations_.clear();
|
||||
|
||||
// Clean up operations. We must not hold the lock since the operations may
|
||||
// make calls back into this reactor.
|
||||
lock.unlock();
|
||||
read_op_queue_.cleanup_operations();
|
||||
write_op_queue_.cleanup_operations();
|
||||
except_op_queue_.cleanup_operations();
|
||||
}
|
||||
|
||||
// Run the select loop in the thread.
|
||||
void run_thread()
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
while (!stop_thread_)
|
||||
{
|
||||
lock.unlock();
|
||||
run(true);
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
// Entry point for the select loop thread.
|
||||
static void call_run_thread(epoll_reactor* reactor)
|
||||
{
|
||||
reactor->run_thread();
|
||||
}
|
||||
|
||||
// Interrupt the select loop.
|
||||
void interrupt()
|
||||
{
|
||||
interrupter_.interrupt();
|
||||
}
|
||||
|
||||
// The hint to pass to epoll_create to size its data structures.
|
||||
enum { epoll_size = 20000 };
|
||||
|
||||
// Create the epoll file descriptor. Throws an exception if the descriptor
|
||||
// cannot be created.
|
||||
static int do_epoll_create()
|
||||
{
|
||||
int fd = epoll_create(epoll_size);
|
||||
if (fd == -1)
|
||||
{
|
||||
system_exception e("epoll", errno);
|
||||
boost::throw_exception(e);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
// Check if all timer queues are empty.
|
||||
bool all_timer_queues_are_empty() const
|
||||
{
|
||||
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
|
||||
if (!timer_queues_[i]->empty())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the timeout value for the epoll_wait call. The timeout value is
|
||||
// returned as a number of milliseconds. A return value of -1 indicates
|
||||
// that epoll_wait should block indefinitely.
|
||||
int get_timeout()
|
||||
{
|
||||
if (all_timer_queues_are_empty())
|
||||
return -1;
|
||||
|
||||
// By default we will wait no longer than 5 minutes. This will ensure that
|
||||
// any changes to the system clock are detected after no longer than this.
|
||||
boost::posix_time::time_duration minimum_wait_duration
|
||||
= boost::posix_time::minutes(5);
|
||||
|
||||
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
|
||||
{
|
||||
boost::posix_time::time_duration wait_duration
|
||||
= timer_queues_[i]->wait_duration();
|
||||
if (wait_duration < minimum_wait_duration)
|
||||
minimum_wait_duration = wait_duration;
|
||||
}
|
||||
|
||||
if (minimum_wait_duration > boost::posix_time::time_duration())
|
||||
{
|
||||
return minimum_wait_duration.total_milliseconds();
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel all operations associated with the given descriptor. The do_cancel
|
||||
// function of the handler objects will be invoked. This function does not
|
||||
// acquire the epoll_reactor's mutex.
|
||||
void cancel_ops_unlocked(socket_type descriptor)
|
||||
{
|
||||
bool interrupt = read_op_queue_.cancel_operations(descriptor);
|
||||
interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt;
|
||||
interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt;
|
||||
if (interrupt)
|
||||
interrupter_.interrupt();
|
||||
}
|
||||
|
||||
// Mutex to protect access to internal data.
|
||||
asio::detail::mutex mutex_;
|
||||
|
||||
// The epoll file descriptor.
|
||||
int epoll_fd_;
|
||||
|
||||
// Whether the epoll_wait call is currently in progress
|
||||
bool wait_in_progress_;
|
||||
|
||||
// The interrupter is used to break a blocking epoll_wait call.
|
||||
select_interrupter interrupter_;
|
||||
|
||||
// The queue of read operations.
|
||||
reactor_op_queue<socket_type> read_op_queue_;
|
||||
|
||||
// The queue of write operations.
|
||||
reactor_op_queue<socket_type> write_op_queue_;
|
||||
|
||||
// The queue of except operations.
|
||||
reactor_op_queue<socket_type> except_op_queue_;
|
||||
|
||||
// The timer queues.
|
||||
std::vector<timer_queue_base*> timer_queues_;
|
||||
|
||||
// The descriptors that are pending cancellation.
|
||||
std::vector<socket_type> pending_cancellations_;
|
||||
|
||||
// Does the reactor loop thread need to stop.
|
||||
bool stop_thread_;
|
||||
|
||||
// The thread that is running the reactor loop.
|
||||
asio::detail::thread* thread_;
|
||||
|
||||
// Whether the service has been shut down.
|
||||
bool shutdown_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // defined(ASIO_HAS_EPOLL)
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_EPOLL_REACTOR_HPP
|
||||
47
include/libtorrent/asio/detail/epoll_reactor_fwd.hpp
Normal file
47
include/libtorrent/asio/detail/epoll_reactor_fwd.hpp
Normal file
@ -0,0 +1,47 @@
|
||||
//
|
||||
// epoll_reactor_fwd.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP
|
||||
#define ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#if !defined(ASIO_DISABLE_EPOLL)
|
||||
#if defined(__linux__) // This service is only supported on Linux.
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <linux/version.h>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45) // Only kernels >= 2.5.45.
|
||||
|
||||
// Define this to indicate that epoll is supported on the target platform.
|
||||
#define ASIO_HAS_EPOLL 1
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
template <bool Own_Thread>
|
||||
class epoll_reactor;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45)
|
||||
#endif // defined(__linux__)
|
||||
#endif // !defined(ASIO_DISABLE_EPOLL)
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP
|
||||
50
include/libtorrent/asio/detail/event.hpp
Normal file
50
include/libtorrent/asio/detail/event.hpp
Normal file
@ -0,0 +1,50 @@
|
||||
//
|
||||
// event.hpp
|
||||
// ~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_EVENT_HPP
|
||||
#define ASIO_DETAIL_EVENT_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#if !defined(BOOST_HAS_THREADS)
|
||||
# include "asio/detail/null_event.hpp"
|
||||
#elif defined(BOOST_WINDOWS)
|
||||
# include "asio/detail/win_event.hpp"
|
||||
#elif defined(BOOST_HAS_PTHREADS)
|
||||
# include "asio/detail/posix_event.hpp"
|
||||
#else
|
||||
# error Only Windows and POSIX are supported!
|
||||
#endif
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
#if !defined(BOOST_HAS_THREADS)
|
||||
typedef null_event event;
|
||||
#elif defined(BOOST_WINDOWS)
|
||||
typedef win_event event;
|
||||
#elif defined(BOOST_HAS_PTHREADS)
|
||||
typedef posix_event event;
|
||||
#endif
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_EVENT_HPP
|
||||
41
include/libtorrent/asio/detail/fd_set_adapter.hpp
Normal file
41
include/libtorrent/asio/detail/fd_set_adapter.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// fd_set_adapter.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_FD_SET_ADAPTER_HPP
|
||||
#define ASIO_DETAIL_FD_SET_ADAPTER_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/detail/posix_fd_set_adapter.hpp"
|
||||
#include "asio/detail/win_fd_set_adapter.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
typedef win_fd_set_adapter fd_set_adapter;
|
||||
#else
|
||||
typedef posix_fd_set_adapter fd_set_adapter;
|
||||
#endif
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_FD_SET_ADAPTER_HPP
|
||||
256
include/libtorrent/asio/detail/handler_alloc_helpers.hpp
Normal file
256
include/libtorrent/asio/detail/handler_alloc_helpers.hpp
Normal file
@ -0,0 +1,256 @@
|
||||
//
|
||||
// handler_alloc_helpers.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP
|
||||
#define ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/detail/workaround.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/handler_alloc_hook.hpp"
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
|
||||
// Calls to asio_handler_allocate and asio_handler_deallocate must be made from
|
||||
// a namespace that does not contain any overloads of these functions. The
|
||||
// asio_handler_alloc_helpers namespace is defined here for that purpose.
|
||||
namespace asio_handler_alloc_helpers {
|
||||
|
||||
template <typename Handler>
|
||||
inline void* allocate(std::size_t s, Handler* h)
|
||||
{
|
||||
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
|
||||
return ::operator new(s);
|
||||
#else
|
||||
using namespace asio;
|
||||
return asio_handler_allocate(s, h);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
inline void deallocate(void* p, std::size_t s, Handler* h)
|
||||
{
|
||||
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
|
||||
::operator delete(p);
|
||||
#else
|
||||
using namespace asio;
|
||||
asio_handler_deallocate(p, s, h);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace asio_handler_alloc_helpers
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
// Traits for handler allocation.
|
||||
template <typename Handler, typename Object>
|
||||
struct handler_alloc_traits
|
||||
{
|
||||
typedef Handler handler_type;
|
||||
typedef Object value_type;
|
||||
typedef Object* pointer_type;
|
||||
BOOST_STATIC_CONSTANT(std::size_t, value_size = sizeof(Object));
|
||||
};
|
||||
|
||||
template <typename Alloc_Traits>
|
||||
class handler_ptr;
|
||||
|
||||
// Helper class to provide RAII on uninitialised handler memory.
|
||||
template <typename Alloc_Traits>
|
||||
class raw_handler_ptr
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
typedef typename Alloc_Traits::handler_type handler_type;
|
||||
typedef typename Alloc_Traits::value_type value_type;
|
||||
typedef typename Alloc_Traits::pointer_type pointer_type;
|
||||
BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size);
|
||||
|
||||
// Constructor allocates the memory.
|
||||
raw_handler_ptr(handler_type& handler)
|
||||
: handler_(handler),
|
||||
pointer_(static_cast<pointer_type>(
|
||||
asio_handler_alloc_helpers::allocate(value_size, &handler_)))
|
||||
{
|
||||
}
|
||||
|
||||
// Destructor automatically deallocates memory, unless it has been stolen by
|
||||
// a handler_ptr object.
|
||||
~raw_handler_ptr()
|
||||
{
|
||||
if (pointer_)
|
||||
asio_handler_alloc_helpers::deallocate(
|
||||
pointer_, value_size, &handler_);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class handler_ptr<Alloc_Traits>;
|
||||
handler_type& handler_;
|
||||
pointer_type pointer_;
|
||||
};
|
||||
|
||||
// Helper class to provide RAII on uninitialised handler memory.
|
||||
template <typename Alloc_Traits>
|
||||
class handler_ptr
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
typedef typename Alloc_Traits::handler_type handler_type;
|
||||
typedef typename Alloc_Traits::value_type value_type;
|
||||
typedef typename Alloc_Traits::pointer_type pointer_type;
|
||||
BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size);
|
||||
typedef raw_handler_ptr<Alloc_Traits> raw_ptr_type;
|
||||
|
||||
// Take ownership of existing memory.
|
||||
handler_ptr(handler_type& handler, pointer_type pointer)
|
||||
: handler_(handler),
|
||||
pointer_(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
// Construct object in raw memory and take ownership if construction succeeds.
|
||||
handler_ptr(raw_ptr_type& raw_ptr)
|
||||
: handler_(raw_ptr.handler_),
|
||||
pointer_(new (raw_ptr.pointer_) value_type)
|
||||
{
|
||||
raw_ptr.pointer_ = 0;
|
||||
}
|
||||
|
||||
// Construct object in raw memory and take ownership if construction succeeds.
|
||||
template <typename Arg1>
|
||||
handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1)
|
||||
: handler_(raw_ptr.handler_),
|
||||
pointer_(new (raw_ptr.pointer_) value_type(a1))
|
||||
{
|
||||
raw_ptr.pointer_ = 0;
|
||||
}
|
||||
|
||||
// Construct object in raw memory and take ownership if construction succeeds.
|
||||
template <typename Arg1, typename Arg2>
|
||||
handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2)
|
||||
: handler_(raw_ptr.handler_),
|
||||
pointer_(new (raw_ptr.pointer_) value_type(a1, a2))
|
||||
{
|
||||
raw_ptr.pointer_ = 0;
|
||||
}
|
||||
|
||||
// Construct object in raw memory and take ownership if construction succeeds.
|
||||
template <typename Arg1, typename Arg2, typename Arg3>
|
||||
handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3)
|
||||
: handler_(raw_ptr.handler_),
|
||||
pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3))
|
||||
{
|
||||
raw_ptr.pointer_ = 0;
|
||||
}
|
||||
|
||||
// Construct object in raw memory and take ownership if construction succeeds.
|
||||
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
|
||||
handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4)
|
||||
: handler_(raw_ptr.handler_),
|
||||
pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4))
|
||||
{
|
||||
raw_ptr.pointer_ = 0;
|
||||
}
|
||||
|
||||
// Construct object in raw memory and take ownership if construction succeeds.
|
||||
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
|
||||
typename Arg5>
|
||||
handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
|
||||
Arg5& a5)
|
||||
: handler_(raw_ptr.handler_),
|
||||
pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5))
|
||||
{
|
||||
raw_ptr.pointer_ = 0;
|
||||
}
|
||||
|
||||
// Construct object in raw memory and take ownership if construction succeeds.
|
||||
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
|
||||
typename Arg5, typename Arg6>
|
||||
handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
|
||||
Arg5& a5, Arg6& a6)
|
||||
: handler_(raw_ptr.handler_),
|
||||
pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6))
|
||||
{
|
||||
raw_ptr.pointer_ = 0;
|
||||
}
|
||||
|
||||
// Construct object in raw memory and take ownership if construction succeeds.
|
||||
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
|
||||
typename Arg5, typename Arg6, typename Arg7>
|
||||
handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
|
||||
Arg5& a5, Arg6& a6, Arg7& a7)
|
||||
: handler_(raw_ptr.handler_),
|
||||
pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6, a7))
|
||||
{
|
||||
raw_ptr.pointer_ = 0;
|
||||
}
|
||||
|
||||
// Construct object in raw memory and take ownership if construction succeeds.
|
||||
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
|
||||
typename Arg5, typename Arg6, typename Arg7, typename Arg8>
|
||||
handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
|
||||
Arg5& a5, Arg6& a6, Arg7& a7, Arg8& a8)
|
||||
: handler_(raw_ptr.handler_),
|
||||
pointer_(new (raw_ptr.pointer_) value_type(
|
||||
a1, a2, a3, a4, a5, a6, a7, a8))
|
||||
{
|
||||
raw_ptr.pointer_ = 0;
|
||||
}
|
||||
|
||||
// Destructor automatically deallocates memory, unless it has been released.
|
||||
~handler_ptr()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
// Get the memory.
|
||||
pointer_type get() const
|
||||
{
|
||||
return pointer_;
|
||||
}
|
||||
|
||||
// Release ownership of the memory.
|
||||
pointer_type release()
|
||||
{
|
||||
pointer_type tmp = pointer_;
|
||||
pointer_ = 0;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
// Explicitly destroy and deallocate the memory.
|
||||
void reset()
|
||||
{
|
||||
if (pointer_)
|
||||
{
|
||||
pointer_->value_type::~value_type();
|
||||
asio_handler_alloc_helpers::deallocate(
|
||||
pointer_, value_size, &handler_);
|
||||
pointer_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
handler_type& handler_;
|
||||
pointer_type pointer_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP
|
||||
47
include/libtorrent/asio/detail/handler_invoke_helpers.hpp
Normal file
47
include/libtorrent/asio/detail/handler_invoke_helpers.hpp
Normal file
@ -0,0 +1,47 @@
|
||||
//
|
||||
// handler_invoke_helpers.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP
|
||||
#define ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/detail/workaround.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/handler_invoke_hook.hpp"
|
||||
|
||||
// Calls to asio_handler_invoke must be made from a namespace that does not
|
||||
// contain overloads of this function. The asio_handler_invoke_helpers
|
||||
// namespace is defined here for that purpose.
|
||||
namespace asio_handler_invoke_helpers {
|
||||
|
||||
template <typename Function, typename Context>
|
||||
inline void invoke(const Function& function, Context* context)
|
||||
{
|
||||
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
|
||||
Function tmp(function);
|
||||
tmp();
|
||||
#else
|
||||
using namespace asio;
|
||||
asio_handler_invoke(function, context);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace asio_handler_invoke_helpers
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP
|
||||
197
include/libtorrent/asio/detail/hash_map.hpp
Normal file
197
include/libtorrent/asio/detail/hash_map.hpp
Normal file
@ -0,0 +1,197 @@
|
||||
//
|
||||
// hash_map.hpp
|
||||
// ~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_HASH_MAP_HPP
|
||||
#define ASIO_DETAIL_HASH_MAP_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cassert>
|
||||
#include <list>
|
||||
#include <utility>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
using boost::hash_value;
|
||||
|
||||
template <typename K, typename V>
|
||||
class hash_map
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
// The type of a value in the map.
|
||||
typedef std::pair<const K, V> value_type;
|
||||
|
||||
// The type of a non-const iterator over the hash map.
|
||||
typedef typename std::list<value_type>::iterator iterator;
|
||||
|
||||
// The type of a const iterator over the hash map.
|
||||
typedef typename std::list<value_type>::const_iterator const_iterator;
|
||||
|
||||
// Constructor.
|
||||
hash_map()
|
||||
{
|
||||
// Initialise all buckets to empty.
|
||||
for (size_t i = 0; i < num_buckets; ++i)
|
||||
buckets_[i].first = buckets_[i].last = values_.end();
|
||||
}
|
||||
|
||||
// Get an iterator for the beginning of the map.
|
||||
iterator begin()
|
||||
{
|
||||
return values_.begin();
|
||||
}
|
||||
|
||||
// Get an iterator for the beginning of the map.
|
||||
const_iterator begin() const
|
||||
{
|
||||
return values_.begin();
|
||||
}
|
||||
|
||||
// Get an iterator for the end of the map.
|
||||
iterator end()
|
||||
{
|
||||
return values_.end();
|
||||
}
|
||||
|
||||
// Get an iterator for the end of the map.
|
||||
const_iterator end() const
|
||||
{
|
||||
return values_.end();
|
||||
}
|
||||
|
||||
// Check whether the map is empty.
|
||||
bool empty() const
|
||||
{
|
||||
return values_.empty();
|
||||
}
|
||||
|
||||
// Find an entry in the map.
|
||||
iterator find(const K& k)
|
||||
{
|
||||
size_t bucket = hash_value(k) % num_buckets;
|
||||
iterator it = buckets_[bucket].first;
|
||||
if (it == values_.end())
|
||||
return values_.end();
|
||||
iterator end = buckets_[bucket].last;
|
||||
++end;
|
||||
while (it != end)
|
||||
{
|
||||
if (it->first == k)
|
||||
return it;
|
||||
++it;
|
||||
}
|
||||
return values_.end();
|
||||
}
|
||||
|
||||
// Find an entry in the map.
|
||||
const_iterator find(const K& k) const
|
||||
{
|
||||
size_t bucket = hash_value(k) % num_buckets;
|
||||
const_iterator it = buckets_[bucket].first;
|
||||
if (it == values_.end())
|
||||
return it;
|
||||
const_iterator end = buckets_[bucket].last;
|
||||
++end;
|
||||
while (it != end)
|
||||
{
|
||||
if (it->first == k)
|
||||
return it;
|
||||
++it;
|
||||
}
|
||||
return values_.end();
|
||||
}
|
||||
|
||||
// Insert a new entry into the map.
|
||||
std::pair<iterator, bool> insert(const value_type& v)
|
||||
{
|
||||
size_t bucket = hash_value(v.first) % num_buckets;
|
||||
iterator it = buckets_[bucket].first;
|
||||
if (it == values_.end())
|
||||
{
|
||||
buckets_[bucket].first = buckets_[bucket].last =
|
||||
values_.insert(values_.end(), v);
|
||||
return std::pair<iterator, bool>(buckets_[bucket].last, true);
|
||||
}
|
||||
iterator end = buckets_[bucket].last;
|
||||
++end;
|
||||
while (it != end)
|
||||
{
|
||||
if (it->first == v.first)
|
||||
return std::pair<iterator, bool>(it, false);
|
||||
++it;
|
||||
}
|
||||
buckets_[bucket].last = values_.insert(end, v);
|
||||
return std::pair<iterator, bool>(buckets_[bucket].last, true);
|
||||
}
|
||||
|
||||
// Erase an entry from the map.
|
||||
void erase(iterator it)
|
||||
{
|
||||
assert(it != values_.end());
|
||||
|
||||
size_t bucket = hash_value(it->first) % num_buckets;
|
||||
bool is_first = (it == buckets_[bucket].first);
|
||||
bool is_last = (it == buckets_[bucket].last);
|
||||
if (is_first && is_last)
|
||||
buckets_[bucket].first = buckets_[bucket].last = values_.end();
|
||||
else if (is_first)
|
||||
++buckets_[bucket].first;
|
||||
else if (is_last)
|
||||
--buckets_[bucket].last;
|
||||
|
||||
values_.erase(it);
|
||||
}
|
||||
|
||||
// Remove all entries from the map.
|
||||
void clear()
|
||||
{
|
||||
// Clear the values.
|
||||
values_.clear();
|
||||
|
||||
// Initialise all buckets to empty.
|
||||
for (size_t i = 0; i < num_buckets; ++i)
|
||||
buckets_[i].first = buckets_[i].last = values_.end();
|
||||
}
|
||||
|
||||
private:
|
||||
// The list of all values in the hash map.
|
||||
std::list<value_type> values_;
|
||||
|
||||
// The type for a bucket in the hash table.
|
||||
struct bucket_type
|
||||
{
|
||||
iterator first;
|
||||
iterator last;
|
||||
};
|
||||
|
||||
// The number of buckets in the hash.
|
||||
enum { num_buckets = 1021 };
|
||||
|
||||
// The buckets in the hash.
|
||||
bucket_type buckets_[num_buckets];
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_HASH_MAP_HPP
|
||||
137
include/libtorrent/asio/detail/io_control.hpp
Normal file
137
include/libtorrent/asio/detail/io_control.hpp
Normal file
@ -0,0 +1,137 @@
|
||||
//
|
||||
// io_control.hpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_IO_CONTROL_HPP
|
||||
#define ASIO_DETAIL_IO_CONTROL_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstddef>
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/detail/socket_types.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
namespace io_control {
|
||||
|
||||
// IO control command for non-blocking I/O.
|
||||
class non_blocking_io
|
||||
{
|
||||
public:
|
||||
// Default constructor.
|
||||
non_blocking_io()
|
||||
: value_(0)
|
||||
{
|
||||
}
|
||||
|
||||
// Construct with a specific command value.
|
||||
non_blocking_io(bool value)
|
||||
: value_(value ? 1 : 0)
|
||||
{
|
||||
}
|
||||
|
||||
// Get the name of the IO control command.
|
||||
int name() const
|
||||
{
|
||||
return FIONBIO;
|
||||
}
|
||||
|
||||
// Set the value of the I/O control command.
|
||||
void set(bool value)
|
||||
{
|
||||
value_ = value ? 1 : 0;
|
||||
}
|
||||
|
||||
// Get the current value of the I/O control command.
|
||||
bool get() const
|
||||
{
|
||||
return value_ != 0;
|
||||
}
|
||||
|
||||
// Get the address of the command data.
|
||||
detail::ioctl_arg_type* data()
|
||||
{
|
||||
return &value_;
|
||||
}
|
||||
|
||||
// Get the address of the command data.
|
||||
const detail::ioctl_arg_type* data() const
|
||||
{
|
||||
return &value_;
|
||||
}
|
||||
|
||||
private:
|
||||
detail::ioctl_arg_type value_;
|
||||
};
|
||||
|
||||
// I/O control command for getting number of bytes available.
|
||||
class bytes_readable
|
||||
{
|
||||
public:
|
||||
// Default constructor.
|
||||
bytes_readable()
|
||||
: value_(0)
|
||||
{
|
||||
}
|
||||
|
||||
// Construct with a specific command value.
|
||||
bytes_readable(std::size_t value)
|
||||
: value_(value)
|
||||
{
|
||||
}
|
||||
|
||||
// Get the name of the IO control command.
|
||||
int name() const
|
||||
{
|
||||
return FIONREAD;
|
||||
}
|
||||
|
||||
// Set the value of the I/O control command.
|
||||
void set(std::size_t value)
|
||||
{
|
||||
value_ = static_cast<detail::ioctl_arg_type>(value);
|
||||
}
|
||||
|
||||
// Get the current value of the I/O control command.
|
||||
std::size_t get() const
|
||||
{
|
||||
return static_cast<std::size_t>(value_);
|
||||
}
|
||||
|
||||
// Get the address of the command data.
|
||||
detail::ioctl_arg_type* data()
|
||||
{
|
||||
return &value_;
|
||||
}
|
||||
|
||||
// Get the address of the command data.
|
||||
const detail::ioctl_arg_type* data() const
|
||||
{
|
||||
return &value_;
|
||||
}
|
||||
|
||||
private:
|
||||
detail::ioctl_arg_type value_;
|
||||
};
|
||||
|
||||
} // namespace io_control
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_IO_CONTROL_HPP
|
||||
596
include/libtorrent/asio/detail/kqueue_reactor.hpp
Normal file
596
include/libtorrent/asio/detail/kqueue_reactor.hpp
Normal file
@ -0,0 +1,596 @@
|
||||
//
|
||||
// kqueue_reactor.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_KQUEUE_REACTOR_HPP
|
||||
#define ASIO_DETAIL_KQUEUE_REACTOR_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/kqueue_reactor_fwd.hpp"
|
||||
|
||||
#if defined(ASIO_HAS_KQUEUE)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <sys/types.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/time.h>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/io_service.hpp"
|
||||
#include "asio/system_exception.hpp"
|
||||
#include "asio/detail/bind_handler.hpp"
|
||||
#include "asio/detail/mutex.hpp"
|
||||
#include "asio/detail/task_io_service.hpp"
|
||||
#include "asio/detail/thread.hpp"
|
||||
#include "asio/detail/reactor_op_queue.hpp"
|
||||
#include "asio/detail/select_interrupter.hpp"
|
||||
#include "asio/detail/signal_blocker.hpp"
|
||||
#include "asio/detail/socket_types.hpp"
|
||||
#include "asio/detail/timer_queue.hpp"
|
||||
|
||||
// Older versions of Mac OS X may not define EV_OOBAND.
|
||||
#if !defined(EV_OOBAND)
|
||||
# define EV_OOBAND EV_FLAG1
|
||||
#endif // !defined(EV_OOBAND)
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
template <bool Own_Thread>
|
||||
class kqueue_reactor
|
||||
: public asio::io_service::service
|
||||
{
|
||||
public:
|
||||
// Constructor.
|
||||
kqueue_reactor(asio::io_service& io_service)
|
||||
: asio::io_service::service(io_service),
|
||||
mutex_(),
|
||||
kqueue_fd_(do_kqueue_create()),
|
||||
wait_in_progress_(false),
|
||||
interrupter_(),
|
||||
read_op_queue_(),
|
||||
write_op_queue_(),
|
||||
except_op_queue_(),
|
||||
pending_cancellations_(),
|
||||
stop_thread_(false),
|
||||
thread_(0),
|
||||
shutdown_(false)
|
||||
{
|
||||
// Start the reactor's internal thread only if needed.
|
||||
if (Own_Thread)
|
||||
{
|
||||
asio::detail::signal_blocker sb;
|
||||
thread_ = new asio::detail::thread(
|
||||
bind_handler(&kqueue_reactor::call_run_thread, this));
|
||||
}
|
||||
|
||||
// Add the interrupter's descriptor to the kqueue.
|
||||
struct kevent event;
|
||||
EV_SET(&event, interrupter_.read_descriptor(),
|
||||
EVFILT_READ, EV_ADD, 0, 0, 0);
|
||||
::kevent(kqueue_fd_, &event, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Destructor.
|
||||
~kqueue_reactor()
|
||||
{
|
||||
shutdown_service();
|
||||
close(kqueue_fd_);
|
||||
}
|
||||
|
||||
// Destroy all user-defined handler objects owned by the service.
|
||||
void shutdown_service()
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
shutdown_ = true;
|
||||
stop_thread_ = true;
|
||||
lock.unlock();
|
||||
|
||||
if (thread_)
|
||||
{
|
||||
interrupter_.interrupt();
|
||||
thread_->join();
|
||||
delete thread_;
|
||||
thread_ = 0;
|
||||
}
|
||||
|
||||
read_op_queue_.destroy_operations();
|
||||
write_op_queue_.destroy_operations();
|
||||
except_op_queue_.destroy_operations();
|
||||
|
||||
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
|
||||
timer_queues_[i]->destroy_timers();
|
||||
timer_queues_.clear();
|
||||
}
|
||||
|
||||
// Register a socket with the reactor. Returns 0 on success, system error
|
||||
// code on failure.
|
||||
int register_descriptor(socket_type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Start a new read operation. The handler object will be invoked when the
|
||||
// given descriptor is ready to be read, or an error has occurred.
|
||||
template <typename Handler>
|
||||
void start_read_op(socket_type descriptor, Handler handler)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
|
||||
if (shutdown_)
|
||||
return;
|
||||
|
||||
if (!read_op_queue_.has_operation(descriptor))
|
||||
if (handler(0))
|
||||
return;
|
||||
|
||||
if (read_op_queue_.enqueue_operation(descriptor, handler))
|
||||
{
|
||||
struct kevent event;
|
||||
EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0);
|
||||
if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
|
||||
{
|
||||
int error = errno;
|
||||
read_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start a new write operation. The handler object will be invoked when the
|
||||
// given descriptor is ready to be written, or an error has occurred.
|
||||
template <typename Handler>
|
||||
void start_write_op(socket_type descriptor, Handler handler)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
|
||||
if (shutdown_)
|
||||
return;
|
||||
|
||||
if (!write_op_queue_.has_operation(descriptor))
|
||||
if (handler(0))
|
||||
return;
|
||||
|
||||
if (write_op_queue_.enqueue_operation(descriptor, handler))
|
||||
{
|
||||
struct kevent event;
|
||||
EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0);
|
||||
if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
|
||||
{
|
||||
int error = errno;
|
||||
write_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start a new exception operation. The handler object will be invoked when
|
||||
// the given descriptor has exception information, or an error has occurred.
|
||||
template <typename Handler>
|
||||
void start_except_op(socket_type descriptor, Handler handler)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
|
||||
if (shutdown_)
|
||||
return;
|
||||
|
||||
if (except_op_queue_.enqueue_operation(descriptor, handler))
|
||||
{
|
||||
struct kevent event;
|
||||
if (read_op_queue_.has_operation(descriptor))
|
||||
EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0);
|
||||
else
|
||||
EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0);
|
||||
if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
|
||||
{
|
||||
int error = errno;
|
||||
except_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start new write and exception operations. The handler object will be
|
||||
// invoked when the given descriptor is ready for writing or has exception
|
||||
// information available, or an error has occurred.
|
||||
template <typename Handler>
|
||||
void start_write_and_except_ops(socket_type descriptor, Handler handler)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
|
||||
if (shutdown_)
|
||||
return;
|
||||
|
||||
if (write_op_queue_.enqueue_operation(descriptor, handler))
|
||||
{
|
||||
struct kevent event;
|
||||
EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0);
|
||||
if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
|
||||
{
|
||||
int error = errno;
|
||||
write_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
}
|
||||
}
|
||||
|
||||
if (except_op_queue_.enqueue_operation(descriptor, handler))
|
||||
{
|
||||
struct kevent event;
|
||||
if (read_op_queue_.has_operation(descriptor))
|
||||
EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0);
|
||||
else
|
||||
EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0);
|
||||
if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
|
||||
{
|
||||
int error = errno;
|
||||
except_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
write_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel all operations associated with the given descriptor. The
|
||||
// handlers associated with the descriptor will be invoked with the
|
||||
// operation_aborted error.
|
||||
void cancel_ops(socket_type descriptor)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
cancel_ops_unlocked(descriptor);
|
||||
}
|
||||
|
||||
// Enqueue cancellation of all operations associated with the given
|
||||
// descriptor. The handlers associated with the descriptor will be invoked
|
||||
// with the operation_aborted error. This function does not acquire the
|
||||
// kqueue_reactor's mutex, and so should only be used from within a reactor
|
||||
// handler.
|
||||
void enqueue_cancel_ops_unlocked(socket_type descriptor)
|
||||
{
|
||||
pending_cancellations_.push_back(descriptor);
|
||||
}
|
||||
|
||||
// Cancel any operations that are running against the descriptor and remove
|
||||
// its registration from the reactor.
|
||||
void close_descriptor(socket_type descriptor)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
|
||||
// Remove the descriptor from kqueue.
|
||||
struct kevent event[2];
|
||||
EV_SET(&event[0], descriptor, EVFILT_READ, EV_DELETE, 0, 0, 0);
|
||||
EV_SET(&event[1], descriptor, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
|
||||
::kevent(kqueue_fd_, event, 2, 0, 0, 0);
|
||||
|
||||
// Cancel any outstanding operations associated with the descriptor.
|
||||
cancel_ops_unlocked(descriptor);
|
||||
}
|
||||
|
||||
// Add a new timer queue to the reactor.
|
||||
template <typename Time_Traits>
|
||||
void add_timer_queue(timer_queue<Time_Traits>& timer_queue)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
timer_queues_.push_back(&timer_queue);
|
||||
}
|
||||
|
||||
// Schedule a timer in the given timer queue to expire at the specified
|
||||
// absolute time. The handler object will be invoked when the timer expires.
|
||||
template <typename Time_Traits, typename Handler>
|
||||
void schedule_timer(timer_queue<Time_Traits>& timer_queue,
|
||||
const typename Time_Traits::time_type& time, Handler handler, void* token)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
if (!shutdown_)
|
||||
if (timer_queue.enqueue_timer(time, handler, token))
|
||||
interrupter_.interrupt();
|
||||
}
|
||||
|
||||
// Cancel the timer associated with the given token. Returns the number of
|
||||
// handlers that have been posted or dispatched.
|
||||
template <typename Time_Traits>
|
||||
std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
return timer_queue.cancel_timer(token);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class task_io_service<kqueue_reactor<Own_Thread> >;
|
||||
|
||||
// Run the kqueue loop.
|
||||
void run(bool block)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
|
||||
// Dispatch any operation cancellations that were made while the select
|
||||
// loop was not running.
|
||||
read_op_queue_.dispatch_cancellations();
|
||||
write_op_queue_.dispatch_cancellations();
|
||||
except_op_queue_.dispatch_cancellations();
|
||||
|
||||
// Check if the thread is supposed to stop.
|
||||
if (stop_thread_)
|
||||
{
|
||||
// Clean up operations. We must not hold the lock since the operations may
|
||||
// make calls back into this reactor.
|
||||
lock.unlock();
|
||||
read_op_queue_.cleanup_operations();
|
||||
write_op_queue_.cleanup_operations();
|
||||
except_op_queue_.cleanup_operations();
|
||||
return;
|
||||
}
|
||||
|
||||
// We can return immediately if there's no work to do and the reactor is
|
||||
// not supposed to block.
|
||||
if (!block && read_op_queue_.empty() && write_op_queue_.empty()
|
||||
&& except_op_queue_.empty() && all_timer_queues_are_empty())
|
||||
{
|
||||
// Clean up operations. We must not hold the lock since the operations may
|
||||
// make calls back into this reactor.
|
||||
lock.unlock();
|
||||
read_op_queue_.cleanup_operations();
|
||||
write_op_queue_.cleanup_operations();
|
||||
except_op_queue_.cleanup_operations();
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine how long to block while waiting for events.
|
||||
timespec timeout_buf = { 0, 0 };
|
||||
timespec* timeout = block ? get_timeout(timeout_buf) : &timeout_buf;
|
||||
|
||||
wait_in_progress_ = true;
|
||||
lock.unlock();
|
||||
|
||||
// Block on the kqueue descriptor.
|
||||
struct kevent events[128];
|
||||
int num_events = kevent(kqueue_fd_, 0, 0, events, 128, timeout);
|
||||
|
||||
lock.lock();
|
||||
wait_in_progress_ = false;
|
||||
|
||||
// Block signals while dispatching operations.
|
||||
asio::detail::signal_blocker sb;
|
||||
|
||||
// Dispatch the waiting events.
|
||||
for (int i = 0; i < num_events; ++i)
|
||||
{
|
||||
int descriptor = events[i].ident;
|
||||
if (descriptor == interrupter_.read_descriptor())
|
||||
{
|
||||
interrupter_.reset();
|
||||
}
|
||||
else if (events[i].filter == EVFILT_READ)
|
||||
{
|
||||
// Dispatch operations associated with the descriptor.
|
||||
bool more_reads = false;
|
||||
bool more_except = false;
|
||||
if (events[i].flags & EV_ERROR)
|
||||
{
|
||||
int error = events[i].data;
|
||||
except_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
read_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
}
|
||||
else if (events[i].flags & EV_OOBAND)
|
||||
{
|
||||
more_except = except_op_queue_.dispatch_operation(descriptor, 0);
|
||||
if (events[i].data > 0)
|
||||
more_reads = read_op_queue_.dispatch_operation(descriptor, 0);
|
||||
else
|
||||
more_reads = read_op_queue_.has_operation(descriptor);
|
||||
}
|
||||
else
|
||||
{
|
||||
more_reads = read_op_queue_.dispatch_operation(descriptor, 0);
|
||||
more_except = except_op_queue_.has_operation(descriptor);
|
||||
}
|
||||
|
||||
// Update the descriptor in the kqueue.
|
||||
struct kevent event;
|
||||
if (more_reads)
|
||||
EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0);
|
||||
else if (more_except)
|
||||
EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0);
|
||||
else
|
||||
EV_SET(&event, descriptor, EVFILT_READ, EV_DELETE, 0, 0, 0);
|
||||
if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
|
||||
{
|
||||
int error = errno;
|
||||
except_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
read_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
}
|
||||
}
|
||||
else if (events[i].filter == EVFILT_WRITE)
|
||||
{
|
||||
// Dispatch operations associated with the descriptor.
|
||||
bool more_writes = false;
|
||||
if (events[i].flags & EV_ERROR)
|
||||
{
|
||||
int error = events[i].data;
|
||||
write_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
}
|
||||
else
|
||||
{
|
||||
more_writes = write_op_queue_.dispatch_operation(descriptor, 0);
|
||||
}
|
||||
|
||||
// Update the descriptor in the kqueue.
|
||||
struct kevent event;
|
||||
if (more_writes)
|
||||
EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0);
|
||||
else
|
||||
EV_SET(&event, descriptor, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
|
||||
if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
|
||||
{
|
||||
int error = errno;
|
||||
write_op_queue_.dispatch_all_operations(descriptor, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
read_op_queue_.dispatch_cancellations();
|
||||
write_op_queue_.dispatch_cancellations();
|
||||
except_op_queue_.dispatch_cancellations();
|
||||
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
|
||||
timer_queues_[i]->dispatch_timers();
|
||||
|
||||
// Issue any pending cancellations.
|
||||
for (std::size_t i = 0; i < pending_cancellations_.size(); ++i)
|
||||
cancel_ops_unlocked(pending_cancellations_[i]);
|
||||
pending_cancellations_.clear();
|
||||
|
||||
// Clean up operations. We must not hold the lock since the operations may
|
||||
// make calls back into this reactor.
|
||||
lock.unlock();
|
||||
read_op_queue_.cleanup_operations();
|
||||
write_op_queue_.cleanup_operations();
|
||||
except_op_queue_.cleanup_operations();
|
||||
}
|
||||
|
||||
// Run the select loop in the thread.
|
||||
void run_thread()
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
while (!stop_thread_)
|
||||
{
|
||||
lock.unlock();
|
||||
run(true);
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
// Entry point for the select loop thread.
|
||||
static void call_run_thread(kqueue_reactor* reactor)
|
||||
{
|
||||
reactor->run_thread();
|
||||
}
|
||||
|
||||
// Interrupt the select loop.
|
||||
void interrupt()
|
||||
{
|
||||
interrupter_.interrupt();
|
||||
}
|
||||
|
||||
// Create the kqueue file descriptor. Throws an exception if the descriptor
|
||||
// cannot be created.
|
||||
static int do_kqueue_create()
|
||||
{
|
||||
int fd = kqueue();
|
||||
if (fd == -1)
|
||||
{
|
||||
system_exception e("kqueue", errno);
|
||||
boost::throw_exception(e);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
// Check if all timer queues are empty.
|
||||
bool all_timer_queues_are_empty() const
|
||||
{
|
||||
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
|
||||
if (!timer_queues_[i]->empty())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the timeout value for the kevent call.
|
||||
timespec* get_timeout(timespec& ts)
|
||||
{
|
||||
if (all_timer_queues_are_empty())
|
||||
return 0;
|
||||
|
||||
// By default we will wait no longer than 5 minutes. This will ensure that
|
||||
// any changes to the system clock are detected after no longer than this.
|
||||
boost::posix_time::time_duration minimum_wait_duration
|
||||
= boost::posix_time::minutes(5);
|
||||
|
||||
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
|
||||
{
|
||||
boost::posix_time::time_duration wait_duration
|
||||
= timer_queues_[i]->wait_duration();
|
||||
if (wait_duration < minimum_wait_duration)
|
||||
minimum_wait_duration = wait_duration;
|
||||
}
|
||||
|
||||
if (minimum_wait_duration > boost::posix_time::time_duration())
|
||||
{
|
||||
ts.tv_sec = minimum_wait_duration.total_seconds();
|
||||
ts.tv_nsec = minimum_wait_duration.total_nanoseconds() % 1000000000;
|
||||
}
|
||||
else
|
||||
{
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 0;
|
||||
}
|
||||
|
||||
return &ts;
|
||||
}
|
||||
|
||||
// Cancel all operations associated with the given descriptor. The do_cancel
|
||||
// function of the handler objects will be invoked. This function does not
|
||||
// acquire the epoll_reactor's mutex.
|
||||
void cancel_ops_unlocked(socket_type descriptor)
|
||||
{
|
||||
bool interrupt = read_op_queue_.cancel_operations(descriptor);
|
||||
interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt;
|
||||
interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt;
|
||||
if (interrupt)
|
||||
interrupter_.interrupt();
|
||||
}
|
||||
|
||||
// Mutex to protect access to internal data.
|
||||
asio::detail::mutex mutex_;
|
||||
|
||||
// The epoll file descriptor.
|
||||
int kqueue_fd_;
|
||||
|
||||
// Whether the kqueue wait call is currently in progress
|
||||
bool wait_in_progress_;
|
||||
|
||||
// The interrupter is used to break a blocking epoll_wait call.
|
||||
select_interrupter interrupter_;
|
||||
|
||||
// The queue of read operations.
|
||||
reactor_op_queue<socket_type> read_op_queue_;
|
||||
|
||||
// The queue of write operations.
|
||||
reactor_op_queue<socket_type> write_op_queue_;
|
||||
|
||||
// The queue of except operations.
|
||||
reactor_op_queue<socket_type> except_op_queue_;
|
||||
|
||||
// The timer queues.
|
||||
std::vector<timer_queue_base*> timer_queues_;
|
||||
|
||||
// The descriptors that are pending cancellation.
|
||||
std::vector<socket_type> pending_cancellations_;
|
||||
|
||||
// Does the reactor loop thread need to stop.
|
||||
bool stop_thread_;
|
||||
|
||||
// The thread that is running the reactor loop.
|
||||
asio::detail::thread* thread_;
|
||||
|
||||
// Whether the service has been shut down.
|
||||
bool shutdown_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // defined(ASIO_HAS_KQUEUE)
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_KQUEUE_REACTOR_HPP
|
||||
41
include/libtorrent/asio/detail/kqueue_reactor_fwd.hpp
Normal file
41
include/libtorrent/asio/detail/kqueue_reactor_fwd.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// kqueue_reactor_fwd.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP
|
||||
#define ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#if !defined(ASIO_DISABLE_KQUEUE)
|
||||
#if defined(__MACH__) && defined(__APPLE__)
|
||||
|
||||
// Define this to indicate that epoll is supported on the target platform.
|
||||
#define ASIO_HAS_KQUEUE 1
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
template <bool Own_Thread>
|
||||
class kqueue_reactor;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // defined(__MACH__) && defined(__APPLE__)
|
||||
#endif // !defined(ASIO_DISABLE_KQUEUE)
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP
|
||||
50
include/libtorrent/asio/detail/mutex.hpp
Normal file
50
include/libtorrent/asio/detail/mutex.hpp
Normal file
@ -0,0 +1,50 @@
|
||||
//
|
||||
// mutex.hpp
|
||||
// ~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_MUTEX_HPP
|
||||
#define ASIO_DETAIL_MUTEX_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#if !defined(BOOST_HAS_THREADS)
|
||||
# include "asio/detail/null_mutex.hpp"
|
||||
#elif defined(BOOST_WINDOWS)
|
||||
# include "asio/detail/win_mutex.hpp"
|
||||
#elif defined(BOOST_HAS_PTHREADS)
|
||||
# include "asio/detail/posix_mutex.hpp"
|
||||
#else
|
||||
# error Only Windows and POSIX are supported!
|
||||
#endif
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
#if !defined(BOOST_HAS_THREADS)
|
||||
typedef null_mutex mutex;
|
||||
#elif defined(BOOST_WINDOWS)
|
||||
typedef win_mutex mutex;
|
||||
#elif defined(BOOST_HAS_PTHREADS)
|
||||
typedef posix_mutex mutex;
|
||||
#endif
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_MUTEX_HPP
|
||||
55
include/libtorrent/asio/detail/noncopyable.hpp
Normal file
55
include/libtorrent/asio/detail/noncopyable.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
//
|
||||
// noncopyable.hpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_NONCOPYABLE_HPP
|
||||
#define ASIO_DETAIL_NONCOPYABLE_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/detail/workaround.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
|
||||
// Redefine the noncopyable class for Borland C++ since that compiler does not
|
||||
// apply the empty base optimisation unless the base class contains a dummy
|
||||
// char data member.
|
||||
class noncopyable
|
||||
{
|
||||
protected:
|
||||
noncopyable() {}
|
||||
~noncopyable() {}
|
||||
private:
|
||||
noncopyable(const noncopyable&);
|
||||
const noncopyable& operator=(const noncopyable&);
|
||||
char dummy_;
|
||||
};
|
||||
#else
|
||||
using boost::noncopyable;
|
||||
#endif
|
||||
|
||||
} // namespace detail
|
||||
|
||||
using asio::detail::noncopyable;
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_NONCOPYABLE_HPP
|
||||
68
include/libtorrent/asio/detail/null_event.hpp
Normal file
68
include/libtorrent/asio/detail/null_event.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
//
|
||||
// null_event.hpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_NULL_EVENT_HPP
|
||||
#define ASIO_DETAIL_NULL_EVENT_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#if !defined(BOOST_HAS_THREADS)
|
||||
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
class null_event
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
// Constructor.
|
||||
null_event()
|
||||
{
|
||||
}
|
||||
|
||||
// Destructor.
|
||||
~null_event()
|
||||
{
|
||||
}
|
||||
|
||||
// Signal the event.
|
||||
void signal()
|
||||
{
|
||||
}
|
||||
|
||||
// Reset the event.
|
||||
void clear()
|
||||
{
|
||||
}
|
||||
|
||||
// Wait for the event to become signalled.
|
||||
void wait()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // !defined(BOOST_HAS_THREADS)
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_NULL_EVENT_HPP
|
||||
66
include/libtorrent/asio/detail/null_mutex.hpp
Normal file
66
include/libtorrent/asio/detail/null_mutex.hpp
Normal file
@ -0,0 +1,66 @@
|
||||
//
|
||||
// null_mutex.hpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_NULL_MUTEX_HPP
|
||||
#define ASIO_DETAIL_NULL_MUTEX_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#if !defined(BOOST_HAS_THREADS)
|
||||
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
#include "asio/detail/scoped_lock.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
class null_mutex
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
typedef asio::detail::scoped_lock<null_mutex> scoped_lock;
|
||||
|
||||
// Constructor.
|
||||
null_mutex()
|
||||
{
|
||||
}
|
||||
|
||||
// Destructor.
|
||||
~null_mutex()
|
||||
{
|
||||
}
|
||||
|
||||
// Lock the mutex.
|
||||
void lock()
|
||||
{
|
||||
}
|
||||
|
||||
// Unlock the mutex.
|
||||
void unlock()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // !defined(BOOST_HAS_THREADS)
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_NULL_MUTEX_HPP
|
||||
63
include/libtorrent/asio/detail/null_signal_blocker.hpp
Normal file
63
include/libtorrent/asio/detail/null_signal_blocker.hpp
Normal file
@ -0,0 +1,63 @@
|
||||
//
|
||||
// null_signal_blocker.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP
|
||||
#define ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#if !defined(BOOST_HAS_THREADS)
|
||||
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
class null_signal_blocker
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
// Constructor blocks all signals for the calling thread.
|
||||
null_signal_blocker()
|
||||
{
|
||||
}
|
||||
|
||||
// Destructor restores the previous signal mask.
|
||||
~null_signal_blocker()
|
||||
{
|
||||
}
|
||||
|
||||
// Block all signals for the calling thread.
|
||||
void block()
|
||||
{
|
||||
}
|
||||
|
||||
// Restore the previous signal mask.
|
||||
void unblock()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // !defined(BOOST_HAS_THREADS)
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP
|
||||
67
include/libtorrent/asio/detail/null_thread.hpp
Normal file
67
include/libtorrent/asio/detail/null_thread.hpp
Normal file
@ -0,0 +1,67 @@
|
||||
//
|
||||
// null_thread.hpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_NULL_THREAD_HPP
|
||||
#define ASIO_DETAIL_NULL_THREAD_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#if !defined(BOOST_HAS_THREADS)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/error.hpp"
|
||||
#include "asio/system_exception.hpp"
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
class null_thread
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
// Constructor.
|
||||
template <typename Function>
|
||||
null_thread(Function f)
|
||||
{
|
||||
system_exception e("thread", asio::error::not_supported);
|
||||
boost::throw_exception(e);
|
||||
}
|
||||
|
||||
// Destructor.
|
||||
~null_thread()
|
||||
{
|
||||
}
|
||||
|
||||
// Wait for the thread to exit.
|
||||
void join()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // !defined(BOOST_HAS_THREADS)
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_NULL_THREAD_HPP
|
||||
70
include/libtorrent/asio/detail/null_tss_ptr.hpp
Normal file
70
include/libtorrent/asio/detail/null_tss_ptr.hpp
Normal file
@ -0,0 +1,70 @@
|
||||
//
|
||||
// null_tss_ptr.hpp
|
||||
// ~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_NULL_TSS_PTR_HPP
|
||||
#define ASIO_DETAIL_NULL_TSS_PTR_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#if !defined(BOOST_HAS_THREADS)
|
||||
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
class null_tss_ptr
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
// Constructor.
|
||||
null_tss_ptr()
|
||||
: value_(0)
|
||||
{
|
||||
}
|
||||
|
||||
// Destructor.
|
||||
~null_tss_ptr()
|
||||
{
|
||||
}
|
||||
|
||||
// Get the value.
|
||||
operator T*() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
// Set the value.
|
||||
void operator=(T* value)
|
||||
{
|
||||
value_ = value;
|
||||
}
|
||||
|
||||
private:
|
||||
T* value_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // !defined(BOOST_HAS_THREADS)
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_NULL_TSS_PTR_HPP
|
||||
312
include/libtorrent/asio/detail/old_win_sdk_compat.hpp
Normal file
312
include/libtorrent/asio/detail/old_win_sdk_compat.hpp
Normal file
@ -0,0 +1,312 @@
|
||||
//
|
||||
// old_win_sdk_compat.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP
|
||||
#define ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
|
||||
// Guess whether we are building against on old Platform SDK.
|
||||
#if !defined(IPPROTO_IPV6)
|
||||
#define ASIO_HAS_OLD_WIN_SDK 1
|
||||
#endif // !defined(IPPROTO_IPV6)
|
||||
|
||||
#if defined(ASIO_HAS_OLD_WIN_SDK)
|
||||
|
||||
// Emulation of types that are missing from old Platform SDKs.
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
enum
|
||||
{
|
||||
sockaddr_storage_maxsize = 128, // Maximum size.
|
||||
sockaddr_storage_alignsize = (sizeof(__int64)), // Desired alignment.
|
||||
sockaddr_storage_pad1size = (sockaddr_storage_alignsize - sizeof(short)),
|
||||
sockaddr_storage_pad2size = (sockaddr_storage_maxsize -
|
||||
(sizeof(short) + sockaddr_storage_pad1size + sockaddr_storage_alignsize))
|
||||
};
|
||||
|
||||
struct sockaddr_storage_emulation
|
||||
{
|
||||
short ss_family;
|
||||
char __ss_pad1[sockaddr_storage_pad1size];
|
||||
__int64 __ss_align;
|
||||
char __ss_pad2[sockaddr_storage_pad2size];
|
||||
};
|
||||
|
||||
struct in6_addr_emulation
|
||||
{
|
||||
u_char s6_addr[16];
|
||||
};
|
||||
|
||||
struct sockaddr_in6_emulation
|
||||
{
|
||||
short sin6_family;
|
||||
u_short sin6_port;
|
||||
u_long sin6_flowinfo;
|
||||
in6_addr_emulation sin6_addr;
|
||||
u_long sin6_scope_id;
|
||||
};
|
||||
|
||||
struct ipv6_mreq_emulation
|
||||
{
|
||||
in6_addr_emulation ipv6mr_multiaddr;
|
||||
unsigned int ipv6mr_interface;
|
||||
};
|
||||
|
||||
#if !defined(IN6ADDR_ANY_INIT)
|
||||
# define IN6ADDR_ANY_INIT { 0 }
|
||||
#endif
|
||||
|
||||
#if !defined(IN6ADDR_LOOPBACK_INIT)
|
||||
# define IN6ADDR_LOOPBACK_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }
|
||||
#endif
|
||||
|
||||
struct addrinfo_emulation
|
||||
{
|
||||
int ai_flags;
|
||||
int ai_family;
|
||||
int ai_socktype;
|
||||
int ai_protocol;
|
||||
size_t ai_addrlen;
|
||||
char* ai_canonname;
|
||||
sockaddr* ai_addr;
|
||||
addrinfo_emulation* ai_next;
|
||||
};
|
||||
|
||||
#if !defined(AI_PASSIVE)
|
||||
# define AI_PASSIVE 0x1
|
||||
#endif
|
||||
|
||||
#if !defined(AI_CANONNAME)
|
||||
# define AI_CANONNAME 0x2
|
||||
#endif
|
||||
|
||||
#if !defined(AI_NUMERICHOST)
|
||||
# define AI_NUMERICHOST 0x4
|
||||
#endif
|
||||
|
||||
#if !defined(EAI_AGAIN)
|
||||
# define EAI_AGAIN WSATRY_AGAIN
|
||||
#endif
|
||||
|
||||
#if !defined(EAI_BADFLAGS)
|
||||
# define EAI_BADFLAGS WSAEINVAL
|
||||
#endif
|
||||
|
||||
#if !defined(EAI_FAIL)
|
||||
# define EAI_FAIL WSANO_RECOVERY
|
||||
#endif
|
||||
|
||||
#if !defined(EAI_FAMILY)
|
||||
# define EAI_FAMILY WSAEAFNOSUPPORT
|
||||
#endif
|
||||
|
||||
#if !defined(EAI_MEMORY)
|
||||
# define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY
|
||||
#endif
|
||||
|
||||
#if !defined(EAI_NODATA)
|
||||
# define EAI_NODATA WSANO_DATA
|
||||
#endif
|
||||
|
||||
#if !defined(EAI_NONAME)
|
||||
# define EAI_NONAME WSAHOST_NOT_FOUND
|
||||
#endif
|
||||
|
||||
#if !defined(EAI_SERVICE)
|
||||
# define EAI_SERVICE WSATYPE_NOT_FOUND
|
||||
#endif
|
||||
|
||||
#if !defined(EAI_SOCKTYPE)
|
||||
# define EAI_SOCKTYPE WSAESOCKTNOSUPPORT
|
||||
#endif
|
||||
|
||||
#if !defined(NI_NOFQDN)
|
||||
# define NI_NOFQDN 0x01
|
||||
#endif
|
||||
|
||||
#if !defined(NI_NUMERICHOST)
|
||||
# define NI_NUMERICHOST 0x02
|
||||
#endif
|
||||
|
||||
#if !defined(NI_NAMEREQD)
|
||||
# define NI_NAMEREQD 0x04
|
||||
#endif
|
||||
|
||||
#if !defined(NI_NUMERICSERV)
|
||||
# define NI_NUMERICSERV 0x08
|
||||
#endif
|
||||
|
||||
#if !defined(NI_DGRAM)
|
||||
# define NI_DGRAM 0x10
|
||||
#endif
|
||||
|
||||
#if !defined(IPPROTO_IPV6)
|
||||
# define IPPROTO_IPV6 41
|
||||
#endif
|
||||
|
||||
#if !defined(IPV6_MULTICAST_IF)
|
||||
# define IPV6_MULTICAST_IF 9
|
||||
#endif
|
||||
|
||||
#if !defined(IPV6_MULTICAST_HOPS)
|
||||
# define IPV6_MULTICAST_HOPS 10
|
||||
#endif
|
||||
|
||||
#if !defined(IPV6_MULTICAST_LOOP)
|
||||
# define IPV6_MULTICAST_LOOP 11
|
||||
#endif
|
||||
|
||||
#if !defined(IPV6_JOIN_GROUP)
|
||||
# define IPV6_JOIN_GROUP 12
|
||||
#endif
|
||||
|
||||
#if !defined(IPV6_LEAVE_GROUP)
|
||||
# define IPV6_LEAVE_GROUP 13
|
||||
#endif
|
||||
|
||||
inline int IN6_IS_ADDR_UNSPECIFIED(const in6_addr_emulation* a)
|
||||
{
|
||||
return ((a->s6_addr[0] == 0)
|
||||
&& (a->s6_addr[1] == 0)
|
||||
&& (a->s6_addr[2] == 0)
|
||||
&& (a->s6_addr[3] == 0)
|
||||
&& (a->s6_addr[4] == 0)
|
||||
&& (a->s6_addr[5] == 0)
|
||||
&& (a->s6_addr[6] == 0)
|
||||
&& (a->s6_addr[7] == 0)
|
||||
&& (a->s6_addr[8] == 0)
|
||||
&& (a->s6_addr[9] == 0)
|
||||
&& (a->s6_addr[10] == 0)
|
||||
&& (a->s6_addr[11] == 0)
|
||||
&& (a->s6_addr[12] == 0)
|
||||
&& (a->s6_addr[13] == 0)
|
||||
&& (a->s6_addr[14] == 0)
|
||||
&& (a->s6_addr[15] == 0));
|
||||
}
|
||||
|
||||
inline int IN6_IS_ADDR_LOOPBACK(const in6_addr_emulation* a)
|
||||
{
|
||||
return ((a->s6_addr[0] == 0)
|
||||
&& (a->s6_addr[1] == 0)
|
||||
&& (a->s6_addr[2] == 0)
|
||||
&& (a->s6_addr[3] == 0)
|
||||
&& (a->s6_addr[4] == 0)
|
||||
&& (a->s6_addr[5] == 0)
|
||||
&& (a->s6_addr[6] == 0)
|
||||
&& (a->s6_addr[7] == 0)
|
||||
&& (a->s6_addr[8] == 0)
|
||||
&& (a->s6_addr[9] == 0)
|
||||
&& (a->s6_addr[10] == 0)
|
||||
&& (a->s6_addr[11] == 0)
|
||||
&& (a->s6_addr[12] == 0)
|
||||
&& (a->s6_addr[13] == 0)
|
||||
&& (a->s6_addr[14] == 0)
|
||||
&& (a->s6_addr[15] == 1));
|
||||
}
|
||||
|
||||
inline int IN6_IS_ADDR_MULTICAST(const in6_addr_emulation* a)
|
||||
{
|
||||
return (a->s6_addr[0] == 0xff);
|
||||
}
|
||||
|
||||
inline int IN6_IS_ADDR_LINKLOCAL(const in6_addr_emulation* a)
|
||||
{
|
||||
return ((a->s6_addr[0] == 0xfe) && ((a->s6_addr[1] & 0xc0) == 0x80));
|
||||
}
|
||||
|
||||
inline int IN6_IS_ADDR_SITELOCAL(const in6_addr_emulation* a)
|
||||
{
|
||||
return ((a->s6_addr[0] == 0xfe) && ((a->s6_addr[1] & 0xc0) == 0xc0));
|
||||
}
|
||||
|
||||
inline int IN6_IS_ADDR_V4MAPPED(const in6_addr_emulation* a)
|
||||
{
|
||||
return ((a->s6_addr[0] == 0)
|
||||
&& (a->s6_addr[1] == 0)
|
||||
&& (a->s6_addr[2] == 0)
|
||||
&& (a->s6_addr[3] == 0)
|
||||
&& (a->s6_addr[4] == 0)
|
||||
&& (a->s6_addr[5] == 0)
|
||||
&& (a->s6_addr[6] == 0)
|
||||
&& (a->s6_addr[7] == 0)
|
||||
&& (a->s6_addr[8] == 0)
|
||||
&& (a->s6_addr[9] == 0)
|
||||
&& (a->s6_addr[10] == 0xff)
|
||||
&& (a->s6_addr[11] == 0xff));
|
||||
}
|
||||
|
||||
inline int IN6_IS_ADDR_V4COMPAT(const in6_addr_emulation* a)
|
||||
{
|
||||
return ((a->s6_addr[0] == 0)
|
||||
&& (a->s6_addr[1] == 0)
|
||||
&& (a->s6_addr[2] == 0)
|
||||
&& (a->s6_addr[3] == 0)
|
||||
&& (a->s6_addr[4] == 0)
|
||||
&& (a->s6_addr[5] == 0)
|
||||
&& (a->s6_addr[6] == 0)
|
||||
&& (a->s6_addr[7] == 0)
|
||||
&& (a->s6_addr[8] == 0)
|
||||
&& (a->s6_addr[9] == 0)
|
||||
&& (a->s6_addr[10] == 0xff)
|
||||
&& (a->s6_addr[11] == 0xff)
|
||||
&& !((a->s6_addr[12] == 0)
|
||||
&& (a->s6_addr[13] == 0)
|
||||
&& (a->s6_addr[14] == 0)
|
||||
&& ((a->s6_addr[15] == 0) || (a->s6_addr[15] == 1))));
|
||||
}
|
||||
|
||||
inline int IN6_IS_ADDR_MC_NODELOCAL(const in6_addr_emulation* a)
|
||||
{
|
||||
return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 1);
|
||||
}
|
||||
|
||||
inline int IN6_IS_ADDR_MC_LINKLOCAL(const in6_addr_emulation* a)
|
||||
{
|
||||
return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 2);
|
||||
}
|
||||
|
||||
inline int IN6_IS_ADDR_MC_SITELOCAL(const in6_addr_emulation* a)
|
||||
{
|
||||
return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 5);
|
||||
}
|
||||
|
||||
inline int IN6_IS_ADDR_MC_ORGLOCAL(const in6_addr_emulation* a)
|
||||
{
|
||||
return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 8);
|
||||
}
|
||||
|
||||
inline int IN6_IS_ADDR_MC_GLOBAL(const in6_addr_emulation* a)
|
||||
{
|
||||
return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 0xe);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // defined(ASIO_HAS_OLD_WIN_SDK)
|
||||
|
||||
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP
|
||||
104
include/libtorrent/asio/detail/pipe_select_interrupter.hpp
Normal file
104
include/libtorrent/asio/detail/pipe_select_interrupter.hpp
Normal file
@ -0,0 +1,104 @@
|
||||
//
|
||||
// pipe_select_interrupter.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP
|
||||
#define ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <fcntl.h>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/detail/socket_types.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
class pipe_select_interrupter
|
||||
{
|
||||
public:
|
||||
// Constructor.
|
||||
pipe_select_interrupter()
|
||||
{
|
||||
int pipe_fds[2];
|
||||
if (pipe(pipe_fds) == 0)
|
||||
{
|
||||
read_descriptor_ = pipe_fds[0];
|
||||
::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK);
|
||||
write_descriptor_ = pipe_fds[1];
|
||||
::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
// Destructor.
|
||||
~pipe_select_interrupter()
|
||||
{
|
||||
if (read_descriptor_ != -1)
|
||||
::close(read_descriptor_);
|
||||
if (write_descriptor_ != -1)
|
||||
::close(write_descriptor_);
|
||||
}
|
||||
|
||||
// Interrupt the select call.
|
||||
void interrupt()
|
||||
{
|
||||
char byte = 0;
|
||||
::write(write_descriptor_, &byte, 1);
|
||||
}
|
||||
|
||||
// Reset the select interrupt. Returns true if the call was interrupted.
|
||||
bool reset()
|
||||
{
|
||||
char data[1024];
|
||||
int bytes_read = ::read(read_descriptor_, data, sizeof(data));
|
||||
bool was_interrupted = (bytes_read > 0);
|
||||
while (bytes_read == sizeof(data))
|
||||
bytes_read = ::read(read_descriptor_, data, sizeof(data));
|
||||
return was_interrupted;
|
||||
}
|
||||
|
||||
// Get the read descriptor to be passed to select.
|
||||
int read_descriptor() const
|
||||
{
|
||||
return read_descriptor_;
|
||||
}
|
||||
|
||||
private:
|
||||
// The read end of a connection used to interrupt the select call. This file
|
||||
// descriptor is passed to select such that when it is time to stop, a single
|
||||
// byte will be written on the other end of the connection and this
|
||||
// descriptor will become readable.
|
||||
int read_descriptor_;
|
||||
|
||||
// The write end of a connection used to interrupt the select call. A single
|
||||
// byte may be written to this to wake up the select which is waiting for the
|
||||
// other end to become readable.
|
||||
int write_descriptor_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP
|
||||
88
include/libtorrent/asio/detail/pop_options.hpp
Normal file
88
include/libtorrent/asio/detail/pop_options.hpp
Normal file
@ -0,0 +1,88 @@
|
||||
//
|
||||
// pop_options.hpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
// No header guard
|
||||
|
||||
#if defined(__COMO__)
|
||||
|
||||
// Comeau C++
|
||||
|
||||
#elif defined(__DMC__)
|
||||
|
||||
// Digital Mars C++
|
||||
|
||||
#elif defined(__INTEL_COMPILER) || defined(__ICL) \
|
||||
|| defined(__ICC) || defined(__ECC)
|
||||
|
||||
// Intel C++
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
// GNU C++
|
||||
|
||||
# if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
# pragma pack (pop)
|
||||
# endif
|
||||
|
||||
#elif defined(__KCC)
|
||||
|
||||
// Kai C++
|
||||
|
||||
#elif defined(__sgi)
|
||||
|
||||
// SGI MIPSpro C++
|
||||
|
||||
#elif defined(__DECCXX)
|
||||
|
||||
// Compaq Tru64 Unix cxx
|
||||
|
||||
#elif defined(__ghs)
|
||||
|
||||
// Greenhills C++
|
||||
|
||||
#elif defined(__BORLANDC__)
|
||||
|
||||
// Borland C++
|
||||
|
||||
# pragma option pop
|
||||
# pragma nopushoptwarn
|
||||
# pragma nopackwarning
|
||||
|
||||
#elif defined(__MWERKS__)
|
||||
|
||||
// Metrowerks CodeWarrior
|
||||
|
||||
#elif defined(__SUNPRO_CC)
|
||||
|
||||
// Sun Workshop Compiler C++
|
||||
|
||||
#elif defined(__HP_aCC)
|
||||
|
||||
// HP aCC
|
||||
|
||||
#elif defined(__MRC__) || defined(__SC__)
|
||||
|
||||
// MPW MrCpp or SCpp
|
||||
|
||||
#elif defined(__IBMCPP__)
|
||||
|
||||
// IBM Visual Age
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
// Microsoft Visual C++
|
||||
//
|
||||
// Must remain the last #elif since some other vendors (Metrowerks, for example)
|
||||
// also #define _MSC_VER
|
||||
|
||||
# pragma warning (pop)
|
||||
# pragma pack (pop)
|
||||
|
||||
#endif
|
||||
107
include/libtorrent/asio/detail/posix_event.hpp
Normal file
107
include/libtorrent/asio/detail/posix_event.hpp
Normal file
@ -0,0 +1,107 @@
|
||||
//
|
||||
// posix_event.hpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_POSIX_EVENT_HPP
|
||||
#define ASIO_DETAIL_POSIX_EVENT_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#if defined(BOOST_HAS_PTHREADS)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <pthread.h>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/system_exception.hpp"
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
class posix_event
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
// Constructor.
|
||||
posix_event()
|
||||
: signalled_(false)
|
||||
{
|
||||
int error = ::pthread_mutex_init(&mutex_, 0);
|
||||
if (error != 0)
|
||||
{
|
||||
system_exception e("event", error);
|
||||
boost::throw_exception(e);
|
||||
}
|
||||
|
||||
error = ::pthread_cond_init(&cond_, 0);
|
||||
if (error != 0)
|
||||
{
|
||||
::pthread_mutex_destroy(&mutex_);
|
||||
system_exception e("event", error);
|
||||
boost::throw_exception(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Destructor.
|
||||
~posix_event()
|
||||
{
|
||||
::pthread_cond_destroy(&cond_);
|
||||
::pthread_mutex_destroy(&mutex_);
|
||||
}
|
||||
|
||||
// Signal the event.
|
||||
void signal()
|
||||
{
|
||||
::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK.
|
||||
signalled_ = true;
|
||||
::pthread_cond_signal(&cond_); // Ignore EINVAL.
|
||||
::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM.
|
||||
}
|
||||
|
||||
// Reset the event.
|
||||
void clear()
|
||||
{
|
||||
::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK.
|
||||
signalled_ = false;
|
||||
::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM.
|
||||
}
|
||||
|
||||
// Wait for the event to become signalled.
|
||||
void wait()
|
||||
{
|
||||
::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK.
|
||||
while (!signalled_)
|
||||
::pthread_cond_wait(&cond_, &mutex_); // Ignore EINVAL.
|
||||
::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM.
|
||||
}
|
||||
|
||||
private:
|
||||
::pthread_mutex_t mutex_;
|
||||
::pthread_cond_t cond_;
|
||||
bool signalled_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // defined(BOOST_HAS_PTHREADS)
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_POSIX_EVENT_HPP
|
||||
71
include/libtorrent/asio/detail/posix_fd_set_adapter.hpp
Normal file
71
include/libtorrent/asio/detail/posix_fd_set_adapter.hpp
Normal file
@ -0,0 +1,71 @@
|
||||
//
|
||||
// posix_fd_set_adapter.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP
|
||||
#define ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/socket_types.hpp"
|
||||
|
||||
#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements.
|
||||
class posix_fd_set_adapter
|
||||
{
|
||||
public:
|
||||
posix_fd_set_adapter()
|
||||
: max_descriptor_(invalid_socket)
|
||||
{
|
||||
FD_ZERO(&fd_set_);
|
||||
}
|
||||
|
||||
void set(socket_type descriptor)
|
||||
{
|
||||
if (max_descriptor_ == invalid_socket || descriptor > max_descriptor_)
|
||||
max_descriptor_ = descriptor;
|
||||
FD_SET(descriptor, &fd_set_);
|
||||
}
|
||||
|
||||
bool is_set(socket_type descriptor) const
|
||||
{
|
||||
return FD_ISSET(descriptor, &fd_set_) != 0;
|
||||
}
|
||||
|
||||
operator fd_set*()
|
||||
{
|
||||
return &fd_set_;
|
||||
}
|
||||
|
||||
socket_type max_descriptor() const
|
||||
{
|
||||
return max_descriptor_;
|
||||
}
|
||||
|
||||
private:
|
||||
fd_set fd_set_;
|
||||
socket_type max_descriptor_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP
|
||||
94
include/libtorrent/asio/detail/posix_mutex.hpp
Normal file
94
include/libtorrent/asio/detail/posix_mutex.hpp
Normal file
@ -0,0 +1,94 @@
|
||||
//
|
||||
// posix_mutex.hpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_POSIX_MUTEX_HPP
|
||||
#define ASIO_DETAIL_POSIX_MUTEX_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#if defined(BOOST_HAS_PTHREADS)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <pthread.h>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/system_exception.hpp"
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
#include "asio/detail/scoped_lock.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
class posix_mutex
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
typedef asio::detail::scoped_lock<posix_mutex> scoped_lock;
|
||||
|
||||
// Constructor.
|
||||
posix_mutex()
|
||||
{
|
||||
int error = ::pthread_mutex_init(&mutex_, 0);
|
||||
if (error != 0)
|
||||
{
|
||||
system_exception e("mutex", error);
|
||||
boost::throw_exception(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Destructor.
|
||||
~posix_mutex()
|
||||
{
|
||||
::pthread_mutex_destroy(&mutex_);
|
||||
}
|
||||
|
||||
// Lock the mutex.
|
||||
void lock()
|
||||
{
|
||||
int error = ::pthread_mutex_lock(&mutex_);
|
||||
if (error != 0)
|
||||
{
|
||||
system_exception e("mutex", error);
|
||||
boost::throw_exception(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock the mutex.
|
||||
void unlock()
|
||||
{
|
||||
int error = ::pthread_mutex_unlock(&mutex_);
|
||||
if (error != 0)
|
||||
{
|
||||
system_exception e("mutex", error);
|
||||
boost::throw_exception(e);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
::pthread_mutex_t mutex_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // defined(BOOST_HAS_PTHREADS)
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_POSIX_MUTEX_HPP
|
||||
89
include/libtorrent/asio/detail/posix_signal_blocker.hpp
Normal file
89
include/libtorrent/asio/detail/posix_signal_blocker.hpp
Normal file
@ -0,0 +1,89 @@
|
||||
//
|
||||
// posix_signal_blocker.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP
|
||||
#define ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#if defined(BOOST_HAS_PTHREADS)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <csignal>
|
||||
#include <pthread.h>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
class posix_signal_blocker
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
// Constructor blocks all signals for the calling thread.
|
||||
posix_signal_blocker()
|
||||
: blocked_(false)
|
||||
{
|
||||
sigset_t new_mask;
|
||||
sigfillset(&new_mask);
|
||||
blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0);
|
||||
}
|
||||
|
||||
// Destructor restores the previous signal mask.
|
||||
~posix_signal_blocker()
|
||||
{
|
||||
if (blocked_)
|
||||
pthread_sigmask(SIG_SETMASK, &old_mask_, 0);
|
||||
}
|
||||
|
||||
// Block all signals for the calling thread.
|
||||
void block()
|
||||
{
|
||||
if (!blocked_)
|
||||
{
|
||||
sigset_t new_mask;
|
||||
sigfillset(&new_mask);
|
||||
blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the previous signal mask.
|
||||
void unblock()
|
||||
{
|
||||
if (blocked_)
|
||||
blocked_ = (pthread_sigmask(SIG_SETMASK, &old_mask_, 0) != 0);
|
||||
}
|
||||
|
||||
private:
|
||||
// Have signals been blocked.
|
||||
bool blocked_;
|
||||
|
||||
// The previous signal mask.
|
||||
sigset_t old_mask_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // defined(BOOST_HAS_PTHREADS)
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP
|
||||
125
include/libtorrent/asio/detail/posix_thread.hpp
Normal file
125
include/libtorrent/asio/detail/posix_thread.hpp
Normal file
@ -0,0 +1,125 @@
|
||||
//
|
||||
// posix_thread.hpp
|
||||
// ~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_POSIX_THREAD_HPP
|
||||
#define ASIO_DETAIL_POSIX_THREAD_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#if defined(BOOST_HAS_PTHREADS)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <memory>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <pthread.h>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/system_exception.hpp"
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
extern "C" void* asio_detail_posix_thread_function(void* arg);
|
||||
|
||||
class posix_thread
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
// Constructor.
|
||||
template <typename Function>
|
||||
posix_thread(Function f)
|
||||
: joined_(false)
|
||||
{
|
||||
std::auto_ptr<func_base> arg(new func<Function>(f));
|
||||
int error = ::pthread_create(&thread_, 0,
|
||||
asio_detail_posix_thread_function, arg.get());
|
||||
if (error != 0)
|
||||
{
|
||||
system_exception e("thread", error);
|
||||
boost::throw_exception(e);
|
||||
}
|
||||
arg.release();
|
||||
}
|
||||
|
||||
// Destructor.
|
||||
~posix_thread()
|
||||
{
|
||||
if (!joined_)
|
||||
::pthread_detach(thread_);
|
||||
}
|
||||
|
||||
// Wait for the thread to exit.
|
||||
void join()
|
||||
{
|
||||
if (!joined_)
|
||||
{
|
||||
::pthread_join(thread_, 0);
|
||||
joined_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
friend void* asio_detail_posix_thread_function(void* arg);
|
||||
|
||||
class func_base
|
||||
{
|
||||
public:
|
||||
virtual ~func_base() {}
|
||||
virtual void run() = 0;
|
||||
};
|
||||
|
||||
template <typename Function>
|
||||
class func
|
||||
: public func_base
|
||||
{
|
||||
public:
|
||||
func(Function f)
|
||||
: f_(f)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void run()
|
||||
{
|
||||
f_();
|
||||
}
|
||||
|
||||
private:
|
||||
Function f_;
|
||||
};
|
||||
|
||||
::pthread_t thread_;
|
||||
bool joined_;
|
||||
};
|
||||
|
||||
inline void* asio_detail_posix_thread_function(void* arg)
|
||||
{
|
||||
std::auto_ptr<posix_thread::func_base> f(
|
||||
static_cast<posix_thread::func_base*>(arg));
|
||||
f->run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // defined(BOOST_HAS_PTHREADS)
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_POSIX_THREAD_HPP
|
||||
84
include/libtorrent/asio/detail/posix_tss_ptr.hpp
Normal file
84
include/libtorrent/asio/detail/posix_tss_ptr.hpp
Normal file
@ -0,0 +1,84 @@
|
||||
//
|
||||
// posix_tss_ptr.hpp
|
||||
// ~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_POSIX_TSS_PTR_HPP
|
||||
#define ASIO_DETAIL_POSIX_TSS_PTR_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#if defined(BOOST_HAS_PTHREADS)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <pthread.h>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/system_exception.hpp"
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
class posix_tss_ptr
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
// Constructor.
|
||||
posix_tss_ptr()
|
||||
{
|
||||
int error = ::pthread_key_create(&tss_key_, 0);
|
||||
if (error != 0)
|
||||
{
|
||||
system_exception e("tss", error);
|
||||
boost::throw_exception(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Destructor.
|
||||
~posix_tss_ptr()
|
||||
{
|
||||
::pthread_key_delete(tss_key_);
|
||||
}
|
||||
|
||||
// Get the value.
|
||||
operator T*() const
|
||||
{
|
||||
return static_cast<T*>(::pthread_getspecific(tss_key_));
|
||||
}
|
||||
|
||||
// Set the value.
|
||||
void operator=(T* value)
|
||||
{
|
||||
::pthread_setspecific(tss_key_, value);
|
||||
}
|
||||
|
||||
private:
|
||||
// Thread-specific storage to allow unlocked access to determine whether a
|
||||
// thread is a member of the pool.
|
||||
pthread_key_t tss_key_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // defined(BOOST_HAS_PTHREADS)
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_POSIX_TSS_PTR_HPP
|
||||
106
include/libtorrent/asio/detail/push_options.hpp
Normal file
106
include/libtorrent/asio/detail/push_options.hpp
Normal file
@ -0,0 +1,106 @@
|
||||
//
|
||||
// push_options.hpp
|
||||
// ~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
// No header guard
|
||||
|
||||
#if defined(__COMO__)
|
||||
|
||||
// Comeau C++
|
||||
|
||||
#elif defined(__DMC__)
|
||||
|
||||
// Digital Mars C++
|
||||
|
||||
#elif defined(__INTEL_COMPILER) || defined(__ICL) \
|
||||
|| defined(__ICC) || defined(__ECC)
|
||||
|
||||
// Intel C++
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
// GNU C++
|
||||
|
||||
# if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
# pragma pack (push, 8)
|
||||
# endif
|
||||
|
||||
#elif defined(__KCC)
|
||||
|
||||
// Kai C++
|
||||
|
||||
#elif defined(__sgi)
|
||||
|
||||
// SGI MIPSpro C++
|
||||
|
||||
#elif defined(__DECCXX)
|
||||
|
||||
// Compaq Tru64 Unix cxx
|
||||
|
||||
#elif defined(__ghs)
|
||||
|
||||
// Greenhills C++
|
||||
|
||||
#elif defined(__BORLANDC__)
|
||||
|
||||
// Borland C++
|
||||
|
||||
# pragma option push -a8 -b -Ve- -Vx- -w-inl -vi-
|
||||
# pragma nopushoptwarn
|
||||
# pragma nopackwarning
|
||||
# if !defined(__MT__)
|
||||
# error Multithreaded RTL must be selected.
|
||||
# endif // !defined(__MT__)
|
||||
|
||||
#elif defined(__MWERKS__)
|
||||
|
||||
// Metrowerks CodeWarrior
|
||||
|
||||
#elif defined(__SUNPRO_CC)
|
||||
|
||||
// Sun Workshop Compiler C++
|
||||
|
||||
#elif defined(__HP_aCC)
|
||||
|
||||
// HP aCC
|
||||
|
||||
#elif defined(__MRC__) || defined(__SC__)
|
||||
|
||||
// MPW MrCpp or SCpp
|
||||
|
||||
#elif defined(__IBMCPP__)
|
||||
|
||||
// IBM Visual Age
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
// Microsoft Visual C++
|
||||
//
|
||||
// Must remain the last #elif since some other vendors (Metrowerks, for example)
|
||||
// also #define _MSC_VER
|
||||
|
||||
# pragma warning (disable:4103)
|
||||
# pragma warning (push)
|
||||
# pragma warning (disable:4244)
|
||||
# pragma warning (disable:4355)
|
||||
# pragma warning (disable:4675)
|
||||
# pragma pack (push, 8)
|
||||
// Note that if the /Og optimisation flag is enabled with MSVC6, the compiler
|
||||
// has a tendency to incorrectly optimise away some calls to member template
|
||||
// functions, even though those functions contain code that should not be
|
||||
// optimised away! Therefore we will always disable this optimisation option
|
||||
// for the MSVC6 compiler.
|
||||
# if (_MSC_VER < 1300)
|
||||
# pragma optimize ("g", off)
|
||||
# endif
|
||||
# if !defined(_MT)
|
||||
# error Multithreaded RTL must be selected.
|
||||
# endif // !defined(_MT)
|
||||
|
||||
#endif
|
||||
1640
include/libtorrent/asio/detail/reactive_socket_service.hpp
Normal file
1640
include/libtorrent/asio/detail/reactive_socket_service.hpp
Normal file
File diff suppressed because it is too large
Load Diff
379
include/libtorrent/asio/detail/reactor_op_queue.hpp
Normal file
379
include/libtorrent/asio/detail/reactor_op_queue.hpp
Normal file
@ -0,0 +1,379 @@
|
||||
//
|
||||
// reactor_op_queue.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_REACTOR_OP_QUEUE_HPP
|
||||
#define ASIO_DETAIL_REACTOR_OP_QUEUE_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <memory>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/error.hpp"
|
||||
#include "asio/detail/hash_map.hpp"
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
template <typename Descriptor>
|
||||
class reactor_op_queue
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
// Constructor.
|
||||
reactor_op_queue()
|
||||
: operations_(),
|
||||
cancelled_operations_(0),
|
||||
cleanup_operations_(0)
|
||||
{
|
||||
}
|
||||
|
||||
// Add a new operation to the queue. Returns true if this is the only
|
||||
// operation for the given descriptor, in which case the reactor's event
|
||||
// demultiplexing function call may need to be interrupted and restarted.
|
||||
template <typename Handler>
|
||||
bool enqueue_operation(Descriptor descriptor, Handler handler)
|
||||
{
|
||||
op_base* new_op = new op<Handler>(descriptor, handler);
|
||||
|
||||
typedef typename operation_map::iterator iterator;
|
||||
typedef typename operation_map::value_type value_type;
|
||||
std::pair<iterator, bool> entry =
|
||||
operations_.insert(value_type(descriptor, new_op));
|
||||
if (entry.second)
|
||||
return true;
|
||||
|
||||
op_base* current_op = entry.first->second;
|
||||
while (current_op->next_)
|
||||
current_op = current_op->next_;
|
||||
current_op->next_ = new_op;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cancel all operations associated with the descriptor. Any operations
|
||||
// pending for the descriptor will be notified that they have been cancelled
|
||||
// next time dispatch_cancellations is called. Returns true if any operations
|
||||
// were cancelled, in which case the reactor's event demultiplexing function
|
||||
// may need to be interrupted and restarted.
|
||||
bool cancel_operations(Descriptor descriptor)
|
||||
{
|
||||
typename operation_map::iterator i = operations_.find(descriptor);
|
||||
if (i != operations_.end())
|
||||
{
|
||||
op_base* last_op = i->second;
|
||||
while (last_op->next_)
|
||||
last_op = last_op->next_;
|
||||
last_op->next_ = cancelled_operations_;
|
||||
cancelled_operations_ = i->second;
|
||||
operations_.erase(i);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Whether there are no operations in the queue.
|
||||
bool empty() const
|
||||
{
|
||||
return operations_.empty();
|
||||
}
|
||||
|
||||
// Determine whether there are any operations associated with the descriptor.
|
||||
bool has_operation(Descriptor descriptor) const
|
||||
{
|
||||
return operations_.find(descriptor) != operations_.end();
|
||||
}
|
||||
|
||||
// Dispatch the first operation corresponding to the descriptor. Returns true
|
||||
// if there are more operations queued for the descriptor.
|
||||
bool dispatch_operation(Descriptor descriptor, int result)
|
||||
{
|
||||
typename operation_map::iterator i = operations_.find(descriptor);
|
||||
if (i != operations_.end())
|
||||
{
|
||||
op_base* this_op = i->second;
|
||||
i->second = this_op->next_;
|
||||
this_op->next_ = cleanup_operations_;
|
||||
cleanup_operations_ = this_op;
|
||||
bool done = this_op->invoke(result);
|
||||
if (done)
|
||||
{
|
||||
// Operation has finished.
|
||||
if (i->second)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
operations_.erase(i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Operation wants to be called again. Leave it at the front of the
|
||||
// queue for this descriptor, and remove from the cleanup list.
|
||||
cleanup_operations_ = this_op->next_;
|
||||
this_op->next_ = i->second;
|
||||
i->second = this_op;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Dispatch all operations corresponding to the descriptor.
|
||||
void dispatch_all_operations(Descriptor descriptor, int result)
|
||||
{
|
||||
typename operation_map::iterator i = operations_.find(descriptor);
|
||||
if (i != operations_.end())
|
||||
{
|
||||
while (i->second)
|
||||
{
|
||||
op_base* this_op = i->second;
|
||||
i->second = this_op->next_;
|
||||
this_op->next_ = cleanup_operations_;
|
||||
cleanup_operations_ = this_op;
|
||||
bool done = this_op->invoke(result);
|
||||
if (!done)
|
||||
{
|
||||
// Operation has not finished yet, so leave at front of queue, and
|
||||
// remove from the cleanup list.
|
||||
cleanup_operations_ = this_op->next_;
|
||||
this_op->next_ = i->second;
|
||||
i->second = this_op;
|
||||
return;
|
||||
}
|
||||
operations_.erase(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fill a descriptor set with the descriptors corresponding to each active
|
||||
// operation.
|
||||
template <typename Descriptor_Set>
|
||||
void get_descriptors(Descriptor_Set& descriptors)
|
||||
{
|
||||
typename operation_map::iterator i = operations_.begin();
|
||||
while (i != operations_.end())
|
||||
{
|
||||
descriptors.set(i->first);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch the operations corresponding to the ready file descriptors
|
||||
// contained in the given descriptor set.
|
||||
template <typename Descriptor_Set>
|
||||
void dispatch_descriptors(const Descriptor_Set& descriptors, int result)
|
||||
{
|
||||
typename operation_map::iterator i = operations_.begin();
|
||||
while (i != operations_.end())
|
||||
{
|
||||
typename operation_map::iterator op_iter = i++;
|
||||
if (descriptors.is_set(op_iter->first))
|
||||
{
|
||||
op_base* this_op = op_iter->second;
|
||||
op_iter->second = this_op->next_;
|
||||
this_op->next_ = cleanup_operations_;
|
||||
cleanup_operations_ = this_op;
|
||||
bool done = this_op->invoke(result);
|
||||
if (done)
|
||||
{
|
||||
if (!op_iter->second)
|
||||
operations_.erase(op_iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Operation has not finished yet, so leave at front of queue, and
|
||||
// remove from the cleanup list.
|
||||
cleanup_operations_ = this_op->next_;
|
||||
this_op->next_ = op_iter->second;
|
||||
op_iter->second = this_op;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch any pending cancels for operations.
|
||||
void dispatch_cancellations()
|
||||
{
|
||||
while (cancelled_operations_)
|
||||
{
|
||||
op_base* this_op = cancelled_operations_;
|
||||
cancelled_operations_ = this_op->next_;
|
||||
this_op->next_ = cleanup_operations_;
|
||||
cleanup_operations_ = this_op;
|
||||
this_op->invoke(asio::error::operation_aborted);
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy operations that are waiting to be cleaned up.
|
||||
void cleanup_operations()
|
||||
{
|
||||
while (cleanup_operations_)
|
||||
{
|
||||
op_base* next_op = cleanup_operations_->next_;
|
||||
cleanup_operations_->next_ = 0;
|
||||
cleanup_operations_->destroy();
|
||||
cleanup_operations_ = next_op;
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy all operations owned by the queue.
|
||||
void destroy_operations()
|
||||
{
|
||||
while (cancelled_operations_)
|
||||
{
|
||||
op_base* next_op = cancelled_operations_->next_;
|
||||
cancelled_operations_->next_ = 0;
|
||||
cancelled_operations_->destroy();
|
||||
cancelled_operations_ = next_op;
|
||||
}
|
||||
|
||||
while (cleanup_operations_)
|
||||
{
|
||||
op_base* next_op = cleanup_operations_->next_;
|
||||
cleanup_operations_->next_ = 0;
|
||||
cleanup_operations_->destroy();
|
||||
cleanup_operations_ = next_op;
|
||||
}
|
||||
|
||||
typename operation_map::iterator i = operations_.begin();
|
||||
while (i != operations_.end())
|
||||
{
|
||||
typename operation_map::iterator op_iter = i++;
|
||||
op_base* curr_op = op_iter->second;
|
||||
operations_.erase(op_iter);
|
||||
while (curr_op)
|
||||
{
|
||||
op_base* next_op = curr_op->next_;
|
||||
curr_op->next_ = 0;
|
||||
curr_op->destroy();
|
||||
curr_op = next_op;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Base class for reactor operations. A function pointer is used instead of
|
||||
// virtual functions to avoid the associated overhead.
|
||||
class op_base
|
||||
{
|
||||
public:
|
||||
// Get the descriptor associated with the operation.
|
||||
Descriptor descriptor() const
|
||||
{
|
||||
return descriptor_;
|
||||
}
|
||||
|
||||
// Perform the operation.
|
||||
bool invoke(int result)
|
||||
{
|
||||
return invoke_func_(this, result);
|
||||
}
|
||||
|
||||
// Destroy the operation.
|
||||
void destroy()
|
||||
{
|
||||
return destroy_func_(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
typedef bool (*invoke_func_type)(op_base*, int);
|
||||
typedef void (*destroy_func_type)(op_base*);
|
||||
|
||||
// Construct an operation for the given descriptor.
|
||||
op_base(invoke_func_type invoke_func,
|
||||
destroy_func_type destroy_func, Descriptor descriptor)
|
||||
: invoke_func_(invoke_func),
|
||||
destroy_func_(destroy_func),
|
||||
descriptor_(descriptor),
|
||||
next_(0)
|
||||
{
|
||||
}
|
||||
|
||||
// Prevent deletion through this type.
|
||||
~op_base()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
friend class reactor_op_queue<Descriptor>;
|
||||
|
||||
// The function to be called to dispatch the handler.
|
||||
invoke_func_type invoke_func_;
|
||||
|
||||
// The function to be called to delete the handler.
|
||||
destroy_func_type destroy_func_;
|
||||
|
||||
// The descriptor associated with the operation.
|
||||
Descriptor descriptor_;
|
||||
|
||||
// The next operation for the same file descriptor.
|
||||
op_base* next_;
|
||||
};
|
||||
|
||||
// Adaptor class template for using handlers in operations.
|
||||
template <typename Handler>
|
||||
class op
|
||||
: public op_base
|
||||
{
|
||||
public:
|
||||
// Constructor.
|
||||
op(Descriptor descriptor, Handler handler)
|
||||
: op_base(&op<Handler>::invoke_handler,
|
||||
&op<Handler>::destroy_handler, descriptor),
|
||||
handler_(handler)
|
||||
{
|
||||
}
|
||||
|
||||
// Invoke the handler.
|
||||
static bool invoke_handler(op_base* base, int result)
|
||||
{
|
||||
return static_cast<op<Handler>*>(base)->handler_(result);
|
||||
}
|
||||
|
||||
// Delete the handler.
|
||||
static void destroy_handler(op_base* base)
|
||||
{
|
||||
delete static_cast<op<Handler>*>(base);
|
||||
}
|
||||
|
||||
private:
|
||||
Handler handler_;
|
||||
};
|
||||
|
||||
// The type for a map of operations.
|
||||
typedef hash_map<Descriptor, op_base*> operation_map;
|
||||
|
||||
// The operations that are currently executing asynchronously.
|
||||
operation_map operations_;
|
||||
|
||||
// The list of operations that have been cancelled.
|
||||
op_base* cancelled_operations_;
|
||||
|
||||
// The list of operations to be destroyed.
|
||||
op_base* cleanup_operations_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_REACTOR_OP_QUEUE_HPP
|
||||
361
include/libtorrent/asio/detail/resolver_service.hpp
Normal file
361
include/libtorrent/asio/detail/resolver_service.hpp
Normal file
@ -0,0 +1,361 @@
|
||||
//
|
||||
// resolver_service.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_RESOLVER_SERVICE_HPP
|
||||
#define ASIO_DETAIL_RESOLVER_SERVICE_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstring>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/weak_ptr.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/error.hpp"
|
||||
#include "asio/io_service.hpp"
|
||||
#include "asio/detail/bind_handler.hpp"
|
||||
#include "asio/detail/mutex.hpp"
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
#include "asio/detail/socket_ops.hpp"
|
||||
#include "asio/detail/socket_types.hpp"
|
||||
#include "asio/detail/thread.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
template <typename Protocol>
|
||||
class resolver_service
|
||||
: public asio::io_service::service
|
||||
{
|
||||
private:
|
||||
// Helper class to perform exception-safe cleanup of addrinfo objects.
|
||||
class auto_addrinfo
|
||||
: private asio::detail::noncopyable
|
||||
{
|
||||
public:
|
||||
explicit auto_addrinfo(asio::detail::addrinfo_type* ai)
|
||||
: ai_(ai)
|
||||
{
|
||||
}
|
||||
|
||||
~auto_addrinfo()
|
||||
{
|
||||
if (ai_)
|
||||
socket_ops::freeaddrinfo(ai_);
|
||||
}
|
||||
|
||||
operator asio::detail::addrinfo_type*()
|
||||
{
|
||||
return ai_;
|
||||
}
|
||||
|
||||
private:
|
||||
asio::detail::addrinfo_type* ai_;
|
||||
};
|
||||
|
||||
public:
|
||||
// The implementation type of the resolver. The shared pointer is used as a
|
||||
// cancellation token to indicate to the background thread that the operation
|
||||
// has been cancelled.
|
||||
typedef boost::shared_ptr<void> implementation_type;
|
||||
struct noop_deleter { void operator()(void*) {} };
|
||||
|
||||
// The endpoint type.
|
||||
typedef typename Protocol::endpoint endpoint_type;
|
||||
|
||||
// The query type.
|
||||
typedef typename Protocol::resolver_query query_type;
|
||||
|
||||
// The iterator type.
|
||||
typedef typename Protocol::resolver_iterator iterator_type;
|
||||
|
||||
// Constructor.
|
||||
resolver_service(asio::io_service& io_service)
|
||||
: asio::io_service::service(io_service),
|
||||
mutex_(),
|
||||
work_io_service_(new asio::io_service),
|
||||
work_(new asio::io_service::work(*work_io_service_)),
|
||||
work_thread_(0)
|
||||
{
|
||||
}
|
||||
|
||||
// Destructor.
|
||||
~resolver_service()
|
||||
{
|
||||
shutdown_service();
|
||||
}
|
||||
|
||||
// Destroy all user-defined handler objects owned by the service.
|
||||
void shutdown_service()
|
||||
{
|
||||
work_.reset();
|
||||
if (work_io_service_)
|
||||
{
|
||||
work_io_service_->interrupt();
|
||||
if (work_thread_)
|
||||
{
|
||||
work_thread_->join();
|
||||
work_thread_.reset();
|
||||
}
|
||||
work_io_service_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Construct a new resolver implementation.
|
||||
void construct(implementation_type& impl)
|
||||
{
|
||||
impl.reset(static_cast<void*>(0), noop_deleter());
|
||||
}
|
||||
|
||||
// Destroy a resolver implementation.
|
||||
void destroy(implementation_type&)
|
||||
{
|
||||
}
|
||||
|
||||
// Cancel pending asynchronous operations.
|
||||
void cancel(implementation_type& impl)
|
||||
{
|
||||
impl.reset(static_cast<void*>(0), noop_deleter());
|
||||
}
|
||||
|
||||
// Resolve a query to a list of entries.
|
||||
template <typename Error_Handler>
|
||||
iterator_type resolve(implementation_type&, const query_type& query,
|
||||
Error_Handler error_handler)
|
||||
{
|
||||
asio::detail::addrinfo_type* address_info = 0;
|
||||
std::string host_name = query.host_name();
|
||||
std::string service_name = query.service_name();
|
||||
asio::detail::addrinfo_type hints = query.hints();
|
||||
|
||||
int result = socket_ops::getaddrinfo(
|
||||
host_name.length() ? host_name.c_str() : 0,
|
||||
service_name.c_str(), &hints, &address_info);
|
||||
auto_addrinfo auto_address_info(address_info);
|
||||
|
||||
error_handler(asio::error(result));
|
||||
if (result != 0)
|
||||
return iterator_type();
|
||||
return iterator_type::create(address_info, host_name, service_name);
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
class resolve_query_handler
|
||||
{
|
||||
public:
|
||||
resolve_query_handler(implementation_type impl, const query_type& query,
|
||||
asio::io_service& io_service, Handler handler)
|
||||
: impl_(impl),
|
||||
query_(query),
|
||||
io_service_(io_service),
|
||||
work_(io_service),
|
||||
handler_(handler)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
// Check if the operation has been cancelled.
|
||||
if (impl_.expired())
|
||||
{
|
||||
iterator_type iterator;
|
||||
io_service_.post(asio::detail::bind_handler(handler_,
|
||||
asio::error(asio::error::operation_aborted),
|
||||
iterator));
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform the blocking host resolution operation.
|
||||
asio::detail::addrinfo_type* address_info = 0;
|
||||
std::string host_name = query_.host_name();
|
||||
std::string service_name = query_.service_name();
|
||||
asio::detail::addrinfo_type hints = query_.hints();
|
||||
int result = socket_ops::getaddrinfo(
|
||||
host_name.length() ? host_name.c_str() : 0,
|
||||
service_name.c_str(), &hints, &address_info);
|
||||
auto_addrinfo auto_address_info(address_info);
|
||||
|
||||
// Invoke the handler and pass the result.
|
||||
asio::error e(result);
|
||||
iterator_type iterator;
|
||||
if (result == 0)
|
||||
iterator = iterator_type::create(address_info, host_name, service_name);
|
||||
io_service_.post(asio::detail::bind_handler(
|
||||
handler_, e, iterator));
|
||||
}
|
||||
|
||||
private:
|
||||
boost::weak_ptr<void> impl_;
|
||||
query_type query_;
|
||||
asio::io_service& io_service_;
|
||||
asio::io_service::work work_;
|
||||
Handler handler_;
|
||||
};
|
||||
|
||||
// Asynchronously resolve a query to a list of entries.
|
||||
template <typename Handler>
|
||||
void async_resolve(implementation_type& impl, const query_type& query,
|
||||
Handler handler)
|
||||
{
|
||||
if (work_io_service_)
|
||||
{
|
||||
start_work_thread();
|
||||
work_io_service_->post(
|
||||
resolve_query_handler<Handler>(
|
||||
impl, query, io_service(), handler));
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve an endpoint to a list of entries.
|
||||
template <typename Error_Handler>
|
||||
iterator_type resolve(implementation_type&,
|
||||
const endpoint_type& endpoint, Error_Handler error_handler)
|
||||
{
|
||||
// First try resolving with the service name. If that fails try resolving
|
||||
// but allow the service to be returned as a number.
|
||||
char host_name[NI_MAXHOST];
|
||||
char service_name[NI_MAXSERV];
|
||||
int flags = endpoint.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0;
|
||||
int result = socket_ops::getnameinfo(endpoint.data(), endpoint.size(),
|
||||
host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags);
|
||||
if (result)
|
||||
{
|
||||
flags |= NI_NUMERICSERV;
|
||||
result = socket_ops::getnameinfo(endpoint.data(), endpoint.size(),
|
||||
host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags);
|
||||
}
|
||||
|
||||
error_handler(asio::error(result));
|
||||
if (result != 0)
|
||||
return iterator_type();
|
||||
return iterator_type::create(endpoint, host_name, service_name);
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
class resolve_endpoint_handler
|
||||
{
|
||||
public:
|
||||
resolve_endpoint_handler(implementation_type impl,
|
||||
const endpoint_type& endpoint, asio::io_service& io_service,
|
||||
Handler handler)
|
||||
: impl_(impl),
|
||||
endpoint_(endpoint),
|
||||
io_service_(io_service),
|
||||
work_(io_service),
|
||||
handler_(handler)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
// Check if the operation has been cancelled.
|
||||
if (impl_.expired())
|
||||
{
|
||||
iterator_type iterator;
|
||||
io_service_.post(asio::detail::bind_handler(handler_,
|
||||
asio::error(asio::error::operation_aborted),
|
||||
iterator));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// First try resolving with the service name. If that fails try resolving
|
||||
// but allow the service to be returned as a number.
|
||||
char host_name[NI_MAXHOST];
|
||||
char service_name[NI_MAXSERV];
|
||||
int flags = endpoint_.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0;
|
||||
int result = socket_ops::getnameinfo(endpoint_.data(), endpoint_.size(),
|
||||
host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags);
|
||||
if (result)
|
||||
{
|
||||
flags |= NI_NUMERICSERV;
|
||||
result = socket_ops::getnameinfo(endpoint_.data(), endpoint_.size(),
|
||||
host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags);
|
||||
}
|
||||
|
||||
// Invoke the handler and pass the result.
|
||||
asio::error e(result);
|
||||
iterator_type iterator;
|
||||
if (result == 0)
|
||||
iterator = iterator_type::create(endpoint_, host_name, service_name);
|
||||
io_service_.post(asio::detail::bind_handler(
|
||||
handler_, e, iterator));
|
||||
}
|
||||
|
||||
private:
|
||||
boost::weak_ptr<void> impl_;
|
||||
endpoint_type endpoint_;
|
||||
asio::io_service& io_service_;
|
||||
asio::io_service::work work_;
|
||||
Handler handler_;
|
||||
};
|
||||
|
||||
// Asynchronously resolve an endpoint to a list of entries.
|
||||
template <typename Handler>
|
||||
void async_resolve(implementation_type& impl, const endpoint_type& endpoint,
|
||||
Handler handler)
|
||||
{
|
||||
if (work_io_service_)
|
||||
{
|
||||
start_work_thread();
|
||||
work_io_service_->post(
|
||||
resolve_endpoint_handler<Handler>(
|
||||
impl, endpoint, io_service(), handler));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Helper class to run the work io_service in a thread.
|
||||
class work_io_service_runner
|
||||
{
|
||||
public:
|
||||
work_io_service_runner(asio::io_service& io_service)
|
||||
: io_service_(io_service) {}
|
||||
void operator()() { io_service_.run(); }
|
||||
private:
|
||||
asio::io_service& io_service_;
|
||||
};
|
||||
|
||||
// Start the work thread if it's not already running.
|
||||
void start_work_thread()
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
if (work_thread_ == 0)
|
||||
{
|
||||
work_thread_.reset(new asio::detail::thread(
|
||||
work_io_service_runner(*work_io_service_)));
|
||||
}
|
||||
}
|
||||
|
||||
// Mutex to protect access to internal data.
|
||||
asio::detail::mutex mutex_;
|
||||
|
||||
// Private io_service used for performing asynchronous host resolution.
|
||||
boost::scoped_ptr<asio::io_service> work_io_service_;
|
||||
|
||||
// Work for the private io_service to perform.
|
||||
boost::scoped_ptr<asio::io_service::work> work_;
|
||||
|
||||
// Thread used for running the work io_service's run loop.
|
||||
boost::scoped_ptr<asio::detail::thread> work_thread_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_RESOLVER_SERVICE_HPP
|
||||
79
include/libtorrent/asio/detail/scoped_lock.hpp
Normal file
79
include/libtorrent/asio/detail/scoped_lock.hpp
Normal file
@ -0,0 +1,79 @@
|
||||
//
|
||||
// scoped_lock.hpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_SCOPED_LOCK_HPP
|
||||
#define ASIO_DETAIL_SCOPED_LOCK_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
// Helper class to lock and unlock a mutex automatically.
|
||||
template <typename Mutex>
|
||||
class scoped_lock
|
||||
: private noncopyable
|
||||
{
|
||||
public:
|
||||
// Constructor acquires the lock.
|
||||
scoped_lock(Mutex& m)
|
||||
: mutex_(m)
|
||||
{
|
||||
mutex_.lock();
|
||||
locked_ = true;
|
||||
}
|
||||
|
||||
// Destructor releases the lock.
|
||||
~scoped_lock()
|
||||
{
|
||||
if (locked_)
|
||||
mutex_.unlock();
|
||||
}
|
||||
|
||||
// Explicitly acquire the lock.
|
||||
void lock()
|
||||
{
|
||||
if (!locked_)
|
||||
{
|
||||
mutex_.lock();
|
||||
locked_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Explicitly release the lock.
|
||||
void unlock()
|
||||
{
|
||||
if (locked_)
|
||||
{
|
||||
mutex_.unlock();
|
||||
locked_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// The underlying mutex.
|
||||
Mutex& mutex_;
|
||||
|
||||
// Whether the mutex is currently locked or unlocked.
|
||||
bool locked_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_SCOPED_LOCK_HPP
|
||||
41
include/libtorrent/asio/detail/select_interrupter.hpp
Normal file
41
include/libtorrent/asio/detail/select_interrupter.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// select_interrupter.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_SELECT_INTERRUPTER_HPP
|
||||
#define ASIO_DETAIL_SELECT_INTERRUPTER_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/detail/pipe_select_interrupter.hpp"
|
||||
#include "asio/detail/socket_select_interrupter.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
typedef socket_select_interrupter select_interrupter;
|
||||
#else
|
||||
typedef pipe_select_interrupter select_interrupter;
|
||||
#endif
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_SELECT_INTERRUPTER_HPP
|
||||
435
include/libtorrent/asio/detail/select_reactor.hpp
Normal file
435
include/libtorrent/asio/detail/select_reactor.hpp
Normal file
@ -0,0 +1,435 @@
|
||||
//
|
||||
// select_reactor.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ASIO_DETAIL_SELECT_REACTOR_HPP
|
||||
#define ASIO_DETAIL_SELECT_REACTOR_HPP
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/socket_types.hpp" // Must come before posix_time.
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstddef>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include <vector>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#include "asio/io_service.hpp"
|
||||
#include "asio/detail/bind_handler.hpp"
|
||||
#include "asio/detail/fd_set_adapter.hpp"
|
||||
#include "asio/detail/mutex.hpp"
|
||||
#include "asio/detail/noncopyable.hpp"
|
||||
#include "asio/detail/task_io_service.hpp"
|
||||
#include "asio/detail/thread.hpp"
|
||||
#include "asio/detail/reactor_op_queue.hpp"
|
||||
#include "asio/detail/select_interrupter.hpp"
|
||||
#include "asio/detail/select_reactor_fwd.hpp"
|
||||
#include "asio/detail/signal_blocker.hpp"
|
||||
#include "asio/detail/socket_ops.hpp"
|
||||
#include "asio/detail/socket_types.hpp"
|
||||
#include "asio/detail/timer_queue.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
template <bool Own_Thread>
|
||||
class select_reactor
|
||||
: public asio::io_service::service
|
||||
{
|
||||
public:
|
||||
// Constructor.
|
||||
select_reactor(asio::io_service& io_service)
|
||||
: asio::io_service::service(io_service),
|
||||
mutex_(),
|
||||
select_in_progress_(false),
|
||||
interrupter_(),
|
||||
read_op_queue_(),
|
||||
write_op_queue_(),
|
||||
except_op_queue_(),
|
||||
pending_cancellations_(),
|
||||
stop_thread_(false),
|
||||
thread_(0),
|
||||
shutdown_(false)
|
||||
{
|
||||
if (Own_Thread)
|
||||
{
|
||||
asio::detail::signal_blocker sb;
|
||||
thread_ = new asio::detail::thread(
|
||||
bind_handler(&select_reactor::call_run_thread, this));
|
||||
}
|
||||
}
|
||||
|
||||
// Destructor.
|
||||
~select_reactor()
|
||||
{
|
||||
shutdown_service();
|
||||
}
|
||||
|
||||
// Destroy all user-defined handler objects owned by the service.
|
||||
void shutdown_service()
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
shutdown_ = true;
|
||||
stop_thread_ = true;
|
||||
lock.unlock();
|
||||
|
||||
if (thread_)
|
||||
{
|
||||
interrupter_.interrupt();
|
||||
thread_->join();
|
||||
delete thread_;
|
||||
thread_ = 0;
|
||||
}
|
||||
|
||||
read_op_queue_.destroy_operations();
|
||||
write_op_queue_.destroy_operations();
|
||||
except_op_queue_.destroy_operations();
|
||||
|
||||
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
|
||||
timer_queues_[i]->destroy_timers();
|
||||
timer_queues_.clear();
|
||||
}
|
||||
|
||||
// Register a socket with the reactor. Returns 0 on success, system error
|
||||
// code on failure.
|
||||
int register_descriptor(socket_type descriptor)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Start a new read operation. The handler object will be invoked when the
|
||||
// given descriptor is ready to be read, or an error has occurred.
|
||||
template <typename Handler>
|
||||
void start_read_op(socket_type descriptor, Handler handler)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
if (!shutdown_)
|
||||
if (read_op_queue_.enqueue_operation(descriptor, handler))
|
||||
interrupter_.interrupt();
|
||||
}
|
||||
|
||||
// Start a new write operation. The handler object will be invoked when the
|
||||
// given descriptor is ready to be written, or an error has occurred.
|
||||
template <typename Handler>
|
||||
void start_write_op(socket_type descriptor, Handler handler)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
if (!shutdown_)
|
||||
if (write_op_queue_.enqueue_operation(descriptor, handler))
|
||||
interrupter_.interrupt();
|
||||
}
|
||||
|
||||
// Start a new exception operation. The handler object will be invoked when
|
||||
// the given descriptor has exception information, or an error has occurred.
|
||||
template <typename Handler>
|
||||
void start_except_op(socket_type descriptor, Handler handler)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
if (!shutdown_)
|
||||
if (except_op_queue_.enqueue_operation(descriptor, handler))
|
||||
interrupter_.interrupt();
|
||||
}
|
||||
|
||||
// Start new write and exception operations. The handler object will be
|
||||
// invoked when the given descriptor is ready for writing or has exception
|
||||
// information available, or an error has occurred.
|
||||
template <typename Handler>
|
||||
void start_write_and_except_ops(socket_type descriptor, Handler handler)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
if (!shutdown_)
|
||||
{
|
||||
bool interrupt = write_op_queue_.enqueue_operation(descriptor, handler);
|
||||
interrupt = except_op_queue_.enqueue_operation(descriptor, handler)
|
||||
|| interrupt;
|
||||
if (interrupt)
|
||||
interrupter_.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel all operations associated with the given descriptor. The
|
||||
// handlers associated with the descriptor will be invoked with the
|
||||
// operation_aborted error.
|
||||
void cancel_ops(socket_type descriptor)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
cancel_ops_unlocked(descriptor);
|
||||
}
|
||||
|
||||
// Enqueue cancellation of all operations associated with the given
|
||||
// descriptor. The handlers associated with the descriptor will be invoked
|
||||
// with the operation_aborted error. This function does not acquire the
|
||||
// select_reactor's mutex, and so should only be used from within a reactor
|
||||
// handler.
|
||||
void enqueue_cancel_ops_unlocked(socket_type descriptor)
|
||||
{
|
||||
pending_cancellations_.push_back(descriptor);
|
||||
}
|
||||
|
||||
// Cancel any operations that are running against the descriptor and remove
|
||||
// its registration from the reactor.
|
||||
void close_descriptor(socket_type descriptor)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
cancel_ops_unlocked(descriptor);
|
||||
}
|
||||
|
||||
// Add a new timer queue to the reactor.
|
||||
template <typename Time_Traits>
|
||||
void add_timer_queue(timer_queue<Time_Traits>& timer_queue)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
timer_queues_.push_back(&timer_queue);
|
||||
}
|
||||
|
||||
// Schedule a timer in the given timer queue to expire at the specified
|
||||
// absolute time. The handler object will be invoked when the timer expires.
|
||||
template <typename Time_Traits, typename Handler>
|
||||
void schedule_timer(timer_queue<Time_Traits>& timer_queue,
|
||||
const typename Time_Traits::time_type& time, Handler handler, void* token)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
if (!shutdown_)
|
||||
if (timer_queue.enqueue_timer(time, handler, token))
|
||||
interrupter_.interrupt();
|
||||
}
|
||||
|
||||
// Cancel the timer associated with the given token. Returns the number of
|
||||
// handlers that have been posted or dispatched.
|
||||
template <typename Time_Traits>
|
||||
std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
return timer_queue.cancel_timer(token);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class task_io_service<select_reactor<Own_Thread> >;
|
||||
|
||||
// Run select once until interrupted or events are ready to be dispatched.
|
||||
void run(bool block)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
|
||||
// Dispatch any operation cancellations that were made while the select
|
||||
// loop was not running.
|
||||
read_op_queue_.dispatch_cancellations();
|
||||
write_op_queue_.dispatch_cancellations();
|
||||
except_op_queue_.dispatch_cancellations();
|
||||
|
||||
// Check if the thread is supposed to stop.
|
||||
if (stop_thread_)
|
||||
{
|
||||
// Clean up operations. We must not hold the lock since the operations may
|
||||
// make calls back into this reactor.
|
||||
lock.unlock();
|
||||
read_op_queue_.cleanup_operations();
|
||||
write_op_queue_.cleanup_operations();
|
||||
except_op_queue_.cleanup_operations();
|
||||
return;
|
||||
}
|
||||
|
||||
// We can return immediately if there's no work to do and the reactor is
|
||||
// not supposed to block.
|
||||
if (!block && read_op_queue_.empty() && write_op_queue_.empty()
|
||||
&& except_op_queue_.empty() && all_timer_queues_are_empty())
|
||||
{
|
||||
// Clean up operations. We must not hold the lock since the operations may
|
||||
// make calls back into this reactor.
|
||||
lock.unlock();
|
||||
read_op_queue_.cleanup_operations();
|
||||
write_op_queue_.cleanup_operations();
|
||||
except_op_queue_.cleanup_operations();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up the descriptor sets.
|
||||
fd_set_adapter read_fds;
|
||||
read_fds.set(interrupter_.read_descriptor());
|
||||
read_op_queue_.get_descriptors(read_fds);
|
||||
fd_set_adapter write_fds;
|
||||
write_op_queue_.get_descriptors(write_fds);
|
||||
fd_set_adapter except_fds;
|
||||
except_op_queue_.get_descriptors(except_fds);
|
||||
socket_type max_fd = read_fds.max_descriptor();
|
||||
if (write_fds.max_descriptor() > max_fd)
|
||||
max_fd = write_fds.max_descriptor();
|
||||
if (except_fds.max_descriptor() > max_fd)
|
||||
max_fd = except_fds.max_descriptor();
|
||||
|
||||
// Block on the select call without holding the lock so that new
|
||||
// operations can be started while the call is executing.
|
||||
timeval tv_buf = { 0, 0 };
|
||||
timeval* tv = block ? get_timeout(tv_buf) : &tv_buf;
|
||||
select_in_progress_ = true;
|
||||
lock.unlock();
|
||||
int retval = socket_ops::select(static_cast<int>(max_fd + 1),
|
||||
read_fds, write_fds, except_fds, tv);
|
||||
lock.lock();
|
||||
select_in_progress_ = false;
|
||||
|
||||
// Block signals while dispatching operations.
|
||||
asio::detail::signal_blocker sb;
|
||||
|
||||
// Reset the interrupter.
|
||||
if (retval > 0 && read_fds.is_set(interrupter_.read_descriptor()))
|
||||
interrupter_.reset();
|
||||
|
||||
// Dispatch all ready operations.
|
||||
if (retval > 0)
|
||||
{
|
||||
// Exception operations must be processed first to ensure that any
|
||||
// out-of-band data is read before normal data.
|
||||
except_op_queue_.dispatch_descriptors(except_fds, 0);
|
||||
read_op_queue_.dispatch_descriptors(read_fds, 0);
|
||||
write_op_queue_.dispatch_descriptors(write_fds, 0);
|
||||
except_op_queue_.dispatch_cancellations();
|
||||
read_op_queue_.dispatch_cancellations();
|
||||
write_op_queue_.dispatch_cancellations();
|
||||
}
|
||||
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
|
||||
timer_queues_[i]->dispatch_timers();
|
||||
|
||||
// Issue any pending cancellations.
|
||||
for (size_t i = 0; i < pending_cancellations_.size(); ++i)
|
||||
cancel_ops_unlocked(pending_cancellations_[i]);
|
||||
pending_cancellations_.clear();
|
||||
|
||||
// Clean up operations. We must not hold the lock since the operations may
|
||||
// make calls back into this reactor.
|
||||
lock.unlock();
|
||||
read_op_queue_.cleanup_operations();
|
||||
write_op_queue_.cleanup_operations();
|
||||
except_op_queue_.cleanup_operations();
|
||||
}
|
||||
|
||||
// Run the select loop in the thread.
|
||||
void run_thread()
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
while (!stop_thread_)
|
||||
{
|
||||
lock.unlock();
|
||||
run(true);
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
// Entry point for the select loop thread.
|
||||
static void call_run_thread(select_reactor* reactor)
|
||||
{
|
||||
reactor->run_thread();
|
||||
}
|
||||
|
||||
// Interrupt the select loop.
|
||||
void interrupt()
|
||||
{
|
||||
interrupter_.interrupt();
|
||||
}
|
||||
|
||||
// Check if all timer queues are empty.
|
||||
bool all_timer_queues_are_empty() const
|
||||
{
|
||||
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
|
||||
if (!timer_queues_[i]->empty())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the timeout value for the select call.
|
||||
timeval* get_timeout(timeval& tv)
|
||||
{
|
||||
if (all_timer_queues_are_empty())
|
||||
return 0;
|
||||
|
||||
// By default we will wait no longer than 5 minutes. This will ensure that
|
||||
// any changes to the system clock are detected after no longer than this.
|
||||
boost::posix_time::time_duration minimum_wait_duration
|
||||
= boost::posix_time::minutes(5);
|
||||
|
||||
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
|
||||
{
|
||||
boost::posix_time::time_duration wait_duration
|
||||
= timer_queues_[i]->wait_duration();
|
||||
if (wait_duration < minimum_wait_duration)
|
||||
minimum_wait_duration = wait_duration;
|
||||
}
|
||||
|
||||
if (minimum_wait_duration > boost::posix_time::time_duration())
|
||||
{
|
||||
tv.tv_sec = minimum_wait_duration.total_seconds();
|
||||
tv.tv_usec = minimum_wait_duration.total_microseconds() % 1000000;
|
||||
}
|
||||
else
|
||||
{
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
}
|
||||
|
||||
return &tv;
|
||||
}
|
||||
|
||||
// Cancel all operations associated with the given descriptor. The do_cancel
|
||||
// function of the handler objects will be invoked. This function does not
|
||||
// acquire the select_reactor's mutex.
|
||||
void cancel_ops_unlocked(socket_type descriptor)
|
||||
{
|
||||
bool interrupt = read_op_queue_.cancel_operations(descriptor);
|
||||
interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt;
|
||||
interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt;
|
||||
if (interrupt)
|
||||
interrupter_.interrupt();
|
||||
}
|
||||
|
||||
// Mutex to protect access to internal data.
|
||||
asio::detail::mutex mutex_;
|
||||
|
||||
// Whether the select loop is currently running or not.
|
||||
bool select_in_progress_;
|
||||
|
||||
// The interrupter is used to break a blocking select call.
|
||||
select_interrupter interrupter_;
|
||||
|
||||
// The queue of read operations.
|
||||
reactor_op_queue<socket_type> read_op_queue_;
|
||||
|
||||
// The queue of write operations.
|
||||
reactor_op_queue<socket_type> write_op_queue_;
|
||||
|
||||
// The queue of exception operations.
|
||||
reactor_op_queue<socket_type> except_op_queue_;
|
||||
|
||||
// The timer queues.
|
||||
std::vector<timer_queue_base*> timer_queues_;
|
||||
|
||||
// The descriptors that are pending cancellation.
|
||||
std::vector<socket_type> pending_cancellations_;
|
||||
|
||||
// Does the reactor loop thread need to stop.
|
||||
bool stop_thread_;
|
||||
|
||||
// The thread that is running the reactor loop.
|
||||
asio::detail::thread* thread_;
|
||||
|
||||
// Whether the service has been shut down.
|
||||
bool shutdown_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#endif // ASIO_DETAIL_SELECT_REACTOR_HPP
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user