Vitaliy Filippov 2015-05-16 00:36:18 +03:00
commit ac1763f2c7
6 changed files with 107 additions and 25 deletions

10
.gitignore vendored
View File

@ -4,3 +4,13 @@ grive.kdev4
.project .project
.cproject .cproject
build/ build/
/CMakeCache.txt
CMakeFiles
moc_*.cxx*
bgrive/ui_MainWindow.h
Makefile
*.a
bgrive/bgrive
grive/grive
libgrive/btest
*.cmake

View File

@ -36,6 +36,7 @@
#include "xml/Node.hh" #include "xml/Node.hh"
#include "xml/NodeSet.hh" #include "xml/NodeSet.hh"
#include "xml/String.hh" #include "xml/String.hh"
#include "xml/TreeBuilder.hh"
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <boost/exception/all.hpp> #include <boost/exception/all.hpp>
@ -252,7 +253,7 @@ void Resource::FromRemoteFile( const Entry& remote, const DateTime& last_sync )
void Resource::FromLocal( const DateTime& last_sync ) void Resource::FromLocal( const DateTime& last_sync )
{ {
fs::path path = Path() ; fs::path path = Path() ;
assert( fs::exists( path ) ) ; //assert( fs::exists( path ) ) ;
// root folder is always in sync // root folder is always in sync
if ( !IsRoot() ) if ( !IsRoot() )
@ -270,8 +271,8 @@ void Resource::FromLocal( const DateTime& last_sync )
m_state = ( m_mtime > last_sync ? local_new : remote_deleted ) ; m_state = ( m_mtime > last_sync ? local_new : remote_deleted ) ;
m_name = path.filename().string() ; m_name = path.filename().string() ;
m_kind = fs::is_directory(path) ? "folder" : "file" ; //m_kind = fs::is_directory(path) ? "folder" : "file" ;
m_md5 = fs::is_directory(path) ? "" : crypt::MD5::Get( path ) ; m_md5 = IsFolder() ? "" : crypt::MD5::Get( path ) ;
} }
assert( m_state != unknown ) ; assert( m_state != unknown ) ;
@ -579,23 +580,68 @@ bool Resource::Upload(
% xml::Escape(m_name) % xml::Escape(m_name)
).str() ; ).str() ;
http::StringResponse str ; bool retrying=false;
if ( post ) while ( true ) {
http->Post( link, meta, &str, hdr ) ; if ( retrying ) {
else file.Seek( 0, SEEK_SET );
http->Put( link, meta, &str, hdr ) ; os::Sleep( 5 );
}
http::Header uphdr ;
uphdr.Add( "Expect:" ) ;
uphdr.Add( "Accept:" ) ;
// the content upload URL is in the "Location" HTTP header try {
std::string uplink = http->RedirLocation() ; http::StringResponse str ;
http::XmlResponse xml ; if ( post )
http->Post( link, meta, &str, hdr ) ;
http->Put( uplink, &file, &xml, uphdr ) ; else
AssignIDs( Entry( xml.Response() ) ) ; http->Put( link, meta, &str, hdr ) ;
m_mtime = Entry(xml.Response()).MTime(); } catch ( Error &e ) {
std::string const *info = boost::get_error_info<xml::TreeBuilder::ExpatApiError>(e);
if ( info && (*info == "XML_Parse") ) {
Log( "Error parsing pre-upload response XML, retrying whole upload in 5s",
log::warning );
retrying = true;
continue;
} else {
throw e;
}
}
http::Header uphdr ;
uphdr.Add( "Expect:" ) ;
uphdr.Add( "Accept:" ) ;
// the content upload URL is in the "Location" HTTP header
std::string uplink = http->RedirLocation() ;
http::XmlResponse xml ;
long http_code = 0;
try {
http_code = http->Put( uplink, &file, &xml, uphdr ) ;
} catch ( Error &e ) {
std::string const *info = boost::get_error_info<xml::TreeBuilder::ExpatApiError>(e);
if ( info && (*info == "XML_Parse") ) {
Log( "Error parsing response XML, retrying whole upload in 5s",
log::warning );
retrying = true;
continue;
} else {
throw e;
}
}
if ( http_code == 410 || http_code == 412 ) {
Log( "request failed with %1%, retrying whole upload in 5s", http_code,
log::warning ) ;
retrying = true;
continue;
}
if ( retrying )
Log( "upload succeeded on retry", log::warning );
Entry responseEntry = Entry( xml.Response() );
AssignIDs( responseEntry ) ;
m_mtime = responseEntry.MTime();
break;
}
return true ; return true ;
} }

View File

