diff --git a/AddressBook.cpp b/AddressBook.cpp index 6570e172..ca96882e 100644 --- a/AddressBook.cpp +++ b/AddressBook.cpp @@ -507,6 +507,7 @@ namespace client << "Host: " << u.host_ << "\r\n" << "Accept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" + //<< "Accept-Encoding: gzip\r\n" << "Connection: close\r\n"; if (m_Etag.length () > 0) // etag request << i2p::util::http::IF_NONE_MATCH << ": \"" << m_Etag << "\"\r\n"; @@ -545,7 +546,7 @@ namespace client response >> status; // status if (status == 200) // OK { - bool isChunked = false; + bool isChunked = false, isGzip = false; std::string header, statusMessage; std::getline (response, statusMessage); // read until new line meaning end of header @@ -563,6 +564,8 @@ namespace client m_LastModified = header.substr (colon + 1); else if (field == i2p::util::http::TRANSFER_ENCODING) isChunked = !header.compare (colon + 1, std::string::npos, "chunked"); + else if (field == i2p::util::http::CONTENT_ENCODING) + isGzip = !header.compare (colon + 1, std::string::npos, "gzip"); } } LogPrint (eLogInfo, "Addressbook: ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified); @@ -570,13 +573,13 @@ namespace client { success = true; if (!isChunked) - m_Book.LoadHostsFromStream (response); + success = ProcessResponse (response, isGzip); else { // merge chunks std::stringstream merged; i2p::util::http::MergeChunkedResponse (response, merged); - m_Book.LoadHostsFromStream (merged); + success = ProcessResponse (merged, isGzip); } } } @@ -599,6 +602,23 @@ namespace client m_Book.DownloadComplete (success); } + + bool AddressBookSubscription::ProcessResponse (std::stringstream& s, bool isGzip) + { + if (isGzip) + { + std::stringstream uncompressed; + i2p::data::GzipInflator inflator; + inflator.Inflate (s, uncompressed); + if (!uncompressed.fail ()) + m_Book.LoadHostsFromStream (uncompressed); + else + return false; + } + else + m_Book.LoadHostsFromStream (s); + return true; + } } } diff --git a/AddressBook.h b/AddressBook.h index f5f5270e..19d8744c 100644 --- a/AddressBook.h +++ b/AddressBook.h @@ -92,6 +92,7 @@ namespace client private: void Request (); + bool ProcessResponse (std::stringstream& s, bool isGzip = false); private: diff --git a/Base.cpp b/Base.cpp index 1479d62f..813b8dfa 100644 --- a/Base.cpp +++ b/Base.cpp @@ -302,6 +302,38 @@ namespace data } } + bool GzipInflator::Inflate (const uint8_t * in, size_t inLen, std::ostream& s) + { + m_IsDirty = true; + uint8_t * out = new uint8_t[GZIP_CHUNK_SIZE]; + m_Inflator.next_in = const_cast(in); + m_Inflator.avail_in = inLen; + int ret; + do + { + m_Inflator.next_out = out; + m_Inflator.avail_out = GZIP_CHUNK_SIZE; + ret = inflate (&m_Inflator, Z_NO_FLUSH); + if (ret < 0) + { + LogPrint (eLogError, "Decompression error ", ret); + inflateEnd (&m_Inflator); + s.setstate(std::ios_base::failbit); + break; + } + else + s.write ((char *)out, GZIP_CHUNK_SIZE - m_Inflator.avail_out); + } + while (!m_Inflator.avail_out); // more data to read + delete[] out; + return ret == Z_STREAM_END || ret < 0; + } + + void GzipInflator::Inflate (const std::stringstream& in, std::ostream& out) + { + Inflate ((const uint8_t *)in.str ().c_str (), in.str ().length (), out); + } + GzipDeflator::GzipDeflator (): m_IsDirty (false) { memset (&m_Deflator, 0, sizeof (m_Deflator)); diff --git a/Base.h b/Base.h index d598542b..b5ac04c3 100644 --- a/Base.h +++ b/Base.h @@ -5,6 +5,8 @@ #include #include #include +#include +#include namespace i2p { @@ -92,6 +94,7 @@ namespace data }; }; + const size_t GZIP_CHUNK_SIZE = 16384; class GzipInflator { public: @@ -100,7 +103,10 @@ namespace data ~GzipInflator (); size_t Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen); - + bool Inflate (const uint8_t * in, size_t inLen, std::ostream& s); + // return true when finshed or error, s failbit will be set in case of error + void Inflate (const std::stringstream& in, std::ostream& out); + private: z_stream m_Inflator; diff --git a/util.h b/util.h index 0377ef8d..15765f37 100644 --- a/util.h +++ b/util.h @@ -34,6 +34,7 @@ namespace util const char IF_MODIFIED_SINCE[] = "If-Modified-Since"; const char LAST_MODIFIED[] = "Last-Modified"; const char TRANSFER_ENCODING[] = "Transfer-Encoding"; + const char CONTENT_ENCODING[] = "Content-Encoding"; std::string GetHttpContent (std::istream& response); void MergeChunkedResponse (std::istream& response, std::ostream& merged);