// TODO: 3 use span here instead of zero-terminated string std::tuple<int, int, bool> feed_bytes(http_parser& parser, char const* str) { std::tuple<int, int, bool> ret(0, 0, false); std::tuple<int, int, bool> prev(0, 0, false); for (int chunks = 1; chunks < 70; ++chunks) { ret = std::make_tuple(0, 0, false); parser.reset(); span<char const> recv_buf(str, 0); for (;;) { int chunk_size = (std::min)(chunks, int(strlen(recv_buf.end()))); if (chunk_size == 0) break; recv_buf = span<char const>(recv_buf.data(), recv_buf.size() + chunk_size); int payload, protocol; bool error = false; std::tie(payload, protocol) = parser.incoming(recv_buf, error); std::get<0>(ret) += payload; std::get<1>(ret) += protocol; std::get<2>(ret) |= error; // std::cerr << payload << ", " << protocol << ", " << chunk_size << std::endl; TORRENT_ASSERT(payload + protocol == chunk_size || std::get<2>(ret)); } TEST_CHECK(prev == std::make_tuple(0, 0, false) || ret == prev || std::get<2>(ret)); if (!std::get<2>(ret)) { TEST_EQUAL(std::get<0>(ret) + std::get<1>(ret), int(strlen(str))); } prev = ret; } return ret; }
tuple<int, int, bool> feed_bytes(http_parser& parser, char const* str) { tuple<int, int, bool> ret(0, 0, false); tuple<int, int, bool> prev(0, 0, false); for (int chunks = 1; chunks < 70; ++chunks) { ret = make_tuple(0, 0, false); parser.reset(); buffer::const_interval recv_buf(str, str); for (; *str;) { int chunk_size = (std::min)(chunks, int(strlen(recv_buf.end))); if (chunk_size == 0) break; recv_buf.end += chunk_size; int payload, protocol; bool error = false; tie(payload, protocol) = parser.incoming(recv_buf, error); ret.get<0>() += payload; ret.get<1>() += protocol; ret.get<2>() += error; // std::cerr << payload << ", " << protocol << ", " << chunk_size << std::endl; TORRENT_ASSERT(payload + protocol == chunk_size); } TEST_CHECK(prev == make_tuple(0, 0, false) || ret == prev); prev = ret; } return ret; }
void print_http_header(http_parser const& p) { std::cerr << " < " << p.status_code() << " " << p.message() << std::endl; for (std::map<std::string, std::string>::const_iterator i = p.headers().begin(), end(p.headers().end()); i != end; ++i) { std::cerr << " < " << i->first << ": " << i->second << std::endl; } }
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 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(-1, ec.message().c_str()); return; } if (!parser.header_finished()) { fail(-1, "premature end of file"); return; } if (parser.status_code() != 200) { fail(parser.status_code(), parser.message().c_str()); return; } if (ec && ec != asio::error::eof) { fail(parser.status_code(), ec.message().c_str()); return; } received_bytes(size + parser.body_start()); // handle tracker response lazy_entry e; int res = lazy_bdecode(data, data + size, e); if (res == 0 && e.type() == lazy_entry::dict_t) { parse(parser.status_code(), e); } else { std::string error_str("invalid encoding of tracker response: \""); for (char const* i = data, *end(data + size); i != end; ++i) { if (*i >= ' ' && *i <= '~') error_str += *i; else { char val[30]; snprintf(val, sizeof(val), "0x%02x ", *i); error_str += val; } } error_str += "\""; fail(parser.status_code(), error_str.c_str()); } close(); }
void http_handler(error_code const& ec, http_parser const& parser , char const* data, int size, http_connection& c) { ++handler_called; data_size = size; g_error_code = ec; if (parser.header_finished()) { http_status = parser.status_code(); if (http_status == 200) { TEST_CHECK(memcmp(data, data_buffer, size) == 0); } } print_http_header(parser); }
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(-1, ec.message().c_str()); return; } if (!parser.header_finished()) { fail(-1, "premature end of file"); return; } if (parser.status_code() != 200) { fail(parser.status_code(), parser.message().c_str()); return; } if (ec && ec != asio::error::eof) { fail(parser.status_code(), ec.message().c_str()); return; } // handle tracker response entry e; e = bdecode(data, data + size); if (e.type() == entry::dictionary_t) { parse(parser.status_code(), e); } else { std::string error_str("invalid bencoding of tracker response: \""); for (char const* i = data, *end(data + size); i != end; ++i) { if (*i >= ' ' && *i <= '~') error_str += *i; else error_str += "0x" + boost::lexical_cast<std::string>((unsigned int)*i) + " "; } error_str += "\""; fail(parser.status_code(), error_str.c_str()); } 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_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(); }