Implement normal reliable HTTP logging (in Agent)

Flushes on every call so may be slightly slow, but in return always provides full
information in case of a segfault or a failed assertion.
pull/40/head
Vitaliy Filippov 2015-09-30 15:01:50 +03:00
parent 9b8e0c826b
commit 9402bff12e
12 changed files with 99 additions and 77 deletions

View File

@ -112,7 +112,7 @@ int Main( int argc, char **argv )
( "path,p", po::value<std::string>(), "Path to sync")
( "dir,s", po::value<std::string>(), "Subdirectory to sync")
( "verbose,V", "Verbose mode. Enable more messages than normal.")
( "log-xml", "Log more HTTP responses as XML for debugging.")
( "log-http", po::value<std::string>(), "Log all HTTP responses in this file for debugging.")
( "new-rev", "Create new revisions in server for updated files.")
( "debug,d", "Enable debug level messages. Implies -v.")
( "log,l", po::value<std::string>(), "Set log output filename." )
@ -147,12 +147,18 @@ int Main( int argc, char **argv )
Log( "config file name %1%", config.Filename(), log::verbose );
std::auto_ptr<http::Agent> http( new http::CurlAgent );
if ( vm.count( "log-http" ) )
http->SetLog( new http::ResponseLog( vm["log-http"].as<std::string>(), ".txt" ) );
if ( vm.count( "auth" ) )
{
OAuth2 token( http, client_id, client_secret ) ;
std::cout
<< "-----------------------\n"
<< "Please go to this URL and get an authentication code:\n\n"
<< OAuth2::MakeAuthURL( client_id )
<< token.MakeAuthURL()
<< std::endl ;
std::cout
@ -161,7 +167,6 @@ int Main( int argc, char **argv )
std::string code ;
std::cin >> code ;
OAuth2 token( client_id, client_secret ) ;
token.Auth( code ) ;
// save to config
@ -184,8 +189,8 @@ int Main( int argc, char **argv )
return -1;
}
OAuth2 token( refresh_token, client_id, client_secret ) ;
AuthAgent agent( token, std::auto_ptr<http::Agent>( new http::CurlAgent ) ) ;
OAuth2 token( http, refresh_token, client_id, client_secret ) ;
AuthAgent agent( token, http ) ;
v2::Syncer2 syncer( &agent );
Drive drive( &syncer, config.GetAll() ) ;

View File

