|
|
@ -705,6 +705,7 @@ namespace client |
|
|
|
i2p::http::HTTPReq req; |
|
|
|
i2p::http::HTTPReq req; |
|
|
|
req.AddHeader("Host", dest_host); |
|
|
|
req.AddHeader("Host", dest_host); |
|
|
|
req.AddHeader("User-Agent", "Wget/1.11.4"); |
|
|
|
req.AddHeader("User-Agent", "Wget/1.11.4"); |
|
|
|
|
|
|
|
req.AddHeader("X-Accept-Encoding", "x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n"); |
|
|
|
req.AddHeader("Connection", "close"); |
|
|
|
req.AddHeader("Connection", "close"); |
|
|
|
if (!m_Etag.empty()) |
|
|
|
if (!m_Etag.empty()) |
|
|
|
req.AddHeader("If-None-Match", m_Etag); |
|
|
|
req.AddHeader("If-None-Match", m_Etag); |
|
|
@ -721,7 +722,9 @@ namespace client |
|
|
|
std::string response; |
|
|
|
std::string response; |
|
|
|
uint8_t recv_buf[4096]; |
|
|
|
uint8_t recv_buf[4096]; |
|
|
|
bool end = false; |
|
|
|
bool end = false; |
|
|
|
while (!end) { |
|
|
|
int numAttempts = 5; |
|
|
|
|
|
|
|
while (!end) |
|
|
|
|
|
|
|
{ |
|
|
|
stream->AsyncReceive (boost::asio::buffer (recv_buf, 4096), |
|
|
|
stream->AsyncReceive (boost::asio::buffer (recv_buf, 4096), |
|
|
|
[&](const boost::system::error_code& ecode, std::size_t bytes_transferred) |
|
|
|
[&](const boost::system::error_code& ecode, std::size_t bytes_transferred) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -734,60 +737,69 @@ namespace client |
|
|
|
30); // wait for 30 seconds
|
|
|
|
30); // wait for 30 seconds
|
|
|
|
std::unique_lock<std::mutex> l(newDataReceivedMutex); |
|
|
|
std::unique_lock<std::mutex> l(newDataReceivedMutex); |
|
|
|
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout) |
|
|
|
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout) |
|
|
|
|
|
|
|
{ |
|
|
|
LogPrint (eLogError, "Addressbook: subscriptions request timeout expired"); |
|
|
|
LogPrint (eLogError, "Addressbook: subscriptions request timeout expired"); |
|
|
|
|
|
|
|
numAttempts++; |
|
|
|
|
|
|
|
if (numAttempts > 5) end = true; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
// process remaining buffer
|
|
|
|
// process remaining buffer
|
|
|
|
while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf))) { |
|
|
|
while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf))) |
|
|
|
response.append ((char *)recv_buf, len); |
|
|
|
response.append ((char *)recv_buf, len); |
|
|
|
} |
|
|
|
|
|
|
|
/* parse response */ |
|
|
|
/* parse response */ |
|
|
|
i2p::http::HTTPRes res; |
|
|
|
i2p::http::HTTPRes res; |
|
|
|
int res_head_len = res.parse(response); |
|
|
|
int res_head_len = res.parse(response); |
|
|
|
if (res_head_len < 0) { |
|
|
|
if (res_head_len < 0) |
|
|
|
|
|
|
|
{ |
|
|
|
LogPrint(eLogError, "Addressbook: can't parse http response from ", dest_host); |
|
|
|
LogPrint(eLogError, "Addressbook: can't parse http response from ", dest_host); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
if (res_head_len == 0) { |
|
|
|
if (res_head_len == 0) |
|
|
|
|
|
|
|
{ |
|
|
|
LogPrint(eLogError, "Addressbook: incomplete http response from ", dest_host, ", interrupted by timeout"); |
|
|
|
LogPrint(eLogError, "Addressbook: incomplete http response from ", dest_host, ", interrupted by timeout"); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
/* assert: res_head_len > 0 */ |
|
|
|
/* assert: res_head_len > 0 */ |
|
|
|
response.erase(0, res_head_len); |
|
|
|
response.erase(0, res_head_len); |
|
|
|
if (res.code == 304) { |
|
|
|
if (res.code == 304) |
|
|
|
|
|
|
|
{ |
|
|
|
LogPrint (eLogInfo, "Addressbook: no updates from ", dest_host, ", code 304"); |
|
|
|
LogPrint (eLogInfo, "Addressbook: no updates from ", dest_host, ", code 304"); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
if (res.code != 200) { |
|
|
|
if (res.code != 200) |
|
|
|
|
|
|
|
{ |
|
|
|
LogPrint (eLogWarning, "Adressbook: can't get updates from ", dest_host, ", response code ", res.code); |
|
|
|
LogPrint (eLogWarning, "Adressbook: can't get updates from ", dest_host, ", response code ", res.code); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
int len = res.content_length(); |
|
|
|
int len = res.content_length(); |
|
|
|
if (response.empty()) { |
|
|
|
if (response.empty()) |
|
|
|
|
|
|
|
{ |
|
|
|
LogPrint(eLogError, "Addressbook: empty response from ", dest_host, ", expected ", len, " bytes"); |
|
|
|
LogPrint(eLogError, "Addressbook: empty response from ", dest_host, ", expected ", len, " bytes"); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
if (len > 0 && len != (int) response.length()) { |
|
|
|
if (!res.is_gzipped () && len > 0 && len != (int) response.length()) |
|
|
|
LogPrint(eLogError, "Addressbook: response size mismatch, expected: ", response.length(), ", got: ", len, "bytes"); |
|
|
|
{ |
|
|
|
|
|
|
|
LogPrint(eLogError, "Addressbook: response size mismatch, expected: ", len, ", got: ", response.length(), "bytes"); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
/* assert: res.code == 200 */ |
|
|
|
/* assert: res.code == 200 */ |
|
|
|
auto it = res.headers.find("ETag"); |
|
|
|
auto it = res.headers.find("ETag"); |
|
|
|
if (it != res.headers.end()) { |
|
|
|
if (it != res.headers.end()) m_Etag = it->second; |
|
|
|
m_Etag = it->second; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
it = res.headers.find("If-Modified-Since"); |
|
|
|
it = res.headers.find("If-Modified-Since"); |
|
|
|
if (it != res.headers.end()) { |
|
|
|
if (it != res.headers.end()) m_LastModified = it->second; |
|
|
|
m_LastModified = it->second; |
|
|
|
if (res.is_chunked()) |
|
|
|
} |
|
|
|
{ |
|
|
|
if (res.is_chunked()) { |
|
|
|
|
|
|
|
std::stringstream in(response), out; |
|
|
|
std::stringstream in(response), out; |
|
|
|
i2p::http::MergeChunkedResponse (in, out); |
|
|
|
i2p::http::MergeChunkedResponse (in, out); |
|
|
|
response = out.str(); |
|
|
|
response = out.str(); |
|
|
|
} else if (res.is_gzipped()) { |
|
|
|
} |
|
|
|
|
|
|
|
else if (res.is_gzipped()) |
|
|
|
|
|
|
|
{ |
|
|
|
std::stringstream out; |
|
|
|
std::stringstream out; |
|
|
|
i2p::data::GzipInflator inflator; |
|
|
|
i2p::data::GzipInflator inflator; |
|
|
|
inflator.Inflate ((const uint8_t *) response.data(), response.length(), out); |
|
|
|
inflator.Inflate ((const uint8_t *) response.data(), response.length(), out); |
|
|
|
if (out.fail()) { |
|
|
|
if (out.fail()) |
|
|
|
|
|
|
|
{ |
|
|
|
LogPrint(eLogError, "Addressbook: can't gunzip http response"); |
|
|
|
LogPrint(eLogError, "Addressbook: can't gunzip http response"); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|