changed http_parser slightly. uses http_parser in lsd.cpp. added more tests to the http_parser

This commit is contained in:
Arvid Norberg 2007-09-25 03:14:05 +00:00
parent 1df7c2006a
commit d48236a738
7 changed files with 99 additions and 61 deletions

View File

@ -69,13 +69,20 @@ namespace libtorrent
{ {
public: public:
http_parser(); http_parser();
template <class T> std::string const& header(char const* key) const
T header(char const* key) const; {
static std::string empty;
std::map<std::string, std::string>::const_iterator i
= m_header.find(key);
if (i == m_header.end()) return empty;
return i->second;
}
std::string const& protocol() const { return m_protocol; } std::string const& protocol() const { return m_protocol; }
int status_code() const { return m_status_code; } int status_code() const { return m_status_code; }
std::string const& method() const { return m_method; } std::string const& method() const { return m_method; }
std::string const& path() const { return m_path; } std::string const& path() const { return m_path; }
std::string message() const { return m_server_message; } std::string const& message() const { return m_server_message; }
buffer::const_interval get_body() const; buffer::const_interval get_body() const;
bool header_finished() const { return m_state == read_body; } bool header_finished() const { return m_state == read_body; }
bool finished() const { return m_finished; } bool finished() const { return m_finished; }
@ -103,15 +110,6 @@ namespace libtorrent
bool m_finished; bool m_finished;
}; };
template <class T>
T http_parser::header(char const* key) const
{
std::map<std::string, std::string>::const_iterator i
= m_header.find(key);
if (i == m_header.end()) return T();
return boost::lexical_cast<T>(i->second);
}
class TORRENT_EXPORT http_tracker_connection class TORRENT_EXPORT http_tracker_connection
: public tracker_connection : public tracker_connection
{ {

View File

@ -263,7 +263,7 @@ void http_connection::on_read(asio::error_code const& e
if (code >= 300 && code < 400) if (code >= 300 && code < 400)
{ {
// attempt a redirect // attempt a redirect
std::string url = m_parser.header<std::string>("location"); std::string const& url = m_parser.header("location");
if (url.empty()) if (url.empty())
{ {
// missing location header // missing location header

View File

@ -679,7 +679,7 @@ namespace libtorrent
if (m_parser.header_finished()) if (m_parser.header_finished())
{ {
int cl = m_parser.header<int>("content-length"); int cl = atoi(m_parser.header("content-length").c_str());
if (cl > m_settings.tracker_maximum_response_length) if (cl > m_settings.tracker_maximum_response_length)
{ {
fail(-1, "content-length is greater than maximum response length"); fail(-1, "content-length is greater than maximum response length");
@ -718,7 +718,7 @@ namespace libtorrent
return; return;
} }
std::string location = m_parser.header<std::string>("location"); std::string location = m_parser.header("location");
boost::shared_ptr<request_callback> cb = requester(); boost::shared_ptr<request_callback> cb = requester();
@ -763,7 +763,7 @@ namespace libtorrent
buffer::const_interval buf(&m_buffer[0] + m_parser.body_start(), &m_buffer[0] + m_recv_pos); buffer::const_interval buf(&m_buffer[0] + m_parser.body_start(), &m_buffer[0] + m_recv_pos);
std::string content_encoding = m_parser.header<std::string>("content-encoding"); std::string content_encoding = m_parser.header("content-encoding");
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (cb) cb->debug_log("content-encoding: \"" + content_encoding + "\""); if (cb) cb->debug_log("content-encoding: \"" + content_encoding + "\"");

View File

@ -121,48 +121,53 @@ void lsd::on_announce(udp::endpoint const& from, char* buffer
{ {
using namespace libtorrent::detail; using namespace libtorrent::detail;
char* p = buffer; http_parser p;
char* end = buffer + bytes_transferred;
char* line = std::find(p, end, '\n'); p.incoming(buffer::const_interval(buffer, buffer + bytes_transferred));
for (char* i = p; i < line; ++i) *i = std::tolower(*i);
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) if (!p.header_finished())
m_log << time_now_string()
<< " <== announce: " << std::string(p, line) << std::endl;
#endif
if (line == end || (line - p >= 9 && std::memcmp("bt-search", p, 9)))
{ {
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) #if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
m_log << time_now_string() m_log << time_now_string()
<< " *** assumed 'bt-search', ignoring" << std::endl; << " <== announce: incomplete HTTP message\n";
#endif #endif
return; return;
} }
p = line + 1;
int port = 0; if (p.method() != "bt-search")
sha1_hash ih(0);
while (p != end)
{ {
line = std::find(p, end, '\n'); #if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
if (line == end) break; m_log << time_now_string()
*line = 0; << " <== announce: invalid HTTP method: " << p.method() << std::endl;
for (char* i = p; i < line; ++i) *i = std::tolower(*i); #endif
if (line - p >= 5 && memcmp(p, "port:", 5) == 0) return;
{
p += 5;
while (*p == ' ') ++p;
port = atoi(p);
}
else if (line - p >= 9 && memcmp(p, "infohash:", 9) == 0)
{
p += 9;
while (*p == ' ') ++p;
if (line - p > 40) p[40] = 0;
try { ih = boost::lexical_cast<sha1_hash>(p); }
catch (std::exception&) {}
}
p = line + 1;
} }
std::string const& port_str = p.header("port");
if (port_str.empty())
{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
m_log << time_now_string()
<< " <== announce: invalid BT-SEARCH, missing port" << std::endl;
#endif
return;
}
std::string const& ih_str = p.header("infohash");
if (ih_str.empty())
{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
m_log << time_now_string()
<< " <== announce: invalid BT-SEARCH, missing infohash" << std::endl;
#endif
return;
}
sha1_hash ih(0);
std::istringstream ih_sstr(ih_str);
ih_sstr >> ih;
int port = atoi(port_str.c_str());
if (!ih.is_all_zeros() && port != 0) if (!ih.is_all_zeros() && port != 0)
{ {
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) #if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)

View File

@ -300,7 +300,7 @@ try
return; return;
} }
std::string url = p.header<std::string>("location"); std::string url = p.header("location");
if (url.empty()) if (url.empty())
{ {
#ifdef TORRENT_UPNP_LOGGING #ifdef TORRENT_UPNP_LOGGING

View File

@ -387,7 +387,7 @@ namespace libtorrent
{ {
// this means we got a redirection request // this means we got a redirection request
// look for the location header // look for the location header
std::string location = m_parser.header<std::string>("location"); std::string location = m_parser.header("location");
if (location.empty()) if (location.empty())
{ {
@ -423,7 +423,7 @@ namespace libtorrent
throw std::runtime_error("redirecting to " + location); throw std::runtime_error("redirecting to " + location);
} }
std::string server_version = m_parser.header<std::string>("server"); std::string const& server_version = m_parser.header("server");
if (!server_version.empty()) if (!server_version.empty())
{ {
m_server_string = "URL seed @ "; m_server_string = "URL seed @ ";
@ -445,7 +445,7 @@ namespace libtorrent
size_type range_end; size_type range_end;
if (m_parser.status_code() == 206) if (m_parser.status_code() == 206)
{ {
std::stringstream range_str(m_parser.header<std::string>("content-range")); std::stringstream range_str(m_parser.header("content-range"));
char dummy; char dummy;
std::string bytes; std::string bytes;
range_str >> bytes >> range_start >> dummy >> range_end; range_str >> bytes >> range_start >> dummy >> range_end;
@ -461,7 +461,7 @@ namespace libtorrent
else else
{ {
range_start = 0; range_start = 0;
range_end = m_parser.header<size_type>("content-length"); range_end = atol(m_parser.header("content-length").c_str());
if (range_end == -1) if (range_end == -1)
{ {
// we should not try this server again. // we should not try this server again.

View File

@ -102,8 +102,8 @@ int test_main()
TEST_CHECK(received == make_tuple(4, 64)); TEST_CHECK(received == make_tuple(4, 64));
TEST_CHECK(parser.finished()); TEST_CHECK(parser.finished());
TEST_CHECK(std::equal(parser.get_body().begin, parser.get_body().end, "test")); TEST_CHECK(std::equal(parser.get_body().begin, parser.get_body().end, "test"));
TEST_CHECK(parser.header<std::string>("content-type") == "text/plain"); TEST_CHECK(parser.header("content-type") == "text/plain");
TEST_CHECK(parser.header<int>("content-length") == 4); TEST_CHECK(atoi(parser.header("content-length").c_str()) == 4);
parser.reset(); parser.reset();
@ -123,11 +123,11 @@ int test_main()
TEST_CHECK(received == make_tuple(0, int(strlen(upnp_response)))); TEST_CHECK(received == make_tuple(0, int(strlen(upnp_response))));
TEST_CHECK(parser.get_body().left() == 0); TEST_CHECK(parser.get_body().left() == 0);
TEST_CHECK(parser.header<std::string>("st") == "upnp:rootdevice"); TEST_CHECK(parser.header("st") == "upnp:rootdevice");
TEST_CHECK(parser.header<std::string>("location") TEST_CHECK(parser.header("location")
== "http://192.168.1.1:5431/dyndev/uuid:000f-66d6-7296000099dc"); == "http://192.168.1.1:5431/dyndev/uuid:000f-66d6-7296000099dc");
TEST_CHECK(parser.header<std::string>("ext") == ""); TEST_CHECK(parser.header("ext") == "");
TEST_CHECK(parser.header<std::string>("date") == "Fri, 02 Jan 1970 08:10:38 GMT"); TEST_CHECK(parser.header("date") == "Fri, 02 Jan 1970 08:10:38 GMT");
parser.reset(); parser.reset();
TEST_CHECK(!parser.finished()); TEST_CHECK(!parser.finished());
@ -148,6 +148,41 @@ int test_main()
TEST_CHECK(parser.method() == "notify"); TEST_CHECK(parser.method() == "notify");
TEST_CHECK(parser.path() == "*"); TEST_CHECK(parser.path() == "*");
parser.reset();
TEST_CHECK(!parser.finished());
char const* bt_lsd = "BT-SEARCH * HTTP/1.1\r\n"
"Host: 239.192.152.143:6771\r\n"
"Port: 6881\r\n"
"Infohash: 12345678901234567890\r\n"
"\r\n\r\n";
received = feed_bytes(parser, bt_lsd);
TEST_CHECK(received == make_tuple(2, int(strlen(bt_lsd) - 2)));
TEST_CHECK(parser.method() == "bt-search");
TEST_CHECK(parser.path() == "*");
TEST_CHECK(atoi(parser.header("port").c_str()) == 6881);
TEST_CHECK(parser.header("infohash") == "12345678901234567890");
TEST_CHECK(!parser.finished());
parser.reset();
TEST_CHECK(!parser.finished());
// make sure we support trackers with incorrect line endings
char const* tracker_response =
"HTTP/1.1 200 OK\n"
"content-length: 5\n"
"content-type: test/plain\n"
"\n"
"\ntest";
received = feed_bytes(parser, tracker_response);
TEST_CHECK(received == make_tuple(5, int(strlen(tracker_response) - 5)));
TEST_CHECK(parser.get_body().left() == 5);
// test xml parser // test xml parser
char xml1[] = "<a>foo<b/>bar</a>"; char xml1[] = "<a>foo<b/>bar</a>";