00001
00002
00003
00004
00005
00006
00007
00008 #include <vector>
00009 #include <iostream>
00010 #include <iomanip>
00011 #include <limits>
00012 #include <boost/bind.hpp>
00013 #include <sstream>
00014
00015 #include "libtorrent/web_peer_connection.hpp"
00016 #include "libtorrent/session.hpp"
00017 #include "libtorrent/identify_client.hpp"
00018 #include "libtorrent/entry.hpp"
00019 #include "libtorrent/bencode.hpp"
00020 #include "libtorrent/alert_types.hpp"
00021 #include "libtorrent/invariant_check.hpp"
00022 #include "libtorrent/io.hpp"
00023 #include "libtorrent/version.hpp"
00024 #include "libtorrent/aux_/session_impl.hpp"
00025
00026 using namespace boost::posix_time;
00027 using boost::bind;
00028 using boost::shared_ptr;
00029 using libtorrent::aux::session_impl;
00030
00031 namespace libtorrent
00032 {
00033 web_peer_connection::web_peer_connection(
00034 session_impl& ses
00035 , boost::weak_ptr<torrent> t
00036 , boost::shared_ptr<stream_socket> s
00037 , tcp::endpoint const& remote
00038 , tcp::endpoint const& proxy
00039 , std::string const& url)
00040 : peer_connection(ses, t, s, remote, proxy)
00041 , m_url(url)
00042 , m_first_request(true)
00043 {
00044 INVARIANT_CHECK;
00045
00046
00047
00048 prefer_whole_pieces(true);
00049
00050
00051 request_large_blocks(true);
00052
00053 set_non_prioritized(true);
00054 shared_ptr<torrent> tor = t.lock();
00055 assert(tor);
00056 int blocks_per_piece = tor->torrent_file().piece_length() / tor->block_size();
00057
00058
00059
00060 m_max_out_request_queue = ses.settings().urlseed_pipeline_size
00061 * blocks_per_piece;
00062
00063
00064
00065 set_timeout(ses.settings().urlseed_timeout);
00066 #ifdef TORRENT_VERBOSE_LOGGING
00067 (*m_logger) << "*** web_peer_connection\n";
00068 #endif
00069
00070 std::string protocol;
00071 boost::tie(protocol, m_host, m_port, m_path)
00072 = parse_url_components(url);
00073
00074 m_server_string = "URL seed @ ";
00075 m_server_string += m_host;
00076 }
00077
00078 web_peer_connection::~web_peer_connection()
00079 {}
00080
00081 boost::optional<piece_block_progress>
00082 web_peer_connection::downloading_piece_progress() const
00083 {
00084 if (!m_parser.header_finished() || m_requests.empty())
00085 return boost::optional<piece_block_progress>();
00086
00087 boost::shared_ptr<torrent> t = associated_torrent().lock();
00088 assert(t);
00089
00090 buffer::const_interval http_body = m_parser.get_body();
00091 piece_block_progress ret;
00092
00093 ret.piece_index = m_requests.front().piece;
00094 ret.bytes_downloaded = http_body.left() % t->block_size();
00095 ret.block_index = (m_requests.front().start + ret.bytes_downloaded) / t->block_size();
00096 ret.full_block_bytes = t->block_size();
00097 const int last_piece = t->torrent_file().num_pieces() - 1;
00098 if (ret.piece_index == last_piece && ret.block_index
00099 == t->torrent_file().piece_size(last_piece) / t->block_size())
00100 ret.full_block_bytes = t->torrent_file().piece_size(last_piece) % t->block_size();
00101 return ret;
00102 }
00103
00104 void web_peer_connection::on_connected()
00105 {
00106 boost::shared_ptr<torrent> t = associated_torrent().lock();
00107 assert(t);
00108
00109
00110 incoming_bitfield(std::vector<bool>(
00111 t->torrent_file().num_pieces(), true));
00112
00113 incoming_unchoke();
00114
00115 reset_recv_buffer(t->torrent_file().piece_length() + 1024 * 2);
00116 }
00117
00118 void web_peer_connection::write_request(peer_request const& r)
00119 {
00120 INVARIANT_CHECK;
00121
00122 boost::shared_ptr<torrent> t = associated_torrent().lock();
00123 assert(t);
00124
00125 assert(t->valid_metadata());
00126
00127 bool single_file_request = false;
00128 if (!m_path.empty() && m_path[m_path.size() - 1] != '/')
00129 single_file_request = true;
00130
00131 torrent_info const& info = t->torrent_file();
00132
00133 std::string request;
00134
00135 int size = r.length;
00136 const int block_size = t->block_size();
00137 while (size > 0)
00138 {
00139 int request_size = std::min(block_size, size);
00140 peer_request pr = {r.piece, r.start + r.length - size
00141 , request_size};
00142 m_requests.push_back(pr);
00143 size -= request_size;
00144 }
00145
00146 bool using_proxy = false;
00147 if (!m_ses.settings().proxy_ip.empty())
00148 using_proxy = true;
00149
00150 if (single_file_request)
00151 {
00152 request += "GET ";
00153
00154
00155 request += using_proxy ? m_url : m_path;
00156 request += " HTTP/1.1\r\n";
00157 request += "Host: ";
00158 request += m_host;
00159 if (m_first_request)
00160 {
00161 request += "\r\nUser-Agent: ";
00162 request += m_ses.settings().user_agent;
00163 }
00164 if (using_proxy && !m_ses.settings().proxy_login.empty())
00165 {
00166 request += "\r\nProxy-Authorization: Basic ";
00167 request += base64encode(m_ses.settings().proxy_login + ":"
00168 + m_ses.settings().proxy_password);
00169 }
00170 if (using_proxy)
00171 {
00172 request += "\r\nProxy-Connection: keep-alive";
00173 }
00174 request += "\r\nRange: bytes=";
00175 request += boost::lexical_cast<std::string>(r.piece
00176 * info.piece_length() + r.start);
00177 request += "-";
00178 request += boost::lexical_cast<std::string>(r.piece
00179 * info.piece_length() + r.start + r.length - 1);
00180 if (m_first_request || using_proxy)
00181 request += "\r\nConnection: keep-alive";
00182 request += "\r\n\r\n";
00183 m_first_request = false;
00184 m_file_requests.push_back(0);
00185 }
00186 else
00187 {
00188 std::vector<file_slice> files = info.map_block(r.piece, r.start
00189 , r.length);
00190
00191 for (std::vector<file_slice>::iterator i = files.begin();
00192 i != files.end(); ++i)
00193 {
00194 file_slice const& f = *i;
00195
00196 request += "GET ";
00197 if (using_proxy)
00198 {
00199 request += m_url;
00200 std::string path = info.file_at(f.file_index).path.string();
00201 request += escape_path(path.c_str(), path.length());
00202 }
00203 else
00204 {
00205 std::string path = m_path;
00206 path += info.file_at(f.file_index).path.string();
00207 request += escape_path(path.c_str(), path.length());
00208 }
00209 request += " HTTP/1.1\r\n";
00210 request += "Host: ";
00211 request += m_host;
00212 if (m_first_request)
00213 {
00214 request += "\r\nUser-Agent: ";
00215 request += m_ses.settings().user_agent;
00216 }
00217 if (using_proxy && !m_ses.settings().proxy_login.empty())
00218 {
00219 request += "\r\nProxy-Authorization: Basic ";
00220 request += base64encode(m_ses.settings().proxy_login + ":"
00221 + m_ses.settings().proxy_password);
00222 }
00223 if (using_proxy)
00224 {
00225 request += "\r\nProxy-Connection: keep-alive";
00226 }
00227 request += "\r\nRange: bytes=";
00228 request += boost::lexical_cast<std::string>(f.offset);
00229 request += "-";
00230 request += boost::lexical_cast<std::string>(f.offset + f.size - 1);
00231 if (m_first_request || using_proxy)
00232 request += "\r\nConnection: keep-alive";
00233 request += "\r\n\r\n";
00234 m_first_request = false;
00235 m_file_requests.push_back(f.file_index);
00236 }
00237 }
00238
00239 #ifdef TORRENT_VERBOSE_LOGGING
00240 (*m_logger) << request << "\n";
00241 #endif
00242
00243 send_buffer(request.c_str(), request.c_str() + request.size());
00244 }
00245
00246
00247
00248
00249
00250 namespace
00251 {
00252 bool range_contains(peer_request const& range, peer_request const& req)
00253 {
00254 return range.start <= req.start
00255 && range.start + range.length >= req.start + req.length;
00256 }
00257 }
00258
00259
00260 void web_peer_connection::on_receive(asio::error_code const& error
00261 , std::size_t bytes_transferred)
00262 {
00263 INVARIANT_CHECK;
00264
00265 if (error) return;
00266
00267 boost::shared_ptr<torrent> t = associated_torrent().lock();
00268 assert(t);
00269
00270 incoming_piece_fragment();
00271
00272 for (;;)
00273 {
00274 buffer::const_interval recv_buffer = receive_buffer();
00275
00276 int payload;
00277 int protocol;
00278 bool header_finished = m_parser.header_finished();
00279 boost::tie(payload, protocol) = m_parser.incoming(recv_buffer);
00280 m_statistics.received_bytes(payload, protocol);
00281
00282 assert(recv_buffer.left() <= packet_size());
00283 assert (recv_buffer.left() < packet_size()
00284 || m_parser.finished());
00285
00286
00287 if (m_parser.status_code() == -1) break;
00288
00289
00290 if (m_parser.status_code() != 206
00291 && m_parser.status_code() != 200
00292 && !(m_parser.status_code() >= 300
00293 && m_parser.status_code() < 400))
00294 {
00295
00296 t->remove_url_seed(m_url);
00297 std::string error_msg = boost::lexical_cast<std::string>(m_parser.status_code())
00298 + " " + m_parser.message();
00299 if (m_ses.m_alerts.should_post(alert::warning))
00300 {
00301 session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
00302 m_ses.m_alerts.post_alert(url_seed_alert(t->get_handle(), url()
00303 , error_msg));
00304 }
00305 throw std::runtime_error(error_msg);
00306 }
00307
00308 if (!m_parser.header_finished()) break;
00309
00310
00311 if (!header_finished)
00312 {
00313 if (m_parser.status_code() >= 300 && m_parser.status_code() < 400)
00314 {
00315
00316
00317 std::string location = m_parser.header<std::string>("location");
00318
00319 if (location.empty())
00320 {
00321
00322 t->remove_url_seed(m_url);
00323 throw std::runtime_error("got HTTP redirection status without location header");
00324 }
00325
00326 bool single_file_request = false;
00327 if (!m_path.empty() && m_path[m_path.size() - 1] != '/')
00328 single_file_request = true;
00329
00330
00331 if (!single_file_request)
00332 {
00333 assert(!m_file_requests.empty());
00334 int file_index = m_file_requests.front();
00335
00336 torrent_info const& info = t->torrent_file();
00337 std::string path = info.file_at(file_index).path.string();
00338 path = escape_path(path.c_str(), path.length());
00339 size_t i = location.rfind(path);
00340 if (i == std::string::npos)
00341 {
00342 t->remove_url_seed(m_url);
00343 throw std::runtime_error("got invalid HTTP redirection location (\"" + location + "\") "
00344 "expected it to end with: " + path);
00345 }
00346 location.resize(i);
00347 }
00348 t->add_url_seed(location);
00349 t->remove_url_seed(m_url);
00350 throw std::runtime_error("redirecting to " + location);
00351 }
00352
00353 std::string server_version = m_parser.header<std::string>("server");
00354 if (!server_version.empty())
00355 {
00356 m_server_string = "URL seed @ ";
00357 m_server_string += m_host;
00358 m_server_string += " (";
00359 m_server_string += server_version;
00360 m_server_string += ")";
00361 }
00362
00363 }
00364
00365 buffer::const_interval http_body = m_parser.get_body();
00366
00367 size_type range_start;
00368 size_type range_end;
00369 if (m_parser.status_code() == 206)
00370 {
00371 std::stringstream range_str(m_parser.header<std::string>("content-range"));
00372 char dummy;
00373 std::string bytes;
00374 range_str >> bytes >> range_start >> dummy >> range_end;
00375 if (!range_str)
00376 {
00377
00378 t->remove_url_seed(m_url);
00379 throw std::runtime_error("invalid range in HTTP response: " + range_str.str());
00380 }
00381
00382 range_end++;
00383 }
00384 else
00385 {
00386 range_start = 0;
00387 range_end = m_parser.header<size_type>("content-length");
00388 if (range_end == -1)
00389 {
00390
00391 t->remove_url_seed(m_url);
00392 throw std::runtime_error("no content-length in HTTP response");
00393 }
00394 }
00395
00396 torrent_info const& info = t->torrent_file();
00397
00398 if (m_requests.empty() || m_file_requests.empty())
00399 throw std::runtime_error("unexpected HTTP response");
00400
00401 int file_index = m_file_requests.front();
00402 peer_request in_range = info.map_file(file_index, range_start
00403 , range_end - range_start);
00404
00405 peer_request front_request = m_requests.front();
00406 if (in_range.piece != front_request.piece
00407 || in_range.start > front_request.start + int(m_piece.size()))
00408 {
00409 throw std::runtime_error("invalid range in HTTP response");
00410 }
00411
00412 front_request = m_requests.front();
00413
00414
00415
00416
00417 assert(in_range.start - int(m_piece.size()) <= front_request.start);
00418 http_body.begin += front_request.start - in_range.start + int(m_piece.size());
00419
00420
00421
00422
00423
00424
00425
00426 bool range_overlaps_request = in_range.start + in_range.length
00427 > front_request.start + int(m_piece.size());
00428
00429
00430
00431
00432
00433 if (range_overlaps_request && !range_contains(in_range, front_request))
00434 {
00435
00436
00437
00438
00439
00440
00441 m_piece.reserve(info.piece_length());
00442 int copy_size = std::min(front_request.length - int(m_piece.size())
00443 , http_body.left());
00444 std::copy(http_body.begin, http_body.begin + copy_size, std::back_inserter(m_piece));
00445 assert(int(m_piece.size()) <= front_request.length);
00446 http_body.begin += copy_size;
00447 int piece_size = int(m_piece.size());
00448 if (piece_size < front_request.length)
00449 return;
00450
00451
00452
00453
00454
00455
00456 m_requests.pop_front();
00457 incoming_piece(front_request, &m_piece[0]);
00458 if (associated_torrent().expired()) return;
00459 m_piece.clear();
00460 }
00461
00462
00463 while (!m_requests.empty()
00464 && range_contains(in_range, m_requests.front())
00465 && http_body.left() >= m_requests.front().length)
00466 {
00467 peer_request r = m_requests.front();
00468 m_requests.pop_front();
00469 assert(http_body.begin == recv_buffer.begin + m_parser.body_start()
00470 + r.start - in_range.start);
00471 assert(http_body.left() >= r.length);
00472
00473 incoming_piece(r, http_body.begin);
00474 if (associated_torrent().expired()) return;
00475 http_body.begin += r.length;
00476 }
00477
00478 if (!m_requests.empty())
00479 {
00480 range_overlaps_request = in_range.start + in_range.length
00481 > m_requests.front().start + int(m_piece.size());
00482
00483 if (in_range.start + in_range.length < m_requests.front().start + m_requests.front().length
00484 && m_parser.finished())
00485 {
00486 m_piece.reserve(info.piece_length());
00487 int copy_size = std::min(m_requests.front().length - int(m_piece.size())
00488 , http_body.left());
00489 std::copy(http_body.begin, http_body.begin + copy_size, std::back_inserter(m_piece));
00490 http_body.begin += copy_size;
00491 }
00492 }
00493
00494 if (m_parser.finished())
00495 {
00496 m_file_requests.pop_front();
00497 assert(http_body.left() == 0);
00498 m_parser.reset();
00499 assert(recv_buffer.end == http_body.end || *http_body.end == 'H');
00500 cut_receive_buffer(http_body.end - recv_buffer.begin
00501 , t->torrent_file().piece_length() + 1024 * 2);
00502 continue;
00503 }
00504 break;
00505 }
00506 }
00507
00508 void web_peer_connection::get_peer_info(peer_info& p) const
00509 {
00510 assert(!associated_torrent().expired());
00511
00512 p.down_speed = statistics().download_rate();
00513 p.up_speed = statistics().upload_rate();
00514 p.payload_down_speed = statistics().download_payload_rate();
00515 p.payload_up_speed = statistics().upload_payload_rate();
00516 p.pid = pid();
00517 p.ip = remote();
00518
00519 p.country[0] = m_country[0];
00520 p.country[1] = m_country[1];
00521
00522 p.total_download = statistics().total_payload_download();
00523 p.total_upload = statistics().total_payload_upload();
00524
00525 if (m_bandwidth_limit[upload_channel].throttle() == bandwidth_limit::inf)
00526 p.upload_limit = -1;
00527 else
00528 p.upload_limit = m_bandwidth_limit[upload_channel].throttle();
00529
00530 if (m_bandwidth_limit[download_channel].throttle() == bandwidth_limit::inf)
00531 p.download_limit = -1;
00532 else
00533 p.download_limit = m_bandwidth_limit[download_channel].throttle();
00534
00535 p.load_balancing = total_free_upload();
00536
00537 p.download_queue_length = (int)download_queue().size();
00538 p.upload_queue_length = (int)upload_queue().size();
00539
00540 if (boost::optional<piece_block_progress> ret = downloading_piece_progress())
00541 {
00542 p.downloading_piece_index = ret->piece_index;
00543 p.downloading_block_index = ret->block_index;
00544 p.downloading_progress = ret->bytes_downloaded;
00545 p.downloading_total = ret->full_block_bytes;
00546 }
00547 else
00548 {
00549 p.downloading_piece_index = -1;
00550 p.downloading_block_index = -1;
00551 p.downloading_progress = 0;
00552 p.downloading_total = 0;
00553 }
00554
00555 p.flags = 0;
00556 if (is_interesting()) p.flags |= peer_info::interesting;
00557 if (is_choked()) p.flags |= peer_info::choked;
00558 if (is_peer_interested()) p.flags |= peer_info::remote_interested;
00559 if (has_peer_choked()) p.flags |= peer_info::remote_choked;
00560 if (is_local()) p.flags |= peer_info::local_connection;
00561 if (!is_connecting() && m_server_string.empty())
00562 p.flags |= peer_info::handshake;
00563 if (is_connecting() && !is_queued()) p.flags |= peer_info::connecting;
00564 if (is_queued()) p.flags |= peer_info::queued;
00565
00566 p.pieces = get_bitfield();
00567 p.seed = is_seed();
00568
00569 p.client = m_server_string;
00570 p.connection_type = peer_info::web_seed;
00571 }
00572
00573 bool web_peer_connection::in_handshake() const
00574 {
00575 return m_server_string.empty();
00576 }
00577
00578
00579 void web_peer_connection::on_sent(asio::error_code const& error
00580 , std::size_t bytes_transferred)
00581 {
00582 INVARIANT_CHECK;
00583
00584 if (error) return;
00585 m_statistics.sent_bytes(0, bytes_transferred);
00586 }
00587
00588 #ifndef NDEBUG
00589 void web_peer_connection::check_invariant() const
00590 {
00591 }
00592 #endif
00593
00594 }
00595