From fcf211378a2cc1cd24136641996a6e618e975c1b Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Tue, 12 Dec 2006 02:28:53 +0000 Subject: [PATCH] fixed http_tracker_connection to finally use the http-parser class (which also web_seed_connection uses). also fixed a case sensitivity issue with http header names. --- .../libtorrent/http_tracker_connection.hpp | 14 +- src/http_tracker_connection.cpp | 248 +++++++----------- src/peer_connection.cpp | 6 +- src/web_peer_connection.cpp | 4 +- 4 files changed, 97 insertions(+), 175 deletions(-) diff --git a/include/libtorrent/http_tracker_connection.hpp b/include/libtorrent/http_tracker_connection.hpp index 42303d4e7..f5596f5dd 100755 --- a/include/libtorrent/http_tracker_connection.hpp +++ b/include/libtorrent/http_tracker_connection.hpp @@ -84,7 +84,6 @@ namespace libtorrent std::string m_server_message; int m_content_length; - enum { plain, gzip } m_content_encoding; enum { read_status, read_header, read_body } m_state; @@ -144,11 +143,7 @@ namespace libtorrent peer_entry extract_peer_info(const entry& e); tracker_manager& m_man; - enum { read_status, read_header, read_body } m_state; - - enum { plain, gzip } m_content_encoding; - int m_content_length; - std::string m_location; + http_parser m_parser; tcp::resolver m_name_lookup; int m_port; @@ -157,16 +152,9 @@ namespace libtorrent std::vector m_buffer; std::string m_send_buffer; - std::string m_server_message; - std::string m_server_protocol; - session_settings const& m_settings; std::string m_password; - int m_code; - // server string in http-reply - std::string m_server; - bool m_timed_out; }; diff --git a/src/http_tracker_connection.cpp b/src/http_tracker_connection.cpp index 7700a8d68..d4d9f3625 100755 --- a/src/http_tracker_connection.cpp +++ b/src/http_tracker_connection.cpp @@ -90,7 +90,6 @@ namespace libtorrent : m_recv_pos(0) , m_status_code(-1) , m_content_length(-1) - , m_content_encoding(plain) , m_state(read_status) , m_recv_buffer(0, 0) , m_body_start_pos(0) @@ -160,10 +159,11 @@ namespace libtorrent } std::string name = line.substr(0, separator); + transform(name.begin(), name.end(), name.begin(), (int(*)(int))std::tolower); std::string value = line.substr(separator + 2, std::string::npos); m_header.insert(std::make_pair(name, value)); - if (name == "Content-Length") + if (name == "content-length") { try { @@ -171,7 +171,7 @@ namespace libtorrent } catch(boost::bad_lexical_cast&) {} } - else if (name == "Content-Encoding") +/* else if (name == "content-encoding") { if (value == "gzip" || value == "x-gzip") { @@ -185,6 +185,7 @@ namespace libtorrent throw std::runtime_error(error_str); } } +*/ // TODO: make sure we don't step outside of the buffer ++pos; ++m_recv_pos; @@ -228,7 +229,7 @@ namespace libtorrent return buffer::const_interval(body_begin, body_end); } - + http_tracker_connection::http_tracker_connection( demuxer& d , tracker_manager& man @@ -241,16 +242,16 @@ namespace libtorrent , std::string const& auth) : tracker_connection(man, req, d, c) , m_man(man) - , m_state(read_status) - , m_content_encoding(plain) - , m_content_length(0) +// , m_state(read_status) +// , m_content_encoding(plain) +// , m_content_length(0) , m_name_lookup(d) , m_port(port) , m_recv_pos(0) , m_buffer(http_buffer_size) , m_settings(stn) , m_password(auth) - , m_code(0) +// , m_code(0) , m_timed_out(false) { const std::string* connect_to_host; @@ -506,6 +507,8 @@ namespace libtorrent #endif m_recv_pos += bytes_transferred; + m_parser.incoming(buffer::const_interval(&m_buffer[0] + , &m_buffer[0] + m_recv_pos)); // if the receive buffer is full, expand it with http_buffer_size if ((int)m_buffer.size() == m_recv_pos) @@ -523,156 +526,26 @@ namespace libtorrent m_buffer.resize(m_buffer.size() + http_buffer_size); } - if (m_state == read_status) + if (m_parser.header_finished()) { - std::vector::iterator end = m_buffer.begin()+m_recv_pos; - std::vector::iterator newline = std::find(m_buffer.begin(), end, '\n'); - // if we don't have a full line yet, wait. - if (newline != end) + int cl = m_parser.header("content-length"); + if (cl > m_settings.tracker_maximum_response_length) { - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) requester().debug_log(std::string(m_buffer.begin(), newline)); -#endif - - std::istringstream line(std::string(m_buffer.begin(), newline)); - ++newline; - m_recv_pos -= (int)std::distance(m_buffer.begin(), newline); - m_buffer.erase(m_buffer.begin(), newline); - - std::string protocol; - line >> m_server_protocol; - if (m_server_protocol.substr(0, 5) != "HTTP/") - { - std::string error_msg = "unknown protocol in response: " + m_server_protocol; - fail(-1, error_msg.c_str()); - return; - } - line >> m_code; - std::getline(line, m_server_message); - m_state = read_header; - } - } - - if (m_state == read_header) - { - std::vector::iterator end = m_buffer.begin() + m_recv_pos; - std::vector::iterator newline - = std::find(m_buffer.begin(), end, '\n'); - std::string line; - - while (newline != end && m_state == read_header) - { - line.assign(m_buffer.begin(), newline); - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) requester().debug_log(line); -#endif - - if (line.substr(0, 16) == "Content-Length: ") - { - try - { - m_content_length = boost::lexical_cast( - line.substr(16, line.length() - 17)); - } - catch(boost::bad_lexical_cast&) - { - fail(-1, "invalid content-length in tracker response"); - return; - } - if (m_content_length > m_settings.tracker_maximum_response_length) - { - fail(-1, "content-length is greater than maximum response length"); - return; - } - - if (m_content_length < minimum_tracker_response_length && m_code == 200) - { - fail(-1, "content-length is smaller than minimum response length"); - return; - } - } - else if (line.substr(0, 18) == "Content-Encoding: ") - { - if (line.substr(18, 4) == "gzip" || line.substr(18, 6) == "x-gzip") - { - m_content_encoding = gzip; - } - else - { - std::string error_str = "unknown content encoding in response: \""; - error_str += line.substr(18, line.length() - 18 - 2); - error_str += "\""; - fail(-1, error_str.c_str()); - return; - } - } - else if (line.substr(0, 10) == "Location: ") - { - m_location.assign(line.begin() + 10, line.end()); - } - else if (line.substr(0, 7) == "Server: ") - { - m_server.assign(line.begin() + 7, line.end()); - } - else if (line.size() < 3) - { - m_state = read_body; -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) requester().debug_log("end of http header"); -#endif - if (m_code >= 300 && m_code < 400) - { - if (m_location.empty()) - { - std::string error_str = "got redirection response ("; - error_str += boost::lexical_cast(m_code); - error_str += ") without 'Location' header"; - fail(-1, error_str.c_str()); - return; - } - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) requester().debug_log("Redirecting to \"" + m_location + "\""); -#endif - tracker_request req = tracker_req(); - std::string::size_type i = m_location.find('?'); - if (i == std::string::npos) - req.url = m_location; - else - req.url.assign(m_location.begin(), m_location.begin() + i); - - m_man.queue_request(m_socket->io_service(), req - , m_password, m_requester); - close(); - return; - } - } - - ++newline; - assert(m_recv_pos <= (int)m_buffer.size()); - m_recv_pos -= (int)std::distance(m_buffer.begin(), newline); - m_buffer.erase(m_buffer.begin(), newline); - assert(m_recv_pos <= (int)m_buffer.size()); - end = m_buffer.begin() + m_recv_pos; - newline = std::find(m_buffer.begin(), end, '\n'); + fail(-1, "content-length is greater than maximum response length"); + return; } - } - - if (m_state == read_body) - { - if (m_recv_pos == m_content_length) + if (cl < minimum_tracker_response_length && m_parser.status_code() == 200) { - on_response(); - close(); + fail(-1, "content-length is smaller than minimum response length"); return; } } - else if (m_recv_pos > m_content_length && m_content_length > 0) + + if (m_parser.finished()) { - fail(-1, "invalid tracker response (body > content_length)"); + on_response(); + close(); return; } @@ -683,14 +556,55 @@ namespace libtorrent } catch (std::exception& e) { - assert(false); fail(-1, e.what()); }; void http_tracker_connection::on_response() { - // GZIP - if (m_content_encoding == gzip) + if (!m_parser.finished()) + { + fail(-1, "premature end of file"); + return; + } + + std::string location = m_parser.header("location"); + + if (m_parser.status_code() >= 300 && m_parser.status_code() < 400) + { + if (location.empty()) + { + std::string error_str = "got redirection response ("; + error_str += boost::lexical_cast(m_parser.status_code()); + error_str += ") without 'Location' header"; + fail(-1, error_str.c_str()); + return; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log("Redirecting to \"" + location + "\""); +#endif + tracker_request req = tracker_req(); + std::string::size_type i = location.find('?'); + if (i == std::string::npos) + req.url = location; + else + req.url.assign(location.begin(), location.begin() + i); + + m_man.queue_request(m_socket->io_service(), req + , m_password, m_requester); + close(); + return; + } + + buffer::const_interval buf(&m_buffer[0] + m_parser.body_start(), &m_buffer[0] + m_recv_pos); + + std::string content_encoding = m_parser.header("content-encoding"); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log("content-encoding: \"" + content_encoding + "\""); +#endif + + if (content_encoding == "gzip" || content_encoding == "x-gzip") { boost::shared_ptr r = m_requester.lock(); @@ -699,26 +613,42 @@ namespace libtorrent close(); return; } + m_buffer.erase(m_buffer.begin(), m_buffer.begin() + m_parser.body_start()); if (inflate_gzip(m_buffer, tracker_request(), r.get(), m_settings.tracker_maximum_response_length)) { close(); return; } + buf.begin = &m_buffer[0]; + buf.end = &m_buffer[0] + m_buffer.size(); + } + else if (!content_encoding.empty()) + { + std::string error_str = "unknown content encoding in response: \""; + error_str += content_encoding; + error_str += "\""; + fail(-1, error_str.c_str()); + return; } // handle tracker response try { - entry e = bdecode(m_buffer.begin(), m_buffer.end()); + entry e = bdecode(buf.begin, buf.end); parse(e); } catch (std::exception& e) { std::string error_str(e.what()); - error_str += ": "; - error_str.append(m_buffer.begin(), m_buffer.end()); - fail(m_code, error_str.c_str()); + error_str += ": \""; + for (char const* i = buf.begin, *end(buf.end); i != end; ++i) + { + if (std::isprint(*i)) error_str += *i; + else error_str += "0x" + boost::lexical_cast((unsigned int)*i) + " "; + } + error_str += "\""; + fail(m_parser.status_code(), error_str.c_str()); } #ifndef NDEBUG catch (...) @@ -770,7 +700,7 @@ namespace libtorrent { entry const& failure = e["failure reason"]; - fail(m_code, failure.string().c_str()); + fail(m_parser.status_code(), failure.string().c_str()); return; } catch (type_error const&) {} @@ -845,11 +775,11 @@ namespace libtorrent } catch(type_error& e) { - requester().tracker_request_error(tracker_request(), m_code, e.what()); + requester().tracker_request_error(tracker_request(), m_parser.status_code(), e.what()); } catch(std::runtime_error& e) { - requester().tracker_request_error(tracker_request(), m_code, e.what()); + requester().tracker_request_error(tracker_request(), m_parser.status_code(), e.what()); } } diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index a02de9ee1..e940ac05b 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -1111,7 +1111,11 @@ namespace libtorrent // i.e. all the pieces we're interested in have // been downloaded. Release the files (they will open // in read only mode if needed) - t->finished(); + try { t->finished(); } + catch (std::exception&) + { + assert(false); + } } } else diff --git a/src/web_peer_connection.cpp b/src/web_peer_connection.cpp index 36d241932..e9a10d205 100755 --- a/src/web_peer_connection.cpp +++ b/src/web_peer_connection.cpp @@ -283,7 +283,7 @@ namespace libtorrent if (!m_parser.finished()) break; - std::string server_version = m_parser.header("Server"); + std::string server_version = m_parser.header("server"); if (!server_version.empty()) { m_server_string = "URL seed @ "; @@ -293,7 +293,7 @@ namespace libtorrent m_server_string += ")"; } - std::stringstream range_str(m_parser.header("Content-Range")); + std::stringstream range_str(m_parser.header("content-range")); size_type range_start; size_type range_end; char dummy;