@ -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 ;
}
}