@ -20,6 +20,7 @@
#pragma once
#include <string>
#include "ResponseLog.hh"
namespace gr {
@ -35,6 +36,9 @@ class Agent
public :
virtual ~Agent() {}
virtual ResponseLog* GetLog() const = 0 ;
virtual void SetLog( ResponseLog* ) = 0 ;
virtual long Put(
const std::string& url,
const std::string& data,

View File

@ -118,6 +118,16 @@ CurlAgent::~CurlAgent()
::curl_easy_cleanup( m_pimpl->curl );
}
ResponseLog* CurlAgent::GetLog() const
{
return m_log.get();
}
void CurlAgent::SetLog(ResponseLog *log)
{
m_log.reset( log );
}
std::size_t CurlAgent::HeaderCallback( void *ptr, size_t size, size_t nmemb, CurlAgent *pthis )
{
char *str = static_cast<char*>(ptr) ;
@ -130,6 +140,9 @@ std::size_t CurlAgent::HeaderCallback( void *ptr, size_t size, size_t nmemb, Cur
if ( pthis->m_pimpl->error )
pthis->m_pimpl->error_headers += line;
if ( pthis->m_log.get() )
pthis->m_log->Write( str, size*nmemb );
static const std::string loc = "Location: " ;
std::size_t pos = line.find( loc ) ;
if ( pos != line.npos )
@ -144,6 +157,8 @@ std::size_t CurlAgent::HeaderCallback( void *ptr, size_t size, size_t nmemb, Cur
std::size_t CurlAgent::Receive( void* ptr, size_t size, size_t nmemb, CurlAgent *pthis )
{
assert( pthis != 0 ) ;
if ( pthis->m_log.get() )
pthis->m_log->Write( (const char*)ptr, size*nmemb );
if ( pthis->m_pimpl->error && pthis->m_pimpl->error_data.size() < 65536 )
{
// Do not feed error responses to destination stream
@ -284,6 +299,7 @@ long CurlAgent::Custom(
{
Trace("HTTP %2% \"%1%\"", url, method ) ;
Init() ;
CURL *curl = m_pimpl->curl ;
::curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method.c_str() );

View File

@ -40,7 +40,10 @@ class CurlAgent : public Agent
public :
CurlAgent() ;
~CurlAgent() ;
ResponseLog* GetLog() const ;
void SetLog( ResponseLog *log ) ;
long Put(
const std::string& url,
const std::string& post_data,
@ -92,6 +95,7 @@ private :
private :
struct Impl ;
std::auto_ptr<Impl> m_pimpl ;
std::auto_ptr<ResponseLog> m_log ;
} ;
} } // end of namespace

View File

@ -28,19 +28,9 @@ namespace gr { namespace http {
ResponseLog::ResponseLog(
const std::string& prefix,
const std::string& suffix,
DataStream *next ) :
m_enabled ( true ),
m_next ( next )
const std::string& suffix )
{
Reset( prefix, suffix, next ) ;
}
ResponseLog::ResponseLog( DataStream *next ) :
m_enabled ( false ),
m_next ( next )
{
assert( m_next != 0 ) ;
Reset( prefix, suffix ) ;
}
std::size_t ResponseLog::Write( const char *data, std::size_t count )
@ -49,15 +39,14 @@ std::size_t ResponseLog::Write( const char *data, std::size_t count )
{
assert( m_log.rdbuf() != 0 ) ;
m_log.rdbuf()->sputn( data, count ) ;
m_log.flush();
}
return m_next->Write( data, count ) ;
return count;
}
std::size_t ResponseLog::Read( char *data, std::size_t count )
{
assert( m_next != 0 ) ;
return m_next->Read( data, count ) ;
return 0 ;
}
std::string ResponseLog::Filename( const std::string& prefix, const std::string& suffix )
@ -65,19 +54,12 @@ std::string ResponseLog::Filename( const std::string& prefix, const std::string&
return prefix + DateTime::Now().Format( "%F.%H%M%S" ) + suffix ;
}
void ResponseLog::Enable( bool enable )
void ResponseLog::Reset( const std::string& prefix, const std::string& suffix )
{
m_enabled = enable ;
}
void ResponseLog::Reset( const std::string& prefix, const std::string& suffix, DataStream *next )
{
assert( next != 0 ) ;
if ( m_log.is_open() )
m_log.close() ;
const std::string fname = Filename(prefix, suffix) ;
const std::string fname = Filename( prefix, suffix ) ;
// reset previous stream state. don't care if file can be opened
// successfully previously
@ -88,12 +70,13 @@ void ResponseLog::Reset( const std::string& prefix, const std::string& suffix, D
if ( m_log )
{
Trace( "logging HTTP response: %1%", fname ) ;
m_enabled = true ;
m_enabled = true ;
}
else
{
Trace( "cannot open log file %1%", fname ) ;
m_next = next ;
m_enabled = false ;
}
}
}} // end of namespace

View File

@ -31,23 +31,19 @@ class ResponseLog : public DataStream
public :
ResponseLog(
const std::string& prefix,
const std::string& suffix,
DataStream *next ) ;
ResponseLog( DataStream *next ) ;
const std::string& suffix ) ;
std::size_t Write( const char *data, std::size_t count ) ;
std::size_t Read( char *data, std::size_t count ) ;
void Enable( bool enable = true ) ;
void Reset( const std::string& prefix, const std::string& suffix, DataStream *next ) ;
void Reset( const std::string& prefix, const std::string& suffix ) ;
private :
static std::string Filename( const std::string& prefix, const std::string& suffix ) ;
private :
bool m_enabled ;
bool m_enabled ;
std::ofstream m_log ;
DataStream *m_next ;
} ;
} } // end of namespace

View File

