UPnP seems to work

This commit is contained in:
Arvid Norberg 2007-04-01 15:39:08 +00:00
parent 3d2a4e00b9
commit 2f9611a1a9
4 changed files with 120 additions and 23 deletions

View File

@ -64,6 +64,7 @@ struct http_connection : boost::enable_shared_from_this<http_connection>, boost:
, m_handler(handler)
, m_timer(ios)
, m_bottled(bottled)
, m_called(false)
{
assert(!m_handler.empty());
}
@ -85,7 +86,8 @@ private:
/* , tcp::resolver::iterator i*/);
void on_write(asio::error_code const& e);
void on_read(asio::error_code const& e, std::size_t bytes_transferred);
void on_timeout(asio::error_code const& e);
static void on_timeout(boost::weak_ptr<http_connection> p
, asio::error_code const& e);
std::vector<char> m_recvbuffer;
tcp::socket m_sock;
@ -100,6 +102,8 @@ private:
// non bottled means that once the headers have been
// received, data is streamed to the handler
bool m_bottled;
// set to true the first time the handler is called
bool m_called;
std::string m_hostname;
std::string m_port;
};

View File

@ -132,6 +132,7 @@ private:
mapping[0].protocol = 0;
mapping[1].protocol = 1;
}
// the interface url, through which the list of
// supported interfaces are fetched
std::string url;

View File

