forked from premiere/premiere-libtorrent
refactored gzip code and added gzip support to http_connection
This commit is contained in:
parent
ebde862341
commit
6caca17883
1
Jamfile
1
Jamfile
|
@ -228,6 +228,7 @@ SOURCES =
|
||||||
connection_queue
|
connection_queue
|
||||||
entry
|
entry
|
||||||
escape_string
|
escape_string
|
||||||
|
gzip
|
||||||
http_connection
|
http_connection
|
||||||
http_stream
|
http_stream
|
||||||
http_parser
|
http_parser
|
||||||
|
|
|
@ -18,6 +18,7 @@ libtorrent/extensions.hpp \
|
||||||
libtorrent/file.hpp \
|
libtorrent/file.hpp \
|
||||||
libtorrent/file_pool.hpp \
|
libtorrent/file_pool.hpp \
|
||||||
libtorrent/fingerprint.hpp \
|
libtorrent/fingerprint.hpp \
|
||||||
|
libtorrent/gzip.hpp \
|
||||||
libtorrent/hasher.hpp \
|
libtorrent/hasher.hpp \
|
||||||
libtorrent/http_connection.hpp \
|
libtorrent/http_connection.hpp \
|
||||||
libtorrent/http_stream.hpp \
|
libtorrent/http_stream.hpp \
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 2007, Arvid Norberg
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in
|
||||||
|
the documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the author nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace libtorrent
|
||||||
|
{
|
||||||
|
|
||||||
|
TORRENT_EXPORT bool inflate_gzip(
|
||||||
|
char const* in, int size
|
||||||
|
, std::vector<char>& buffer
|
||||||
|
, int maximum_size
|
||||||
|
, std::string& error);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -63,7 +63,6 @@ typedef boost::function<void(asio::error_code const&
|
||||||
typedef boost::function<void(http_connection&)> http_connect_handler;
|
typedef boost::function<void(http_connection&)> http_connect_handler;
|
||||||
|
|
||||||
// TODO: add bind interface
|
// TODO: add bind interface
|
||||||
// TODO: add gzip support
|
|
||||||
|
|
||||||
// when bottled, the last two arguments to the handler
|
// when bottled, the last two arguments to the handler
|
||||||
// will always be 0
|
// will always be 0
|
||||||
|
@ -101,7 +100,8 @@ struct http_connection : boost::enable_shared_from_this<http_connection>, boost:
|
||||||
std::string sendbuffer;
|
std::string sendbuffer;
|
||||||
|
|
||||||
void get(std::string const& url, time_duration timeout = seconds(30)
|
void get(std::string const& url, time_duration timeout = seconds(30)
|
||||||
, proxy_settings const* ps = 0, int handle_redirects = 5);
|
, proxy_settings const* ps = 0, int handle_redirects = 5
|
||||||
|
, std::string const& user_agent = "");
|
||||||
|
|
||||||
void start(std::string const& hostname, std::string const& port
|
void start(std::string const& hostname, std::string const& port
|
||||||
, time_duration timeout, proxy_settings const* ps = 0, bool ssl = false
|
, time_duration timeout, proxy_settings const* ps = 0, bool ssl = false
|
||||||
|
|
|
@ -143,12 +143,6 @@ namespace libtorrent
|
||||||
tracker_manager* m_manager;
|
tracker_manager* m_manager;
|
||||||
};
|
};
|
||||||
|
|
||||||
TORRENT_EXPORT bool inflate_gzip(
|
|
||||||
std::vector<char>& buffer
|
|
||||||
, tracker_request const& req
|
|
||||||
, request_callback* requester
|
|
||||||
, int maximum_tracker_response_length);
|
|
||||||
|
|
||||||
struct TORRENT_EXPORT timeout_handler
|
struct TORRENT_EXPORT timeout_handler
|
||||||
: intrusive_ptr_base<timeout_handler>
|
: intrusive_ptr_base<timeout_handler>
|
||||||
, boost::noncopyable
|
, boost::noncopyable
|
||||||
|
|
|
@ -23,7 +23,7 @@ alert.cpp identify_client.cpp ip_filter.cpp file.cpp metadata_transfer.cpp \
|
||||||
logger.cpp file_pool.cpp ut_pex.cpp lsd.cpp upnp.cpp instantiate_connection.cpp \
|
logger.cpp file_pool.cpp ut_pex.cpp lsd.cpp upnp.cpp instantiate_connection.cpp \
|
||||||
socks5_stream.cpp socks4_stream.cpp http_stream.cpp connection_queue.cpp \
|
socks5_stream.cpp socks4_stream.cpp http_stream.cpp connection_queue.cpp \
|
||||||
disk_io_thread.cpp ut_metadata.cpp magnet_uri.cpp udp_socket.cpp smart_ban.cpp \
|
disk_io_thread.cpp ut_metadata.cpp magnet_uri.cpp udp_socket.cpp smart_ban.cpp \
|
||||||
http_parser.cpp $(kademlia_sources)
|
http_parser.cpp gzip.cpp $(kademlia_sources)
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
$(top_srcdir)/include/libtorrent/alert.hpp \
|
$(top_srcdir)/include/libtorrent/alert.hpp \
|
||||||
|
@ -49,6 +49,7 @@ $(top_srcdir)/include/libtorrent/extensions/ut_pex.hpp \
|
||||||
$(top_srcdir)/include/libtorrent/file.hpp \
|
$(top_srcdir)/include/libtorrent/file.hpp \
|
||||||
$(top_srcdir)/include/libtorrent/file_pool.hpp \
|
$(top_srcdir)/include/libtorrent/file_pool.hpp \
|
||||||
$(top_srcdir)/include/libtorrent/fingerprint.hpp \
|
$(top_srcdir)/include/libtorrent/fingerprint.hpp \
|
||||||
|
$(top_srcdir)/include/libtorrent/gzip.hpp \
|
||||||
$(top_srcdir)/include/libtorrent/hasher.hpp \
|
$(top_srcdir)/include/libtorrent/hasher.hpp \
|
||||||
$(top_srcdir)/include/libtorrent/http_connection.hpp \
|
$(top_srcdir)/include/libtorrent/http_connection.hpp \
|
||||||
$(top_srcdir)/include/libtorrent/http_stream.hpp \
|
$(top_srcdir)/include/libtorrent/http_stream.hpp \
|
||||||
|
|
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 2007, Arvid Norberg
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in
|
||||||
|
the documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the author nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libtorrent/assert.hpp"
|
||||||
|
|
||||||
|
#include "zlib.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
FTEXT = 0x01,
|
||||||
|
FHCRC = 0x02,
|
||||||
|
FEXTRA = 0x04,
|
||||||
|
FNAME = 0x08,
|
||||||
|
FCOMMENT = 0x10,
|
||||||
|
FRESERVED = 0xe0,
|
||||||
|
|
||||||
|
GZIP_MAGIC0 = 0x1f,
|
||||||
|
GZIP_MAGIC1 = 0x8b
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace libtorrent
|
||||||
|
{
|
||||||
|
// returns -1 if gzip header is invalid or the header size in bytes
|
||||||
|
int gzip_header(const char* buf, int size)
|
||||||
|
{
|
||||||
|
TORRENT_ASSERT(buf != 0);
|
||||||
|
TORRENT_ASSERT(size > 0);
|
||||||
|
|
||||||
|
const unsigned char* buffer = reinterpret_cast<const unsigned char*>(buf);
|
||||||
|
const int total_size = size;
|
||||||
|
|
||||||
|
// The zip header cannot be shorter than 10 bytes
|
||||||
|
if (size < 10) return -1;
|
||||||
|
|
||||||
|
// check the magic header of gzip
|
||||||
|
if ((buffer[0] != GZIP_MAGIC0) || (buffer[1] != GZIP_MAGIC1)) return -1;
|
||||||
|
|
||||||
|
int method = buffer[2];
|
||||||
|
int flags = buffer[3];
|
||||||
|
|
||||||
|
// check for reserved flag and make sure it's compressed with the correct metod
|
||||||
|
if (method != Z_DEFLATED || (flags & FRESERVED) != 0) return -1;
|
||||||
|
|
||||||
|
// skip time, xflags, OS code
|
||||||
|
size -= 10;
|
||||||
|
buffer += 10;
|
||||||
|
|
||||||
|
if (flags & FEXTRA)
|
||||||
|
{
|
||||||
|
int extra_len;
|
||||||
|
|
||||||
|
if (size < 2) return -1;
|
||||||
|
|
||||||
|
extra_len = (buffer[1] << 8) | buffer[0];
|
||||||
|
|
||||||
|
if (size < (extra_len+2)) return -1;
|
||||||
|
size -= (extra_len + 2);
|
||||||
|
buffer += (extra_len + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & FNAME)
|
||||||
|
{
|
||||||
|
while (size && *buffer)
|
||||||
|
{
|
||||||
|
--size;
|
||||||
|
++buffer;
|
||||||
|
}
|
||||||
|
if (!size || *buffer) return -1;
|
||||||
|
|
||||||
|
--size;
|
||||||
|
++buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & FCOMMENT)
|
||||||
|
{
|
||||||
|
while (size && *buffer)
|
||||||
|
{
|
||||||
|
--size;
|
||||||
|
++buffer;
|
||||||
|
}
|
||||||
|
if (!size || *buffer) return -1;
|
||||||
|
|
||||||
|
--size;
|
||||||
|
++buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & FHCRC)
|
||||||
|
{
|
||||||
|
if (size < 2) return -1;
|
||||||
|
|
||||||
|
size -= 2;
|
||||||
|
buffer += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return total_size - size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inflate_gzip(
|
||||||
|
char const* in
|
||||||
|
, int size
|
||||||
|
, std::vector<char>& buffer
|
||||||
|
, int maximum_size
|
||||||
|
, std::string& error)
|
||||||
|
{
|
||||||
|
TORRENT_ASSERT(maximum_size > 0);
|
||||||
|
|
||||||
|
int header_len = gzip_header(in, size);
|
||||||
|
if (header_len < 0)
|
||||||
|
{
|
||||||
|
error = "invalid gzip header in tracker response";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// start off with one kilobyte and grow
|
||||||
|
// if needed
|
||||||
|
buffer.resize(1024);
|
||||||
|
|
||||||
|
// initialize the zlib-stream
|
||||||
|
z_stream str;
|
||||||
|
|
||||||
|
// subtract 8 from the end of the buffer since that's CRC32 and input size
|
||||||
|
// and those belong to the gzip file
|
||||||
|
str.avail_in = (int)size - header_len - 8;
|
||||||
|
str.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(in + header_len));
|
||||||
|
str.next_out = reinterpret_cast<Bytef*>(&buffer[0]);
|
||||||
|
str.avail_out = (int)buffer.size();
|
||||||
|
str.zalloc = Z_NULL;
|
||||||
|
str.zfree = Z_NULL;
|
||||||
|
str.opaque = 0;
|
||||||
|
// -15 is really important. It will make inflate() not look for a zlib header
|
||||||
|
// and just deflate the buffer
|
||||||
|
if (inflateInit2(&str, -15) != Z_OK)
|
||||||
|
{
|
||||||
|
error = "gzip out of memory";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// inflate and grow inflate_buffer as needed
|
||||||
|
int ret = inflate(&str, Z_SYNC_FLUSH);
|
||||||
|
while (ret == Z_OK)
|
||||||
|
{
|
||||||
|
if (str.avail_out == 0)
|
||||||
|
{
|
||||||
|
if (buffer.size() >= (unsigned)maximum_size)
|
||||||
|
{
|
||||||
|
inflateEnd(&str);
|
||||||
|
error = "response too large";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int new_size = (int)buffer.size() * 2;
|
||||||
|
if (new_size > maximum_size)
|
||||||
|
new_size = maximum_size;
|
||||||
|
int old_size = (int)buffer.size();
|
||||||
|
|
||||||
|
buffer.resize(new_size);
|
||||||
|
str.next_out = reinterpret_cast<Bytef*>(&buffer[old_size]);
|
||||||
|
str.avail_out = new_size - old_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = inflate(&str, Z_SYNC_FLUSH);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.resize(buffer.size() - str.avail_out);
|
||||||
|
inflateEnd(&str);
|
||||||
|
|
||||||
|
if (ret != Z_STREAM_END)
|
||||||
|
{
|
||||||
|
error = "gzip error";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// commit the resulting buffer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "libtorrent/http_connection.hpp"
|
#include "libtorrent/http_connection.hpp"
|
||||||
#include "libtorrent/escape_string.hpp"
|
#include "libtorrent/escape_string.hpp"
|
||||||
#include "libtorrent/instantiate_connection.hpp"
|
#include "libtorrent/instantiate_connection.hpp"
|
||||||
|
#include "libtorrent/gzip.hpp"
|
||||||
|
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
|
@ -41,13 +42,18 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
using boost::bind;
|
using boost::bind;
|
||||||
|
|
||||||
namespace libtorrent
|
namespace libtorrent {
|
||||||
{
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
char to_lower(char c) { return std::tolower(c); }
|
||||||
|
}
|
||||||
|
|
||||||
|
enum { max_bottled_buffer = 1024 * 1024 };
|
||||||
|
|
||||||
enum { max_bottled_buffer = 1024 * 1024 };
|
|
||||||
|
|
||||||
void http_connection::get(std::string const& url, time_duration timeout
|
void http_connection::get(std::string const& url, time_duration timeout
|
||||||
, proxy_settings const* ps, int handle_redirects)
|
, proxy_settings const* ps, int handle_redirects, std::string const& user_agent)
|
||||||
{
|
{
|
||||||
std::string protocol;
|
std::string protocol;
|
||||||
std::string auth;
|
std::string auth;
|
||||||
|
@ -73,8 +79,7 @@ void http_connection::get(std::string const& url, time_duration timeout
|
||||||
{
|
{
|
||||||
// if we're using an http proxy and not an ssl
|
// if we're using an http proxy and not an ssl
|
||||||
// connection, just do a regular http proxy request
|
// connection, just do a regular http proxy request
|
||||||
headers << "GET " << url << " HTTP/1.0\r\n"
|
headers << "GET " << url << " HTTP/1.0\r\n";
|
||||||
"Connection: close\r\n";
|
|
||||||
if (ps->type == proxy_settings::http_pw)
|
if (ps->type == proxy_settings::http_pw)
|
||||||
headers << "Proxy-Authorization: Basic " << base64encode(
|
headers << "Proxy-Authorization: Basic " << base64encode(
|
||||||
ps->username + ":" + ps->password) << "\r\n";
|
ps->username + ":" + ps->password) << "\r\n";
|
||||||
|
@ -85,13 +90,20 @@ void http_connection::get(std::string const& url, time_duration timeout
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
headers << "GET " << path << " HTTP/1.0\r\n"
|
headers << "GET " << path << " HTTP/1.0\r\n"
|
||||||
"Host:" << hostname << "\r\n"
|
"Host:" << hostname << "\r\n";
|
||||||
"Connection: close\r\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!auth.empty())
|
if (!auth.empty())
|
||||||
headers << "Authorization: Basic " << base64encode(auth) << "\r\n";
|
headers << "Authorization: Basic " << base64encode(auth) << "\r\n";
|
||||||
headers << "\r\n";
|
|
||||||
|
if (!user_agent.empty())
|
||||||
|
headers << "User-Agent: " << user_agent << "\r\n";
|
||||||
|
|
||||||
|
headers <<
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"Accept-Encoding: gzip\r\n"
|
||||||
|
"\r\n";
|
||||||
|
|
||||||
sendbuffer = headers.str();
|
sendbuffer = headers.str();
|
||||||
start(hostname, boost::lexical_cast<std::string>(port), timeout, ps
|
start(hostname, boost::lexical_cast<std::string>(port), timeout, ps
|
||||||
, ssl, handle_redirects);
|
, ssl, handle_redirects);
|
||||||
|
@ -250,6 +262,23 @@ void http_connection::callback(asio::error_code const& e, char const* data, int
|
||||||
{
|
{
|
||||||
if (!m_bottled || !m_called)
|
if (!m_bottled || !m_called)
|
||||||
{
|
{
|
||||||
|
std::vector<char> buf;
|
||||||
|
if (m_bottled && m_parser.finished())
|
||||||
|
{
|
||||||
|
std::string const& encoding = m_parser.header("content-encoding");
|
||||||
|
if (encoding == "gzip" || encoding == "x-gzip")
|
||||||
|
{
|
||||||
|
std::string error;
|
||||||
|
if (inflate_gzip(data, size, buf, max_bottled_buffer, error))
|
||||||
|
{
|
||||||
|
callback(asio::error::fault, data, size);
|
||||||
|
close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data = &buf[0];
|
||||||
|
size = int(buf.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
m_called = true;
|
m_called = true;
|
||||||
m_timer.cancel();
|
m_timer.cancel();
|
||||||
if (m_handler) m_handler(e, m_parser, data, size);
|
if (m_handler) m_handler(e, m_parser, data, size);
|
||||||
|
|
|
@ -40,7 +40,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "libtorrent/config.hpp"
|
#include "libtorrent/config.hpp"
|
||||||
#include "zlib.h"
|
#include "libtorrent/gzip.hpp"
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(push, 1)
|
#pragma warning(push, 1)
|
||||||
|
@ -70,21 +70,6 @@ namespace
|
||||||
minimum_tracker_response_length = 3,
|
minimum_tracker_response_length = 3,
|
||||||
http_buffer_size = 2048
|
http_buffer_size = 2048
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
FTEXT = 0x01,
|
|
||||||
FHCRC = 0x02,
|
|
||||||
FEXTRA = 0x04,
|
|
||||||
FNAME = 0x08,
|
|
||||||
FCOMMENT = 0x10,
|
|
||||||
FRESERVED = 0xe0,
|
|
||||||
|
|
||||||
GZIP_MAGIC0 = 0x1f,
|
|
||||||
GZIP_MAGIC1 = 0x8b
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -637,13 +622,16 @@ namespace libtorrent
|
||||||
close();
|
close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + m_parser.body_start());
|
std::vector<char> buffer;
|
||||||
if (inflate_gzip(m_buffer, tracker_req(), cb.get(),
|
std::string error;
|
||||||
m_settings.tracker_maximum_response_length))
|
if (inflate_gzip(&m_buffer[0] + m_parser.body_start(), m_buffer.size(), buffer
|
||||||
|
, m_settings.tracker_maximum_response_length, error))
|
||||||
{
|
{
|
||||||
|
cb->tracker_request_error(tracker_req(), 200, error);
|
||||||
close();
|
close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
m_buffer.swap(buffer);
|
||||||
buf.begin = &m_buffer[0];
|
buf.begin = &m_buffer[0];
|
||||||
buf.end = &m_buffer[0] + m_buffer.size();
|
buf.end = &m_buffer[0] + m_buffer.size();
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "zlib.h"
|
|
||||||
|
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
|
|
||||||
#include "libtorrent/tracker_manager.hpp"
|
#include "libtorrent/tracker_manager.hpp"
|
||||||
|
@ -63,177 +61,10 @@ namespace
|
||||||
http_buffer_size = 2048
|
http_buffer_size = 2048
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
FTEXT = 0x01,
|
|
||||||
FHCRC = 0x02,
|
|
||||||
FEXTRA = 0x04,
|
|
||||||
FNAME = 0x08,
|
|
||||||
FCOMMENT = 0x10,
|
|
||||||
FRESERVED = 0xe0,
|
|
||||||
|
|
||||||
GZIP_MAGIC0 = 0x1f,
|
|
||||||
GZIP_MAGIC1 = 0x8b
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace libtorrent
|
namespace libtorrent
|
||||||
{
|
{
|
||||||
// returns -1 if gzip header is invalid or the header size in bytes
|
|
||||||
int gzip_header(const char* buf, int size)
|
|
||||||
{
|
|
||||||
TORRENT_ASSERT(buf != 0);
|
|
||||||
TORRENT_ASSERT(size > 0);
|
|
||||||
|
|
||||||
const unsigned char* buffer = reinterpret_cast<const unsigned char*>(buf);
|
|
||||||
const int total_size = size;
|
|
||||||
|
|
||||||
// The zip header cannot be shorter than 10 bytes
|
|
||||||
if (size < 10) return -1;
|
|
||||||
|
|
||||||
// check the magic header of gzip
|
|
||||||
if ((buffer[0] != GZIP_MAGIC0) || (buffer[1] != GZIP_MAGIC1)) return -1;
|
|
||||||
|
|
||||||
int method = buffer[2];
|
|
||||||
int flags = buffer[3];
|
|
||||||
|
|
||||||
// check for reserved flag and make sure it's compressed with the correct metod
|
|
||||||
if (method != Z_DEFLATED || (flags & FRESERVED) != 0) return -1;
|
|
||||||
|
|
||||||
// skip time, xflags, OS code
|
|
||||||
size -= 10;
|
|
||||||
buffer += 10;
|
|
||||||
|
|
||||||
if (flags & FEXTRA)
|
|
||||||
{
|
|
||||||
int extra_len;
|
|
||||||
|
|
||||||
if (size < 2) return -1;
|
|
||||||
|
|
||||||
extra_len = (buffer[1] << 8) | buffer[0];
|
|
||||||
|
|
||||||
if (size < (extra_len+2)) return -1;
|
|
||||||
size -= (extra_len + 2);
|
|
||||||
buffer += (extra_len + 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & FNAME)
|
|
||||||
{
|
|
||||||
while (size && *buffer)
|
|
||||||
{
|
|
||||||
--size;
|
|
||||||
++buffer;
|
|
||||||
}
|
|
||||||
if (!size || *buffer) return -1;
|
|
||||||
|
|
||||||
--size;
|
|
||||||
++buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & FCOMMENT)
|
|
||||||
{
|
|
||||||
while (size && *buffer)
|
|
||||||
{
|
|
||||||
--size;
|
|
||||||
++buffer;
|
|
||||||
}
|
|
||||||
if (!size || *buffer) return -1;
|
|
||||||
|
|
||||||
--size;
|
|
||||||
++buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & FHCRC)
|
|
||||||
{
|
|
||||||
if (size < 2) return -1;
|
|
||||||
|
|
||||||
size -= 2;
|
|
||||||
buffer += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return total_size - size;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool inflate_gzip(
|
|
||||||
std::vector<char>& buffer
|
|
||||||
, tracker_request const& req
|
|
||||||
, request_callback* requester
|
|
||||||
, int maximum_tracker_response_length)
|
|
||||||
{
|
|
||||||
TORRENT_ASSERT(maximum_tracker_response_length > 0);
|
|
||||||
|
|
||||||
int header_len = gzip_header(&buffer[0], (int)buffer.size());
|
|
||||||
if (header_len < 0)
|
|
||||||
{
|
|
||||||
requester->tracker_request_error(req, 200, "invalid gzip header in tracker response");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// start off wth one kilobyte and grow
|
|
||||||
// if needed
|
|
||||||
std::vector<char> inflate_buffer(1024);
|
|
||||||
|
|
||||||
// initialize the zlib-stream
|
|
||||||
z_stream str;
|
|
||||||
|
|
||||||
// subtract 8 from the end of the buffer since that's CRC32 and input size
|
|
||||||
// and those belong to the gzip file
|
|
||||||
str.avail_in = (int)buffer.size() - header_len - 8;
|
|
||||||
str.next_in = reinterpret_cast<Bytef*>(&buffer[header_len]);
|
|
||||||
str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[0]);
|
|
||||||
str.avail_out = (int)inflate_buffer.size();
|
|
||||||
str.zalloc = Z_NULL;
|
|
||||||
str.zfree = Z_NULL;
|
|
||||||
str.opaque = 0;
|
|
||||||
// -15 is really important. It will make inflate() not look for a zlib header
|
|
||||||
// and just deflate the buffer
|
|
||||||
if (inflateInit2(&str, -15) != Z_OK)
|
|
||||||
{
|
|
||||||
requester->tracker_request_error(req, 200, "gzip out of memory");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// inflate and grow inflate_buffer as needed
|
|
||||||
int ret = inflate(&str, Z_SYNC_FLUSH);
|
|
||||||
while (ret == Z_OK)
|
|
||||||
{
|
|
||||||
if (str.avail_out == 0)
|
|
||||||
{
|
|
||||||
if (inflate_buffer.size() >= (unsigned)maximum_tracker_response_length)
|
|
||||||
{
|
|
||||||
inflateEnd(&str);
|
|
||||||
requester->tracker_request_error(req, 200
|
|
||||||
, "tracker response too large");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
int new_size = (int)inflate_buffer.size() * 2;
|
|
||||||
if (new_size > maximum_tracker_response_length) new_size = maximum_tracker_response_length;
|
|
||||||
int old_size = (int)inflate_buffer.size();
|
|
||||||
|
|
||||||
inflate_buffer.resize(new_size);
|
|
||||||
str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[old_size]);
|
|
||||||
str.avail_out = new_size - old_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = inflate(&str, Z_SYNC_FLUSH);
|
|
||||||
}
|
|
||||||
|
|
||||||
inflate_buffer.resize(inflate_buffer.size() - str.avail_out);
|
|
||||||
inflateEnd(&str);
|
|
||||||
|
|
||||||
if (ret != Z_STREAM_END)
|
|
||||||
{
|
|
||||||
requester->tracker_request_error(req, 200, "gzip error");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// commit the resulting buffer
|
|
||||||
std::swap(buffer, inflate_buffer);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
timeout_handler::timeout_handler(io_service& ios)
|
timeout_handler::timeout_handler(io_service& ios)
|
||||||
: m_start_time(time_now())
|
: m_start_time(time_now())
|
||||||
, m_read_time(time_now())
|
, m_read_time(time_now())
|
||||||
|
|
|
@ -80,7 +80,7 @@ void start_web_server(int port, bool ssl)
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ofstream f("lighty_config");
|
std::ofstream f("lighty_config");
|
||||||
f << "server.modules = (\"mod_access\", \"mod_redirect\")\n"
|
f << "server.modules = (\"mod_access\", \"mod_redirect\", \"mod_setenv\")\n"
|
||||||
"server.document-root = \"" << boost::filesystem::initial_path().string() << "\"\n"
|
"server.document-root = \"" << boost::filesystem::initial_path().string() << "\"\n"
|
||||||
"server.range-requests = \"enable\"\n"
|
"server.range-requests = \"enable\"\n"
|
||||||
"server.port = " << port << "\n"
|
"server.port = " << port << "\n"
|
||||||
|
@ -88,7 +88,11 @@ void start_web_server(int port, bool ssl)
|
||||||
"url.redirect = (\"^/redirect$\" => \""
|
"url.redirect = (\"^/redirect$\" => \""
|
||||||
<< (ssl?"https":"http") << "://127.0.0.1:" << port << "/test_file\", "
|
<< (ssl?"https":"http") << "://127.0.0.1:" << port << "/test_file\", "
|
||||||
"\"^/infinite_redirect$\" => \""
|
"\"^/infinite_redirect$\" => \""
|
||||||
<< (ssl?"https":"http") << "://127.0.0.1:" << port << "/infinite_redirect\")\n";
|
<< (ssl?"https":"http") << "://127.0.0.1:" << port << "/infinite_redirect\")\n"
|
||||||
|
"$HTTP[\"url\"] == \"/test_file.gz\" {\n"
|
||||||
|
" setenv.add-response-header = ( \"Content-Encoding\" => \"gzip\" )\n"
|
||||||
|
" mimetype.assign = ()\n"
|
||||||
|
"}\n";
|
||||||
// this requires lighttpd to be built with ssl support.
|
// this requires lighttpd to be built with ssl support.
|
||||||
// The port distribution for mac is not built with ssl
|
// The port distribution for mac is not built with ssl
|
||||||
// support by default.
|
// support by default.
|
||||||
|
|
|
@ -106,6 +106,7 @@ void run_suite(std::string const& protocol, proxy_settings const& ps)
|
||||||
run_test(protocol + "://127.0.0.1:8001/redirect", 3216, 200, 2, asio::error_code(), ps);
|
run_test(protocol + "://127.0.0.1:8001/redirect", 3216, 200, 2, asio::error_code(), ps);
|
||||||
run_test(protocol + "://127.0.0.1:8001/infinite_redirect", 0, 301, 6, asio::error_code(), ps);
|
run_test(protocol + "://127.0.0.1:8001/infinite_redirect", 0, 301, 6, asio::error_code(), ps);
|
||||||
run_test(protocol + "://127.0.0.1:8001/test_file", 3216, 200, 1, asio::error_code(), ps);
|
run_test(protocol + "://127.0.0.1:8001/test_file", 3216, 200, 1, asio::error_code(), ps);
|
||||||
|
run_test(protocol + "://127.0.0.1:8001/test_file.gz", 3216, 200, 1, asio::error_code(), ps);
|
||||||
run_test(protocol + "://127.0.0.1:8001/non-existing-file", -1, 404, 1, err(), ps);
|
run_test(protocol + "://127.0.0.1:8001/non-existing-file", -1, 404, 1, err(), ps);
|
||||||
// if we're going through an http proxy, we won't get the same error as if the hostname
|
// if we're going through an http proxy, we won't get the same error as if the hostname
|
||||||
// resolution failed
|
// resolution failed
|
||||||
|
@ -123,7 +124,8 @@ int test_main()
|
||||||
std::srand(std::time(0));
|
std::srand(std::time(0));
|
||||||
std::generate(data_buffer, data_buffer + sizeof(data_buffer), &std::rand);
|
std::generate(data_buffer, data_buffer + sizeof(data_buffer), &std::rand);
|
||||||
std::ofstream("test_file").write(data_buffer, 3216);
|
std::ofstream("test_file").write(data_buffer, 3216);
|
||||||
|
std::system("gzip -9 -c test_file > test_file.gz");
|
||||||
|
|
||||||
proxy_settings ps;
|
proxy_settings ps;
|
||||||
ps.hostname = "127.0.0.1";
|
ps.hostname = "127.0.0.1";
|
||||||
ps.port = 8034;
|
ps.port = 8034;
|
||||||
|
|
Loading…
Reference in New Issue