@ -20,8 +20,6 @@
#include "ValBuilder.hh"
#include <cassert>
namespace gr {
ValBuilder::ValBuilder( )
@ -106,7 +104,8 @@ void ValBuilder::End( Val::TypeEnum type )
{
if ( m_ctx.top().val.Type() == type )
{
assert( m_ctx.top().key.Is<void>() ) ;
if( !m_ctx.top().key.Is<void>() )
BOOST_THROW_EXCEPTION( Error() << Unexpected_(m_ctx.top().key) ) ;
// get top Val from stack
Val current ;
@ -130,8 +129,12 @@ void ValBuilder::EndObject()
Val ValBuilder::Result() const
{
assert( m_ctx.size() == 1U ) ;
return m_ctx.top().val ;
if ( !m_ctx.size() )
BOOST_THROW_EXCEPTION( Error() << NoKey_( Val(std::string("")) ) ) ;
Val r = m_ctx.top().val;
if ( m_ctx.size() > 0 )
BOOST_THROW_EXCEPTION( Error() << Unexpected_(m_ctx.top().val) ) ;
return r;
}
} // end of namespace

View File

@ -31,13 +31,23 @@ namespace gr {
using namespace http ;
AuthAgent::AuthAgent( const OAuth2& auth, std::auto_ptr<Agent> real_agent ) :
AuthAgent::AuthAgent( OAuth2& auth, std::auto_ptr<Agent> real_agent ) :
m_auth ( auth ),
m_agent ( real_agent )
{
assert( m_agent.get() != 0 ) ;
}
http::ResponseLog* AuthAgent::GetLog() const
{
return m_agent->GetLog();
}
void AuthAgent::SetLog( http::ResponseLog *log )
{
return m_agent->SetLog( log );
}
Header AuthAgent::AppendHeader( const Header& hdr ) const
{
Header h(hdr) ;

View File

@ -34,7 +34,10 @@ namespace gr {
class AuthAgent : public http::Agent
{
public :
AuthAgent( const OAuth2& auth, std::auto_ptr<http::Agent> real_agent ) ;
AuthAgent( OAuth2& auth, std::auto_ptr<http::Agent> real_agent ) ;
http::ResponseLog* GetLog() const ;
void SetLog( http::ResponseLog *log ) ;
long Put(
const std::string& url,
@ -82,7 +85,7 @@ private :
const http::Header& hdr ) ;
private :
OAuth2 m_auth ;
OAuth2& m_auth ;
const std::auto_ptr<http::Agent> m_agent ;
} ;

View File

@ -33,9 +33,11 @@ namespace gr {
const std::string token_url = "https://accounts.google.com/o/oauth2/token" ;
OAuth2::OAuth2(
std::auto_ptr<http::Agent>& agent,
const std::string& refresh_code,
const std::string& client_id,
const std::string& client_secret ) :
m_agent( agent ),
m_refresh( refresh_code ),
m_client_id( client_id ),
m_client_secret( client_secret )
@ -44,8 +46,10 @@ OAuth2::OAuth2(
}
OAuth2::OAuth2(
std::auto_ptr<http::Agent>& agent,
const std::string& client_id,
const std::string& client_secret ) :
m_agent( agent ),
m_client_id( client_id ),
m_client_secret( client_secret )
{
@ -61,32 +65,26 @@ void OAuth2::Auth( const std::string& auth_code )
"&grant_type=authorization_code" ;
http::ValResponse resp ;
http::CurlAgent http ;
DisableLog dlog( log::debug ) ;
http.Post( token_url, post, &resp, http::Header() ) ;
m_agent->Post( token_url, post, &resp, http::Header() ) ;
Val jresp = resp.Response() ;
m_access = jresp["access_token"].Str() ;
m_refresh = jresp["refresh_token"].Str() ;
}
std::string OAuth2::MakeAuthURL(
const std::string& client_id,
const std::string& state )
std::string OAuth2::MakeAuthURL()
{
http::CurlAgent h ;
return "https://accounts.google.com/o/oauth2/auth"
"?scope=" +
h.Escape( "https://www.googleapis.com/auth/userinfo.email" ) + "+" +
h.Escape( "https://www.googleapis.com/auth/userinfo.profile" ) + "+" +
h.Escape( "https://docs.google.com/feeds/" ) + "+" +
h.Escape( "https://docs.googleusercontent.com/" ) + "+" +
h.Escape( "https://spreadsheets.google.com/feeds/" ) +
m_agent->Escape( "https://www.googleapis.com/auth/userinfo.email" ) + "+" +
m_agent->Escape( "https://www.googleapis.com/auth/userinfo.profile" ) + "+" +
m_agent->Escape( "https://docs.google.com/feeds/" ) + "+" +
m_agent->Escape( "https://docs.googleusercontent.com/" ) + "+" +
m_agent->Escape( "https://spreadsheets.google.com/feeds/" ) +
"&redirect_uri=urn:ietf:wg:oauth:2.0:oob"
"&response_type=code"
"&client_id=" + client_id ;
"&client_id=" + m_client_id ;
}
void OAuth2::Refresh( )
@ -98,10 +96,8 @@ void OAuth2::Refresh( )
"&grant_type=refresh_token" ;
http::ValResponse resp ;
http::CurlAgent http ;
DisableLog dlog( log::debug ) ;
http.Post( token_url, post, &resp, http::Header() ) ;
m_agent->Post( token_url, post, &resp, http::Header() ) ;
m_access = resp.Response()["access_token"].Str() ;
}

View File

@ -19,7 +19,9 @@
#pragma once
#include "http/Agent.hh"
#include <string>
#include <memory>
namespace gr {
@ -27,34 +29,35 @@ class OAuth2
{
public :
OAuth2(
std::auto_ptr<http::Agent>& agent,
const std::string& client_id,
const std::string& client_secret ) ;
OAuth2(
std::auto_ptr<http::Agent>& agent,
const std::string& refresh_code,
const std::string& client_id,
const std::string& client_secret ) ;
std::string Str() const ;
static std::string MakeAuthURL(
const std::string& client_id,
const std::string& state = std::string() ) ;
std::string MakeAuthURL() ;
void Auth( const std::string& auth_code ) ;
void Refresh( ) ;
std::string RefreshToken( ) const ;
std::string AccessToken( ) const ;
// adding HTTP auth header
std::string HttpHeader( ) const ;
private :
std::string m_access ;
std::string m_refresh ;
std::auto_ptr<http::Agent> m_agent ;
const std::string m_client_id ;
const std::string m_client_secret ;
} ;
} // end of namespace

View File

@ -38,7 +38,6 @@ const std::string default_root_folder = ".";
Config::Config( const po::variables_map& vm )
{
m_cmd.Add( "log-xml", Val(vm.count("log-xml") > 0) ) ;
m_cmd.Add( "new-rev", Val(vm.count("new-rev") > 0) ) ;
m_cmd.Add( "force", Val(vm.count("force") > 0 ) ) ;
m_cmd.Add( "path", Val(vm.count("path") > 0