@ -61,7 +61,7 @@ void State::FromLocal( const fs::path& p )
bool State::IsIgnore( const std::string& filename ) bool State::IsIgnore( const std::string& filename )
{ {
return filename[0] == '.' ; return filename == ".grive" || filename == ".grive_state";
} }
void State::FromLocal( const fs::path& p, Resource* folder ) void State::FromLocal( const fs::path& p, Resource* folder )
@ -89,19 +89,20 @@ void State::FromLocal( const fs::path& p, Resource* folder )
else else
{ {
bool is_dir = fs::is_directory(i->path());
// if the Resource object of the child already exists, it should // if the Resource object of the child already exists, it should
// have been so no need to do anything here // have been so no need to do anything here
Resource *c = folder->FindChild( fname ) ; Resource *c = folder->FindChild( fname ) ;
if ( c == 0 ) if ( c == 0 )
{ {
c = new Resource( fname, fs::is_directory(i->path()) ? "folder" : "file" ) ; c = new Resource( fname, is_dir ? "folder" : "file" ) ;
folder->AddChild( c ) ; folder->AddChild( c ) ;
m_res.Insert( c ) ; m_res.Insert( c ) ;
} }
c->FromLocal( m_last_sync ) ; c->FromLocal( m_last_sync ) ;
if ( fs::is_directory( i->path() ) ) if ( is_dir )
FromLocal( *i, c ) ; FromLocal( *i, c ) ;
} }
} }

View File

@ -28,6 +28,11 @@ XmlResponse::XmlResponse() : m_tb( new xml::TreeBuilder )
{ {
} }
void XmlResponse::Clear()
{
m_tb.reset(new xml::TreeBuilder);
}
std::size_t XmlResponse::Write( const char *data, std::size_t count ) std::size_t XmlResponse::Write( const char *data, std::size_t count )
{ {
m_tb->ParseData( data, count ) ; m_tb->ParseData( data, count ) ;

View File

@ -21,8 +21,10 @@
#include "http/Error.hh" #include "http/Error.hh"
#include "http/Header.hh" #include "http/Header.hh"
#include "http/XmlResponse.hh"
#include "util/log/Log.hh" #include "util/log/Log.hh"
#include "util/OS.hh" #include "util/OS.hh"
#include "util/File.hh"
#include <cassert> #include <cassert>
@ -69,8 +71,22 @@ long AuthAgent::Put(
Header auth = AppendHeader(hdr) ; Header auth = AppendHeader(hdr) ;
long response ; long response ;
while ( CheckRetry( bool keepTrying = true;
response = m_agent->Put( url, file, dest, AppendHeader(hdr) ) ) ) ; while ( keepTrying ) {
response = m_agent->Put( url, file, dest, auth );
keepTrying = CheckRetry( response );
if ( keepTrying ) {
file->Seek( 0, SEEK_SET );
XmlResponse *xmlResponse = dynamic_cast<XmlResponse*>(dest);
if( xmlResponse )
xmlResponse->Clear();
}
}
// On 410 Gone or 412 Precondition failed, recovery may be possible so don't
// throw an exception
if ( response == 410 || response == 412 )
return response;
return CheckHttpResponse(response, url, auth) ; return CheckHttpResponse(response, url, auth) ;
} }
@ -152,6 +168,7 @@ bool AuthAgent::CheckRetry( long response )
Log( "resquest failed due to auth token expired: %1%. refreshing token", Log( "resquest failed due to auth token expired: %1%. refreshing token",
response, log::warning ) ; response, log::warning ) ;
os::Sleep( 5 ) ;
m_auth.Refresh() ; m_auth.Refresh() ;
return true ; return true ;
} }

View File

@ -21,6 +21,7 @@
#include "Error.hh" #include "Error.hh"
#include "Node.hh" #include "Node.hh"
#include "util/log/Log.hh"
#include <expat.h> #include <expat.h>
@ -72,8 +73,10 @@ void TreeBuilder::ParseData( const char *data, std::size_t count, bool last )
{ {
is_new = false ; is_new = false ;
if ( ::XML_Parse( m_impl->psr, data, count, last ) == 0 ) if ( ::XML_Parse( m_impl->psr, data, count, last ) == 0 ) {
Log("Error parsing XML: %1%", data, log::error);
BOOST_THROW_EXCEPTION( Error() << ExpatApiError("XML_Parse") ); BOOST_THROW_EXCEPTION( Error() << ExpatApiError("XML_Parse") );
}
} }
Node TreeBuilder::Parse( const std::string& xml ) Node TreeBuilder::Parse( const std::string& xml )