Fix hang when upload receives HTTP 500

When an uploading PUT request got a HTTP 500 as reponse, grive hanged
forever inside libcurl. This was because the File parameter was not
rewound to 0 position on retry. The XmlResponse had to be cleared as
well.

Rewinding the File and clearing the XmlResponse were not enough to fix
the problem, because when retrying after 500, HTTP 410 Gone or 412
Precondition failed is often received, and CheckHttpResponse would throw
an exception that crashes grive. Therefore, I implemented a retry logic
to Resource::Upload that retries the whole upload transaction if 410 or
412 was received.
pull/40/head
Visa Putkinen 2013-11-25 00:07:27 +02:00
parent 645bb2e7d4
commit 84785ec473
3 changed files with 61 additions and 18 deletions

View File

@ -36,6 +36,7 @@
#include "xml/Node.hh"
#include "xml/NodeSet.hh"
#include "xml/String.hh"
#include "xml/TreeBuilder.hh"
#include <boost/bind.hpp>
#include <boost/exception/all.hpp>
@ -599,23 +600,44 @@ bool Resource::Upload(
% xml::Escape(m_name)
).str() ;
http::StringResponse str ;
if ( post )
http->Post( link, meta, &str, hdr ) ;
else
http->Put( link, meta, &str, hdr ) ;
http::Header uphdr ;
uphdr.Add( "Expect:" ) ;
uphdr.Add( "Accept:" ) ;
bool retrying=false;
while ( true ) {
if ( retrying ) {
file.Seek( 0, SEEK_SET );
os::Sleep( 5 );
}
// the content upload URL is in the "Location" HTTP header
std::string uplink = http->RedirLocation() ;
http::XmlResponse xml ;
http->Put( uplink, &file, &xml, uphdr ) ;
AssignIDs( Entry( xml.Response() ) ) ;
m_mtime = Entry(xml.Response()).MTime();
http::StringResponse str ;
if ( post )
http->Post( link, meta, &str, hdr ) ;
else
http->Put( link, meta, &str, hdr ) ;
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;
http_code = http->Put( uplink, &file, &xml, uphdr ) ;
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 ;
}

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 )
{
m_tb->ParseData( data, count ) ;

View File

@ -21,8 +21,10 @@
#include "http/Error.hh"
#include "http/Header.hh"
#include "http/XmlResponse.hh"
#include "util/log/Log.hh"
#include "util/OS.hh"
#include "util/File.hh"
#include <cassert>
@ -69,8 +71,22 @@ long AuthAgent::Put(
Header auth = AppendHeader(hdr) ;
long response ;
while ( CheckRetry(
response = m_agent->Put( url, file, dest, AppendHeader(hdr) ) ) ) ;
bool keepTrying = true;
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) ;
}