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();
	}
示例#2
0
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();
}
示例#3
0
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();
}