void http_tracker_connection::on_response(error_code const& ec , http_parser const& parser, char const* data, int size) { // keep this alive boost::intrusive_ptr<http_tracker_connection> me(this); if (ec && ec != asio::error::eof) { fail(ec); return; } if (!parser.header_finished()) { fail(asio::error::eof); return; } if (parser.status_code() != 200) { fail(error_code(parser.status_code(), get_http_category()) , parser.status_code(), parser.message().c_str()); return; } if (ec && ec != asio::error::eof) { fail(ec, parser.status_code()); return; } received_bytes(size + parser.body_start()); // Howdy Patch to work around extra lines in tracker responses. while (data && (*data == 0x0d || *data == 0x0a)) { data++; size--; } // handle tracker response lazy_entry e; error_code ecode; int res = lazy_bdecode(data, data + size, e, ecode); if (res == 0 && e.type() == lazy_entry::dict_t) { parse(parser.status_code(), e); } else { fail(ecode, parser.status_code()); } close(); }
void feed::on_feed(error_code const& ec , http_parser const& parser, char const* data, int size) { // enabling this assert makes the unit test a lot more difficult // TORRENT_ASSERT(m_updating); m_updating = false; if (ec && ec != asio::error::eof) { ++m_failures; m_error = ec; if (m_ses.m_alerts.should_post<rss_alert>()) { m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url , rss_alert::state_error, m_error)); } return; } if (parser.status_code() != 200) { ++m_failures; m_error = error_code(parser.status_code(), get_http_category()); if (m_ses.m_alerts.should_post<rss_alert>()) { m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url , rss_alert::state_error, m_error)); } return; } m_failures = 0; char* buf = const_cast<char*>(data); feed_state s(*this); xml_parse(buf, buf + size, boost::bind(&parse_feed, boost::ref(s), _1, _2, _3)); time_t now = time(NULL); // keep history of the typical feed size times 5 int max_history = (std::max)(s.num_items * 5, 100); // this is not very efficient, but that's probably OK for now while (int(m_added.size()) > max_history) { // loop over all elements and find the one with the lowest timestamp // i.e. it was added the longest ago, then remove it std::map<std::string, time_t>::iterator i = std::min_element( m_added.begin(), m_added.end() , boost::bind(&std::pair<const std::string, time_t>::second, _1) < boost::bind(&std::pair<const std::string, time_t>::second, _2)); m_added.erase(i); } m_last_update = now; // report that we successfully updated the feed if (m_ses.m_alerts.should_post<rss_alert>()) { m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url , rss_alert::state_updated, error_code())); } // update m_ses.m_next_rss_update timestamps // now that we have updated our timestamp m_ses.update_rss_feeds(); }
void feed::on_feed(error_code const& ec , http_parser const& parser, char const* data, int size) { // enabling this assert makes the unit test a lot more difficult // TORRENT_ASSERT(m_updating); m_updating = false; if (ec && ec != asio::error::eof) { ++m_failures; m_error = ec; if (m_ses.m_alerts.should_post<rss_alert>()) { m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url , rss_alert::state_error, m_error)); } return; } if (parser.status_code() != 200) { ++m_failures; m_error = error_code(parser.status_code(), get_http_category()); if (m_ses.m_alerts.should_post<rss_alert>()) { m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url , rss_alert::state_error, m_error)); } return; } m_failures = 0; char* buf = const_cast<char*>(data); feed_state s(*this); xml_parse(buf, buf + size, boost::bind(&parse_feed, boost::ref(s), _1, _2, _3)); time_t now = time(NULL); if (m_settings.auto_download || m_settings.auto_map_handles) { for (std::vector<feed_item>::iterator i = m_items.begin() , end(m_items.end()); i != end; ++i) { i->handle = torrent_handle(m_ses.find_torrent(i->uuid.empty() ? i->url : i->uuid)); // if we're already downloading this torrent, or if we // don't have auto-download enabled, just move along to // the next one if (i->handle.is_valid() || !m_settings.auto_download) continue; // has this already been added? if (m_added.find(i->url) != m_added.end()) continue; // this means we should add this torrent to the session add_torrent_params p = m_settings.add_args; p.url = i->url; p.uuid = i->uuid; p.source_feed_url = m_settings.url; p.ti.reset(); p.info_hash.clear(); p.name = i->title.c_str(); error_code e; torrent_handle h = m_ses.add_torrent(p, e); m_ses.m_alerts.post_alert(add_torrent_alert(h, p, e)); m_added.insert(make_pair(i->url, now)); } } m_last_update = now; // keep history of the typical feed size times 5 int max_history = (std::max)(s.num_items * 5, 100); // this is not very efficient, but that's probably OK for now while (int(m_added.size()) > max_history) { // loop over all elements and find the one with the lowest timestamp // i.e. it was added the longest ago, then remove it std::map<std::string, time_t>::iterator i = std::min_element( m_added.begin(), m_added.end() , boost::bind(&std::pair<const std::string, time_t>::second, _1) < boost::bind(&std::pair<const std::string, time_t>::second, _2)); m_added.erase(i); } // report that we successfully updated the feed if (m_ses.m_alerts.should_post<rss_alert>()) { m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url , rss_alert::state_updated, error_code())); } // update m_ses.m_next_rss_update timestamps // now that we have updated our timestamp m_ses.update_rss_feeds(); }
void http_seed_connection::on_receive(error_code const& error , std::size_t bytes_transferred) { INVARIANT_CHECK; if (error) { received_bytes(0, bytes_transferred); #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "ERROR" , "http_seed_connection error: %s", error.message().c_str()); #endif return; } boost::shared_ptr<torrent> t = associated_torrent().lock(); TORRENT_ASSERT(t); for (;;) { buffer::const_interval recv_buffer = m_recv_buffer.get(); if (bytes_transferred == 0) break; TORRENT_ASSERT(recv_buffer.left() > 0); TORRENT_ASSERT(!m_requests.empty()); if (m_requests.empty()) { received_bytes(0, bytes_transferred); disconnect(errors::http_error, op_bittorrent, 2); return; } peer_request front_request = m_requests.front(); bool header_finished = m_parser.header_finished(); if (!header_finished) { bool parse_error = false; int protocol = 0; int payload = 0; boost::tie(payload, protocol) = m_parser.incoming(recv_buffer, parse_error); received_bytes(0, protocol); bytes_transferred -= protocol; #if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS if (payload > front_request.length) payload = front_request.length; #endif if (parse_error) { received_bytes(0, bytes_transferred); disconnect(errors::http_parse_error, op_bittorrent, 2); return; } TORRENT_ASSERT(recv_buffer.left() == 0 || *recv_buffer.begin == 'H'); TORRENT_ASSERT(recv_buffer.left() <= m_recv_buffer.packet_size()); // this means the entire status line hasn't been received yet if (m_parser.status_code() == -1) { TORRENT_ASSERT(payload == 0); TORRENT_ASSERT(bytes_transferred == 0); break; } // if the status code is not one of the accepted ones, abort if (!is_ok_status(m_parser.status_code())) { int retry_time = atoi(m_parser.header("retry-after").c_str()); if (retry_time <= 0) retry_time = 5 * 60; // temporarily unavailable, retry later t->retry_web_seed(this, retry_time); std::string error_msg = to_string(m_parser.status_code()).elems + (" " + m_parser.message()); if (t->alerts().should_post<url_seed_alert>()) { t->alerts().emplace_alert<url_seed_alert>(t->get_handle(), url() , error_msg); } received_bytes(0, bytes_transferred); disconnect(error_code(m_parser.status_code(), get_http_category()), op_bittorrent, 1); return; } if (!m_parser.header_finished()) { TORRENT_ASSERT(payload == 0); TORRENT_ASSERT(bytes_transferred == 0); break; } } // we just completed reading the header if (!header_finished) { if (is_redirect(m_parser.status_code())) { // this means we got a redirection request // look for the location header std::string location = m_parser.header("location"); received_bytes(0, bytes_transferred); if (location.empty()) { // we should not try this server again. t->remove_web_seed(this, errors::missing_location, op_bittorrent, 2); return; } // add the redirected url and remove the current one t->add_web_seed(location, web_seed_entry::http_seed); t->remove_web_seed(this, errors::redirecting, op_bittorrent, 2); return; } std::string const& server_version = m_parser.header("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 += ")"; } m_response_left = atol(m_parser.header("content-length").c_str()); if (m_response_left == -1) { received_bytes(0, bytes_transferred); // we should not try this server again. t->remove_web_seed(this, errors::no_content_length, op_bittorrent, 2); return; } if (m_response_left != front_request.length) { received_bytes(0, bytes_transferred); // we should not try this server again. t->remove_web_seed(this, errors::invalid_range, op_bittorrent, 2); return; } m_body_start = m_parser.body_start(); } recv_buffer.begin += m_body_start; // ========================= // === CHUNKED ENCODING === // ========================= while (m_parser.chunked_encoding() && m_chunk_pos >= 0 && m_chunk_pos < recv_buffer.left()) { int header_size = 0; boost::int64_t chunk_size = 0; buffer::const_interval chunk_start = recv_buffer; chunk_start.begin += m_chunk_pos; TORRENT_ASSERT(chunk_start.begin[0] == '\r' || detail::is_hex(chunk_start.begin, 1)); bool ret = m_parser.parse_chunk_header(chunk_start, &chunk_size, &header_size); if (!ret) { TORRENT_ASSERT(bytes_transferred >= size_t(chunk_start.left() - m_partial_chunk_header)); bytes_transferred -= chunk_start.left() - m_partial_chunk_header; received_bytes(0, chunk_start.left() - m_partial_chunk_header); m_partial_chunk_header = chunk_start.left(); if (bytes_transferred == 0) return; break; } else { #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "CHUNKED_ENCODING" , "parsed chunk: %" PRId64 " header_size: %d" , chunk_size, header_size); #endif TORRENT_ASSERT(bytes_transferred >= size_t(header_size - m_partial_chunk_header)); bytes_transferred -= header_size - m_partial_chunk_header; received_bytes(0, header_size - m_partial_chunk_header); m_partial_chunk_header = 0; TORRENT_ASSERT(chunk_size != 0 || chunk_start.left() <= header_size || chunk_start.begin[header_size] == 'H'); // cut out the chunk header from the receive buffer TORRENT_ASSERT(m_chunk_pos + m_body_start < INT_MAX); m_recv_buffer.cut(header_size, t->block_size() + 1024, int(m_chunk_pos + m_body_start)); recv_buffer = m_recv_buffer.get(); recv_buffer.begin += m_body_start; m_chunk_pos += chunk_size; if (chunk_size == 0) { TORRENT_ASSERT(m_recv_buffer.get().left() < m_chunk_pos + m_body_start + 1 || m_recv_buffer.get()[int(m_chunk_pos + m_body_start)] == 'H' || (m_parser.chunked_encoding() && m_recv_buffer.get()[int(m_chunk_pos + m_body_start)] == '\r')); m_chunk_pos = -1; } } } int payload = bytes_transferred; if (payload > m_response_left) payload = int(m_response_left); if (payload > front_request.length) payload = front_request.length; received_bytes(payload, 0); incoming_piece_fragment(payload); m_response_left -= payload; if (m_parser.status_code() == 503) { if (!m_parser.finished()) return; int retry_time = atol(std::string(recv_buffer.begin, recv_buffer.end).c_str()); if (retry_time <= 0) retry_time = 60; #ifndef TORRENT_DISABLE_LOGGING peer_log(peer_log_alert::info, "CONNECT", "retrying in %d seconds", retry_time); #endif received_bytes(0, bytes_transferred); // temporarily unavailable, retry later t->retry_web_seed(this, retry_time); disconnect(error_code(m_parser.status_code(), get_http_category()), op_bittorrent, 1); return; } // we only received the header, no data if (recv_buffer.left() == 0) break; if (recv_buffer.left() < front_request.length) break; // if the response is chunked, we need to receive the last // terminating chunk and the tail headers before we can proceed if (m_parser.chunked_encoding() && m_chunk_pos >= 0) break; m_requests.pop_front(); incoming_piece(front_request, recv_buffer.begin); if (associated_torrent().expired()) return; int size_to_cut = m_body_start + front_request.length; TORRENT_ASSERT(m_recv_buffer.get().left() < size_to_cut + 1 || m_recv_buffer.get()[size_to_cut] == 'H' || (m_parser.chunked_encoding() && m_recv_buffer.get()[size_to_cut] == '\r')); m_recv_buffer.cut(size_to_cut, t->block_size() + 1024); if (m_response_left == 0) m_chunk_pos = 0; else m_chunk_pos -= front_request.length; bytes_transferred -= payload; m_body_start = 0; if (m_response_left > 0) continue; TORRENT_ASSERT(m_response_left == 0); m_parser.reset(); } }
void http_tracker_connection::on_response(error_code const& ec , http_parser const& parser, char const* data, int size) { // keep this alive boost::shared_ptr<http_tracker_connection> me(shared_from_this()); if (ec && ec != boost::asio::error::eof) { fail(ec); return; } if (!parser.header_finished()) { fail(boost::asio::error::eof); return; } if (parser.status_code() != 200) { fail(error_code(parser.status_code(), get_http_category()) , parser.status_code(), parser.message().c_str()); return; } if (ec && ec != boost::asio::error::eof) { fail(ec, parser.status_code()); return; } received_bytes(size + parser.body_start()); // handle tracker response error_code ecode; boost::shared_ptr<request_callback> cb = requester(); if (!cb) { close(); return; } tracker_response resp = parse_tracker_response(data, size, ecode , tracker_req().kind, tracker_req().info_hash); if (!resp.warning_message.empty()) cb->tracker_warning(tracker_req(), resp.warning_message); if (ecode) { fail(ecode, parser.status_code(), resp.failure_reason.c_str() , resp.interval, resp.min_interval); close(); return; } // do slightly different things for scrape requests if (0 != (tracker_req().kind & tracker_request::scrape_request)) { cb->tracker_scrape_response(tracker_req(), resp.complete , resp.incomplete, resp.downloaded, resp.downloaders); } else { std::list<address> ip_list; if (m_tracker_connection) { error_code ignore; std::vector<tcp::endpoint> const& epts = m_tracker_connection->endpoints(); for (std::vector<tcp::endpoint>::const_iterator i = epts.begin() , end(epts.end()); i != end; ++i) { ip_list.push_back(i->address()); } } cb->tracker_response(tracker_req(), m_tracker_ip, ip_list, resp); } close(); }