@ -57,7 +57,9 @@ void http_connection::start(std::string const& hostname, std::string const& port
{
m_timeout = timeout;
m_timer.expires_from_now(m_timeout);
m_timer.async_wait(bind(&http_connection::on_timeout, shared_from_this(), _1));
m_timer.async_wait(bind(&http_connection::on_timeout
, boost::weak_ptr<http_connection>(shared_from_this()), _1));
m_called = false;
if (m_sock.is_open() && m_hostname == hostname && m_port == port)
{
m_parser.reset();
@ -75,10 +77,15 @@ void http_connection::start(std::string const& hostname, std::string const& port
}
}
void http_connection::on_timeout(asio::error_code const& e)
void http_connection::on_timeout(boost::weak_ptr<http_connection> p
, asio::error_code const& e)
{
if (e == asio::error::operation_aborted) return;
m_handler(asio::error::timed_out, m_parser, 0, 0);
boost::shared_ptr<http_connection> c = p.lock();
if (!c) return;
if (c->m_bottled && c->m_called) return;
c->m_called = true;
c->m_handler(asio::error::timed_out, c->m_parser, 0, 0);
}
void http_connection::close()
@ -95,6 +102,8 @@ void http_connection::on_resolve(asio::error_code const& e
if (e)
{
close();
if (m_bottled && m_called) return;
m_called = true;
m_handler(e, m_parser, 0, 0);
return;
}
@ -123,6 +132,8 @@ void http_connection::on_connect(asio::error_code const& e
*/ else
{
close();
if (m_bottled && m_called) return;
m_called = true;
m_handler(e, m_parser, 0, 0);
}
}
@ -132,6 +143,8 @@ void http_connection::on_write(asio::error_code const& e)
if (e)
{
close();
if (m_bottled && m_called) return;
m_called = true;
m_handler(e, m_parser, 0, 0);
return;
}
@ -149,13 +162,17 @@ void http_connection::on_read(asio::error_code const& e
if (e == asio::error::eof)
{
close();
m_handler(e, m_parser, 0, 0);
if (m_bottled && m_called) return;
m_called = true;
m_handler(asio::error_code(), m_parser, 0, 0);
return;
}
if (e)
{
close();
if (m_bottled && m_called) return;
m_called = true;
m_handler(e, m_parser, 0, 0);
return;
}
@ -179,11 +196,15 @@ void http_connection::on_read(asio::error_code const& e
}
else if (m_bottled && m_parser.finished())
{
m_timer.cancel();
if (m_bottled && m_called) return;
m_called = true;
m_handler(e, m_parser, 0, 0);
}
}
else
{
assert(!m_bottled);
m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos);
m_read_pos = 0;
}
@ -193,6 +214,8 @@ void http_connection::on_read(asio::error_code const& e
if (m_read_pos == 1024 * 500)
{
close();
if (m_bottled && m_called) return;
m_called = true;
m_handler(asio::error::eof, m_parser, 0, 0);
return;
}

View File

@ -128,10 +128,16 @@ void upnp::rebind(address const& listen_interface)
m_disabled = true;
return;
}
try
{
// the local interface hasn't changed
if (m_socket.is_open()
&& m_socket.local_endpoint().address() == m_local_ip)
return;
m_socket.close();
using namespace asio::ip::multicast;
m_socket.open(udp::v4());
@ -142,8 +148,11 @@ void upnp::rebind(address const& listen_interface)
m_socket.set_option(outbound_interface(m_local_ip));
}
catch (std::exception&)
catch (std::exception& e)
{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
m_log << "socket multicast error " << e.what() << ". disabling UPnP" << std::endl;
#endif
m_disabled = true;
return;
}
@ -199,8 +208,8 @@ void upnp::set_mappings(int tcp, int udp)
if (d.mapping[1].local_port != m_udp_local_port)
{
if (d.mapping[1].external_port == 0)
d.mapping[1].external_port = m_tcp_local_port;
d.mapping[1].local_port = m_tcp_local_port;
d.mapping[1].external_port = m_udp_local_port;
d.mapping[1].local_port = m_udp_local_port;
d.mapping[1].need_update = true;
}
if (d.mapping[0].need_update || d.mapping[1].need_update)
@ -386,6 +395,7 @@ void upnp::map_port(rootdevice& d, int i)
return;
}
d.mapping[i].need_update = false;
assert(!d.upnp_connection);
d.upnp_connection.reset(new http_connection(m_socket.io_service()
, boost::bind(&upnp::on_upnp_map_response, this, _1, _2
, boost::ref(d), i)));
@ -417,8 +427,22 @@ void upnp::map_port(rootdevice& d, int i)
}
// requires the mutex to be locked
void upnp::unmap_port(rootdevice& d, int i)
{
if (d.mapping[i].external_port == 0)
{
if (i < num_mappings - 1)
{
unmap_port(d, i + 1);
}
else
{
m_devices.erase(d);
m_condvar.notify_all();
}
return;
}
d.upnp_connection.reset(new http_connection(m_socket.io_service()
, boost::bind(&upnp::on_upnp_unmap_response, this, _1, _2
, boost::ref(d), i)));
@ -504,7 +528,11 @@ namespace
void upnp::on_upnp_xml(asio::error_code const& e
, libtorrent::http_parser const& p, rootdevice& d)
{
d.upnp_connection.reset();
if (d.upnp_connection)
{
d.upnp_connection->close();
d.upnp_connection.reset();
}
if (e)
{
@ -515,6 +543,15 @@ void upnp::on_upnp_xml(asio::error_code const& e
return;
}
if (!p.header_finished())
{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
m_log << to_simple_string(microsec_clock::universal_time())
<< " <== incomplete http message" << std::endl;
#endif
return;
}
parse_state s;
s.reset("urn:schemas-upnp-org:service:WANIPConnection:1");
xml_parse((char*)p.get_body().begin, (char*)p.get_body().end
@ -568,7 +605,11 @@ namespace
void upnp::on_upnp_map_response(asio::error_code const& e
, libtorrent::http_parser const& p, rootdevice& d, int mapping)
{
d.upnp_connection.reset();
if (d.upnp_connection)
{
d.upnp_connection->close();
d.upnp_connection.reset();
}
if (e)
{
@ -603,6 +644,14 @@ void upnp::on_upnp_map_response(asio::error_code const& e
// </s:Body>
// </s:Envelope>
if (!p.header_finished())
{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
m_log << to_simple_string(microsec_clock::universal_time())
<< " <== incomplete http message" << std::endl;
#endif
return;
}
error_code_parse_state s;
xml_parse((char*)p.get_body().begin, (char*)p.get_body().end
@ -620,13 +669,17 @@ void upnp::on_upnp_map_response(asio::error_code const& e
{
// only permanent leases supported
d.lease_duration = 0;
d.mapping[mapping].need_update = true;
map_port(d, mapping);
return;
}
else if (s.error_code == 718)
{
// conflict in mapping, try next external port
++d.mapping[0].external_port;
++d.mapping[mapping].external_port;
d.mapping[mapping].need_update = true;
map_port(d, mapping);
return;
}
else if (s.error_code != -1)
{
@ -667,7 +720,8 @@ void upnp::on_upnp_map_response(asio::error_code const& e
m_callback(tcp, udp, "");
if (d.lease_duration > 0)
{
d.mapping[mapping].expires = second_clock::universal_time() + seconds(d.lease_duration * 0.75);
d.mapping[mapping].expires = second_clock::universal_time()
+ seconds(int(d.lease_duration * 0.75f));
ptime next_expire = m_refresh_timer.expires_at();
if (next_expire == ptime(boost::date_time::not_a_date_time)
|| next_expire < second_clock::universal_time()
@ -690,7 +744,11 @@ void upnp::on_upnp_map_response(asio::error_code const& e
void upnp::on_upnp_unmap_response(asio::error_code const& e
, libtorrent::http_parser const& p, rootdevice& d, int mapping)
{
d.upnp_connection.reset();
if (d.upnp_connection)
{
d.upnp_connection->close();
d.upnp_connection.reset();
}
if (e)
{
@ -700,6 +758,15 @@ void upnp::on_upnp_unmap_response(asio::error_code const& e
#endif
}
if (!p.header_finished())
{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
m_log << to_simple_string(microsec_clock::universal_time())
<< " <== incomplete http message" << std::endl;
#endif
return;
}
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
m_log << to_simple_string(microsec_clock::universal_time())
<< " <== unmap response: " << std::string(p.get_body().begin, p.get_body().end)
@ -707,18 +774,17 @@ void upnp::on_upnp_unmap_response(asio::error_code const& e
#endif
// ignore errors and continue with the next mapping for this device
boost::mutex::scoped_lock l(m_mutex);
if (mapping < num_mappings - 1)
{
unmap_port(d, mapping + 1);
return;
}
else
{
boost::mutex::scoped_lock l(m_mutex);
// the main thread is likely to be waiting for
// all the unmap operations to complete
m_devices.erase(d);
m_condvar.notify_all();
}
// the main thread is likely to be waiting for
// all the unmap operations to complete
m_devices.erase(d);
m_condvar.notify_all();
}
void upnp::on_expire(asio::error_code const& e)
@ -770,7 +836,10 @@ void upnp::close()
{
rootdevice& d = const_cast<rootdevice&>(*i);
if (d.control_url.empty())
{
m_devices.erase(i++);
continue;
}
unmap_port(d, 0);
++i;
}