3 Copyright (c) 2007, Arvid Norberg
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
33 #include "libtorrent/http_connection.hpp"
34 #include "libtorrent/escape_string.hpp"
35 #include "libtorrent/instantiate_connection.hpp"
36 #include "libtorrent/gzip.hpp"
37 #include "libtorrent/parse_url.hpp"
38 #include "libtorrent/socket.hpp"
39 #include "libtorrent/connection_queue.hpp"
41 #include <boost/bind.hpp>
42 #include <boost/lexical_cast.hpp>
48 namespace libtorrent
{
50 enum { max_bottled_buffer
= 1024 * 1024 };
53 void http_connection::get(std::string
const& url
, time_duration timeout
, int prio
54 , proxy_settings
const* ps
, int handle_redirects
, std::string
const& user_agent
55 , address
const& bind_addr
)
64 boost::tie(protocol
, auth
, hostname
, port
, path
, error
)
65 = parse_url_components(url
);
69 callback(asio::error::socket_type_not_supported
);
73 TORRENT_ASSERT(prio
>= 0 && prio
< 2);
76 if (protocol
== "https") ssl
= true;
77 #ifndef TORRENT_USE_OPENSSL
80 callback(asio::error::socket_type_not_supported
);
85 std::stringstream headers
;
86 if (ps
&& (ps
->type
== proxy_settings::http
87 || ps
->type
== proxy_settings::http_pw
)
90 // if we're using an http proxy and not an ssl
91 // connection, just do a regular http proxy request
92 headers
<< "GET " << url
<< " HTTP/1.0\r\n";
93 if (ps
->type
== proxy_settings::http_pw
)
94 headers
<< "Proxy-Authorization: Basic " << base64encode(
95 ps
->username
+ ":" + ps
->password
) << "\r\n";
96 hostname
= ps
->hostname
;
102 headers
<< "GET " << path
<< " HTTP/1.0\r\n"
103 "Host:" << hostname
<< "\r\n";
107 headers
<< "Authorization: Basic " << base64encode(auth
) << "\r\n";
109 if (!user_agent
.empty())
110 headers
<< "User-Agent: " << user_agent
<< "\r\n";
113 "Connection: close\r\n"
114 "Accept-Encoding: gzip\r\n"
117 sendbuffer
= headers
.str();
119 start(hostname
, boost::lexical_cast
<std::string
>(port
), timeout
, prio
120 , ps
, ssl
, handle_redirects
, bind_addr
);
123 void http_connection::start(std::string
const& hostname
, std::string
const& port
124 , time_duration timeout
, int prio
, proxy_settings
const* ps
, bool ssl
, int handle_redirects
125 , address
const& bind_addr
)
127 TORRENT_ASSERT(prio
>= 0 && prio
< 2);
129 m_redirects
= handle_redirects
;
130 if (ps
) m_proxy
= *ps
;
134 m_timer
.expires_from_now(m_timeout
, ec
);
135 m_timer
.async_wait(bind(&http_connection::on_timeout
136 , boost::weak_ptr
<http_connection
>(shared_from_this()), _1
));
139 m_recvbuffer
.clear();
149 if (m_sock
.is_open() && m_hostname
== hostname
&& m_port
== port
150 && m_ssl
== ssl
&& m_bind_addr
== bind_addr
)
152 async_write(m_sock
, asio::buffer(sendbuffer
)
153 , bind(&http_connection::on_write
, shared_from_this(), _1
));
158 m_bind_addr
= bind_addr
;
162 #ifdef TORRENT_USE_OPENSSL
165 m_sock
.instantiate
<ssl_stream
<socket_type
> >(m_resolver
.get_io_service());
166 ssl_stream
<socket_type
>& s
= m_sock
.get
<ssl_stream
<socket_type
> >();
167 bool ret
= instantiate_connection(m_resolver
.get_io_service(), m_proxy
, s
.next_layer());
172 m_sock
.instantiate
<socket_type
>(m_resolver
.get_io_service());
173 bool ret
= instantiate_connection(m_resolver
.get_io_service()
174 , m_proxy
, m_sock
.get
<socket_type
>());
178 bool ret
= instantiate_connection(m_resolver
.get_io_service(), m_proxy
, m_sock
);
181 if (m_bind_addr
!= address_v4::any())
184 m_sock
.bind(tcp::endpoint(m_bind_addr
, 0), ec
);
192 tcp::resolver::query
query(hostname
, port
);
193 m_resolver
.async_resolve(query
, bind(&http_connection::on_resolve
194 , shared_from_this(), _1
, _2
));
195 m_hostname
= hostname
;
200 void http_connection::on_connect_timeout()
202 TORRENT_ASSERT(m_connection_ticket
>= 0);
203 if (m_connection_ticket
> -1) m_cc
.done(m_connection_ticket
);
204 m_connection_ticket
= -1;
206 if (!m_endpoints
.empty())
212 callback(asio::error::timed_out
);
217 void http_connection::on_timeout(boost::weak_ptr
<http_connection
> p
218 , error_code
const& e
)
220 boost::shared_ptr
<http_connection
> c
= p
.lock();
223 if (e
== asio::error::operation_aborted
) return;
225 if (c
->m_last_receive
+ c
->m_timeout
< time_now())
227 if (c
->m_connection_ticket
> -1 && !c
->m_endpoints
.empty())
231 c
->m_timer
.expires_at(c
->m_last_receive
+ c
->m_timeout
, ec
);
232 c
->m_timer
.async_wait(bind(&http_connection::on_timeout
, p
, _1
));
236 c
->callback(asio::error::timed_out
);
242 if (!c
->m_sock
.is_open()) return;
244 c
->m_timer
.expires_at(c
->m_last_receive
+ c
->m_timeout
, ec
);
245 c
->m_timer
.async_wait(bind(&http_connection::on_timeout
, p
, _1
));
248 void http_connection::close()
252 m_limiter_timer
.cancel(ec
);
260 void http_connection::on_resolve(error_code
const& e
261 , tcp::resolver::iterator i
)
269 TORRENT_ASSERT(i
!= tcp::resolver::iterator());
271 std::transform(i
, tcp::resolver::iterator(), std::back_inserter(m_endpoints
)
272 , boost::bind(&tcp::resolver::iterator::value_type::endpoint
, _1
));
274 // The following statement causes msvc to crash (ICE). Since it's not
275 // necessary in the vast majority of cases, just ignore the endpoint
277 #if !defined _MSC_VER || _MSC_VER > 1310
278 // sort the endpoints so that the ones with the same IP version as our
279 // bound listen socket are first. So that when contacting a tracker,
280 // we'll talk to it from the same IP that we're listening on
281 std::partition(m_endpoints
.begin(), m_endpoints
.end()
282 , boost::bind(&address::is_v4
, boost::bind(&tcp::endpoint::address
, _1
)) == m_bind_addr
.is_v4());
288 void http_connection::queue_connect()
290 TORRENT_ASSERT(!m_endpoints
.empty());
291 tcp::endpoint target
= m_endpoints
.front();
292 m_endpoints
.pop_front();
294 m_cc
.enqueue(bind(&http_connection::connect
, shared_from_this(), _1
, target
)
295 , bind(&http_connection::on_connect_timeout
, shared_from_this())
296 , m_timeout
, m_priority
);
299 void http_connection::connect(int ticket
, tcp::endpoint target_address
)
301 m_connection_ticket
= ticket
;
302 m_sock
.async_connect(target_address
, boost::bind(&http_connection::on_connect
303 , shared_from_this(), _1
));
306 void http_connection::on_connect(error_code
const& e
)
308 if (m_connection_ticket
>= 0)
310 m_cc
.done(m_connection_ticket
);
311 m_connection_ticket
= -1;
314 m_last_receive
= time_now();
317 if (m_connect_handler
) m_connect_handler(*this);
318 async_write(m_sock
, asio::buffer(sendbuffer
)
319 , bind(&http_connection::on_write
, shared_from_this(), _1
));
321 else if (!m_endpoints
.empty() && !m_abort
)
323 // The connection failed. Try the next endpoint in the list.
334 void http_connection::callback(error_code
const& e
, char const* data
, int size
)
336 if (!m_bottled
|| !m_called
)
338 std::vector
<char> buf
;
339 if (m_bottled
&& m_parser
.finished())
341 std::string
const& encoding
= m_parser
.header("content-encoding");
342 if (encoding
== "gzip" || encoding
== "x-gzip")
345 if (inflate_gzip(data
, size
, buf
, max_bottled_buffer
, error
))
347 if (m_handler
) m_handler(asio::error::fault
, m_parser
, data
, size
, *this);
352 size
= int(buf
.size());
357 if (m_handler
) m_handler(e
, m_parser
, data
, size
, *this);
361 void http_connection::on_write(error_code
const& e
)
370 std::string().swap(sendbuffer
);
371 m_recvbuffer
.resize(4096);
373 int amount_to_read
= m_recvbuffer
.size() - m_read_pos
;
374 if (m_rate_limit
> 0 && amount_to_read
> m_download_quota
)
376 amount_to_read
= m_download_quota
;
377 if (m_download_quota
== 0)
379 if (!m_limiter_timer_active
)
380 on_assign_bandwidth(error_code());
384 m_sock
.async_read_some(asio::buffer(&m_recvbuffer
[0] + m_read_pos
386 , bind(&http_connection::on_read
387 , shared_from_this(), _1
, _2
));
390 void http_connection::on_read(error_code
const& e
391 , std::size_t bytes_transferred
)
395 m_download_quota
-= bytes_transferred
;
396 TORRENT_ASSERT(m_download_quota
>= 0);
399 if (e
== asio::error::eof
)
401 TORRENT_ASSERT(bytes_transferred
== 0);
402 char const* data
= 0;
403 std::size_t size
= 0;
404 if (m_bottled
&& m_parser
.header_finished())
406 data
= m_parser
.get_body().begin
;
407 size
= m_parser
.get_body().left();
409 callback(e
, data
, size
);
416 TORRENT_ASSERT(bytes_transferred
== 0);
422 m_read_pos
+= bytes_transferred
;
423 TORRENT_ASSERT(m_read_pos
<= int(m_recvbuffer
.size()));
425 if (m_bottled
|| !m_parser
.header_finished())
427 libtorrent::buffer::const_interval
rcv_buf(&m_recvbuffer
[0]
428 , &m_recvbuffer
[0] + m_read_pos
);
430 m_parser
.incoming(rcv_buf
, error
);
434 error_code ec
= asio::error::fault
;
439 // having a nonempty path means we should handle redirects
440 if (m_redirects
&& m_parser
.header_finished())
442 int code
= m_parser
.status_code();
444 if (code
>= 300 && code
< 400)
446 // attempt a redirect
447 std::string
const& location
= m_parser
.header("location");
448 if (location
.empty())
450 // missing location header
451 callback(asio::error::fault
);
458 using boost::tuples::ignore
;
460 boost::tie(ignore
, ignore
, ignore
, ignore
, ignore
, error
)
461 = parse_url_components(location
);
464 get(location
, m_timeout
, m_priority
, &m_proxy
, m_redirects
- 1);
468 // some broken web servers send out relative paths
469 // in the location header.
470 std::string url
= m_url
;
471 // remove the leaf filename
472 std::size_t i
= url
.find_last_of('/');
473 if (i
== std::string::npos
)
483 get(url
, m_timeout
, m_priority
, &m_proxy
, m_redirects
- 1);
491 if (!m_bottled
&& m_parser
.header_finished())
493 if (m_read_pos
> m_parser
.body_start())
494 callback(e
, &m_recvbuffer
[0] + m_parser
.body_start()
495 , m_read_pos
- m_parser
.body_start());
497 m_last_receive
= time_now();
499 else if (m_bottled
&& m_parser
.finished())
503 callback(e
, m_parser
.get_body().begin
, m_parser
.get_body().left());
508 TORRENT_ASSERT(!m_bottled
);
509 callback(e
, &m_recvbuffer
[0], m_read_pos
);
511 m_last_receive
= time_now();
514 if (int(m_recvbuffer
.size()) == m_read_pos
)
515 m_recvbuffer
.resize((std::min
)(m_read_pos
+ 2048, int(max_bottled_buffer
)));
516 if (m_read_pos
== max_bottled_buffer
)
518 callback(asio::error::eof
);
522 int amount_to_read
= m_recvbuffer
.size() - m_read_pos
;
523 if (m_rate_limit
> 0 && amount_to_read
> m_download_quota
)
525 amount_to_read
= m_download_quota
;
526 if (m_download_quota
== 0)
528 if (!m_limiter_timer_active
)
529 on_assign_bandwidth(error_code());
533 m_sock
.async_read_some(asio::buffer(&m_recvbuffer
[0] + m_read_pos
535 , bind(&http_connection::on_read
536 , shared_from_this(), _1
, _2
));
539 void http_connection::on_assign_bandwidth(error_code
const& e
)
541 if ((e
== asio::error::operation_aborted
542 && m_limiter_timer_active
)
543 || !m_sock
.is_open())
545 callback(asio::error::eof
);
548 m_limiter_timer_active
= false;
551 if (m_download_quota
> 0) return;
553 m_download_quota
= m_rate_limit
/ 4;
555 int amount_to_read
= m_recvbuffer
.size() - m_read_pos
;
556 if (amount_to_read
> m_download_quota
)
557 amount_to_read
= m_download_quota
;
559 if (!m_sock
.is_open()) return;
561 m_sock
.async_read_some(asio::buffer(&m_recvbuffer
[0] + m_read_pos
563 , bind(&http_connection::on_read
564 , shared_from_this(), _1
, _2
));
567 m_limiter_timer_active
= true;
568 m_limiter_timer
.expires_from_now(milliseconds(250), ec
);
569 m_limiter_timer
.async_wait(bind(&http_connection::on_assign_bandwidth
570 , shared_from_this(), _1
));
573 void http_connection::rate_limit(int limit
)
575 if (!m_sock
.is_open()) return;
577 if (!m_limiter_timer_active
)
580 m_limiter_timer_active
= true;
581 m_limiter_timer
.expires_from_now(milliseconds(250), ec
);
582 m_limiter_timer
.async_wait(bind(&http_connection::on_assign_bandwidth
583 , shared_from_this(), _1
));
585 m_rate_limit
= limit
;