formatting fixes in client test, and made the test build when resolve countries is...
[libtorrent-kjk.git] / src / http_connection.cpp
blobba159289879a8bb9f468f14298ebfb3772dc5c91
1 /*
3 Copyright (c) 2007, Arvid Norberg
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
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"
35 #include <boost/bind.hpp>
36 #include <boost/lexical_cast.hpp>
37 #include <asio/ip/tcp.hpp>
38 #include <string>
40 using boost::bind;
42 namespace libtorrent
45 void http_connection::get(std::string const& url, time_duration timeout
46 , bool handle_redirect)
48 m_redirect = handle_redirect;
49 std::string protocol;
50 std::string auth;
51 std::string hostname;
52 std::string path;
53 int port;
54 boost::tie(protocol, auth, hostname, port, path) = parse_url_components(url);
55 std::stringstream headers;
56 headers << "GET " << path << " HTTP/1.0\r\n"
57 "Host:" << hostname <<
58 "\r\nConnection: close\r\n";
59 if (!auth.empty())
60 headers << "Authorization: Basic " << base64encode(auth) << "\r\n";
61 headers << "\r\n";
62 sendbuffer = headers.str();
63 start(hostname, boost::lexical_cast<std::string>(port), timeout);
66 void http_connection::start(std::string const& hostname, std::string const& port
67 , time_duration timeout, bool handle_redirect)
69 m_redirect = handle_redirect;
70 m_timeout = timeout;
71 m_timer.expires_from_now(m_timeout);
72 m_timer.async_wait(bind(&http_connection::on_timeout
73 , boost::weak_ptr<http_connection>(shared_from_this()), _1));
74 m_called = false;
75 if (m_sock.is_open() && m_hostname == hostname && m_port == port)
77 m_parser.reset();
78 asio::async_write(m_sock, asio::buffer(sendbuffer)
79 , bind(&http_connection::on_write, shared_from_this(), _1));
81 else
83 m_sock.close();
84 tcp::resolver::query query(hostname, port);
85 m_resolver.async_resolve(query, bind(&http_connection::on_resolve
86 , shared_from_this(), _1, _2));
87 m_hostname = hostname;
88 m_port = port;
92 void http_connection::on_connect_timeout()
94 if (m_connection_ticket > -1) m_cc.done(m_connection_ticket);
95 m_connection_ticket = -1;
97 if (m_bottled && m_called) return;
98 m_called = true;
99 m_handler(asio::error::timed_out, m_parser, 0, 0);
100 close();
103 void http_connection::on_timeout(boost::weak_ptr<http_connection> p
104 , asio::error_code const& e)
106 boost::shared_ptr<http_connection> c = p.lock();
107 if (!c) return;
108 if (c->m_connection_ticket > -1) c->m_cc.done(c->m_connection_ticket);
109 c->m_connection_ticket = -1;
111 if (e == asio::error::operation_aborted) return;
113 if (c->m_bottled && c->m_called) return;
115 if (c->m_last_receive + c->m_timeout < time_now())
117 c->m_called = true;
118 c->m_handler(asio::error::timed_out, c->m_parser, 0, 0);
119 return;
122 c->m_timer.expires_at(c->m_last_receive + c->m_timeout);
123 c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
126 void http_connection::close()
128 m_timer.cancel();
129 m_limiter_timer.cancel();
130 m_sock.close();
131 m_hostname.clear();
132 m_port.clear();
134 if (m_connection_ticket > -1) m_cc.done(m_connection_ticket);
135 m_connection_ticket = -1;
138 void http_connection::on_resolve(asio::error_code const& e
139 , tcp::resolver::iterator i)
141 if (e)
143 close();
144 if (m_bottled && m_called) return;
145 m_called = true;
146 m_handler(e, m_parser, 0, 0);
147 return;
149 assert(i != tcp::resolver::iterator());
150 m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i)
151 , bind(&http_connection::on_connect_timeout, shared_from_this())
152 , m_timeout);
155 void http_connection::connect(int ticket, tcp::endpoint target_address)
157 m_connection_ticket = ticket;
158 m_sock.async_connect(target_address, boost::bind(&http_connection::on_connect
159 , shared_from_this(), _1/*, ++i*/));
162 void http_connection::on_connect(asio::error_code const& e
163 /*, tcp::resolver::iterator i*/)
165 if (!e)
167 m_last_receive = time_now();
168 asio::async_write(m_sock, asio::buffer(sendbuffer)
169 , bind(&http_connection::on_write, shared_from_this(), _1));
171 /* else if (i != tcp::resolver::iterator())
173 // The connection failed. Try the next endpoint in the list.
174 m_sock.close();
175 m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i)
176 , bind(&http_connection::on_connect_timeout, shared_from_this())
177 , m_timeout);
179 */ else
181 close();
182 if (m_bottled && m_called) return;
183 m_called = true;
184 m_handler(e, m_parser, 0, 0);
188 void http_connection::on_write(asio::error_code const& e)
190 if (e)
192 close();
193 if (m_bottled && m_called) return;
194 m_called = true;
195 m_handler(e, m_parser, 0, 0);
196 return;
199 std::string().swap(sendbuffer);
200 m_recvbuffer.resize(4096);
202 int amount_to_read = m_recvbuffer.size() - m_read_pos;
203 if (m_rate_limit > 0 && amount_to_read > m_download_quota)
205 amount_to_read = m_download_quota;
206 if (m_download_quota == 0)
208 if (!m_limiter_timer_active)
209 on_assign_bandwidth(asio::error_code());
210 return;
213 m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
214 , amount_to_read)
215 , bind(&http_connection::on_read
216 , shared_from_this(), _1, _2));
219 void http_connection::on_read(asio::error_code const& e
220 , std::size_t bytes_transferred)
222 if (m_rate_limit)
224 m_download_quota -= bytes_transferred;
225 assert(m_download_quota >= 0);
228 if (e == asio::error::eof)
230 close();
231 if (m_bottled && m_called) return;
232 m_called = true;
233 char const* data = 0;
234 std::size_t size = 0;
235 if (m_bottled)
237 data = m_parser.get_body().begin;
238 size = m_parser.get_body().left();
240 m_handler(asio::error_code(), m_parser, data, size);
241 return;
244 if (e)
246 close();
247 if (m_bottled && m_called) return;
248 m_called = true;
249 m_handler(e, m_parser, 0, 0);
250 return;
253 m_read_pos += bytes_transferred;
254 assert(m_read_pos <= int(m_recvbuffer.size()));
256 // having a nonempty path means we should handle redirects
257 if (m_redirect && m_parser.header_finished())
259 int code = m_parser.status_code();
260 if (code >= 300 && code < 400)
262 // attempt a redirect
263 std::string url = m_parser.header<std::string>("location");
264 if (url.empty())
266 // missing location header
267 if (m_bottled && m_called) return;
268 m_called = true;
269 m_handler(e, m_parser, 0, 0);
270 return;
273 m_limiter_timer_active = false;
274 close();
276 get(url, m_timeout);
277 return;
280 m_redirect = false;
283 if (m_bottled || !m_parser.header_finished())
285 libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
286 , &m_recvbuffer[0] + m_read_pos);
287 m_parser.incoming(rcv_buf);
288 if (!m_bottled && m_parser.header_finished())
290 if (m_read_pos > m_parser.body_start())
291 m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start()
292 , m_read_pos - m_parser.body_start());
293 m_read_pos = 0;
294 m_last_receive = time_now();
296 else if (m_bottled && m_parser.finished())
298 m_timer.cancel();
299 if (m_bottled && m_called) return;
300 m_called = true;
301 m_handler(e, m_parser, m_parser.get_body().begin, m_parser.get_body().left());
304 else
306 assert(!m_bottled);
307 m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos);
308 m_read_pos = 0;
309 m_last_receive = time_now();
312 if (int(m_recvbuffer.size()) == m_read_pos)
313 m_recvbuffer.resize((std::min)(m_read_pos + 2048, 1024*500));
314 if (m_read_pos == 1024 * 500)
316 close();
317 if (m_bottled && m_called) return;
318 m_called = true;
319 m_handler(asio::error::eof, m_parser, 0, 0);
320 return;
322 int amount_to_read = m_recvbuffer.size() - m_read_pos;
323 if (m_rate_limit > 0 && amount_to_read > m_download_quota)
325 amount_to_read = m_download_quota;
326 if (m_download_quota == 0)
328 if (!m_limiter_timer_active)
329 on_assign_bandwidth(asio::error_code());
330 return;
333 m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
334 , amount_to_read)
335 , bind(&http_connection::on_read
336 , shared_from_this(), _1, _2));
339 void http_connection::on_assign_bandwidth(asio::error_code const& e)
341 if ((e == asio::error::operation_aborted
342 && m_limiter_timer_active)
343 || !m_sock.is_open())
345 if (!m_bottled || !m_called)
346 m_handler(e, m_parser, 0, 0);
347 return;
349 m_limiter_timer_active = false;
350 if (e) return;
352 if (m_download_quota > 0) return;
354 m_download_quota = m_rate_limit / 4;
356 int amount_to_read = m_recvbuffer.size() - m_read_pos;
357 if (amount_to_read > m_download_quota)
358 amount_to_read = m_download_quota;
360 m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
361 , amount_to_read)
362 , bind(&http_connection::on_read
363 , shared_from_this(), _1, _2));
365 m_limiter_timer_active = true;
366 m_limiter_timer.expires_from_now(milliseconds(250));
367 m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
368 , shared_from_this(), _1));
371 void http_connection::rate_limit(int limit)
373 if (!m_limiter_timer_active)
375 m_limiter_timer_active = true;
376 m_limiter_timer.expires_from_now(milliseconds(250));
377 m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
378 , shared_from_this(), _1));
380 m_rate_limit = limit;