grive2/libgrive/src/http/CurlAgent.cc

275 lines
6.7 KiB
C++
Raw Normal View History

2012-04-25 20:13:17 +04:00
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
2012-04-25 20:13:17 +04:00
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
2012-06-10 19:11:32 +04:00
#include "CurlAgent.hh"
2012-04-25 20:13:17 +04:00
2012-05-13 11:27:58 +04:00
#include "Error.hh"
2012-06-10 19:11:32 +04:00
#include "Header.hh"
2012-05-05 21:12:44 +04:00
2012-06-03 14:31:02 +04:00
#include "util/log/Log.hh"
#include "util/DataStream.hh"
2013-04-28 14:40:21 +04:00
#include "util/File.hh"
2012-05-13 12:10:18 +04:00
#include <boost/throw_exception.hpp>
// dependent libraries
2012-04-25 20:13:17 +04:00
#include <curl/curl.h>
#include <algorithm>
2012-04-25 20:13:17 +04:00
#include <cassert>
#include <cstring>
2012-07-16 21:58:16 +04:00
#include <limits>
2012-04-25 20:13:17 +04:00
#include <streambuf>
#include <iostream>
2012-04-25 20:13:17 +04:00
#include <signal.h>
namespace {
2012-04-25 20:13:17 +04:00
using namespace gr::http ;
2012-07-16 21:58:16 +04:00
using namespace gr ;
2012-04-25 20:13:17 +04:00
std::size_t ReadFileCallback( void *ptr, std::size_t size, std::size_t nmemb, SeekStream *file )
2012-07-16 21:58:16 +04:00
{
assert( ptr != 0 ) ;
assert( file != 0 ) ;
if ( size*nmemb > 0 )
return file->Read( static_cast<char*>(ptr), size*nmemb ) ;
return 0 ;
2012-07-16 21:58:16 +04:00
}
} // end of local namespace
namespace gr { namespace http {
2012-06-10 19:11:32 +04:00
struct CurlAgent::Impl
2012-05-01 20:26:48 +04:00
{
CURL *curl ;
std::string location ;
bool error ;
std::string error_headers ;
std::string error_data ;
DataStream *dest ;
2012-05-01 20:26:48 +04:00
} ;
2015-07-04 00:18:59 +03:00
static struct curl_slist* SetHeader( CURL* handle, const Header& hdr );
2012-06-10 19:11:32 +04:00
CurlAgent::CurlAgent() :
2012-05-01 20:26:48 +04:00
m_pimpl( new Impl )
{
m_pimpl->curl = ::curl_easy_init();
2012-05-28 20:31:29 +04:00
}
2012-06-10 19:11:32 +04:00
void CurlAgent::Init()
2012-05-28 20:31:29 +04:00
{
::curl_easy_reset( m_pimpl->curl ) ;
::curl_easy_setopt( m_pimpl->curl, CURLOPT_SSL_VERIFYPEER, 0L ) ;
::curl_easy_setopt( m_pimpl->curl, CURLOPT_SSL_VERIFYHOST, 0L ) ;
2012-06-10 19:11:32 +04:00
::curl_easy_setopt( m_pimpl->curl, CURLOPT_HEADERFUNCTION, &CurlAgent::HeaderCallback ) ;
::curl_easy_setopt( m_pimpl->curl, CURLOPT_HEADERDATA, this ) ;
::curl_easy_setopt( m_pimpl->curl, CURLOPT_HEADER, 0L ) ;
if ( mMaxUpload > 0 )
::curl_easy_setopt( m_pimpl->curl, CURLOPT_MAX_SEND_SPEED_LARGE, mMaxUpload ) ;
if ( mMaxDownload > 0 )
::curl_easy_setopt( m_pimpl->curl, CURLOPT_MAX_RECV_SPEED_LARGE, mMaxDownload ) ;
m_pimpl->error = false;
m_pimpl->error_headers = "";
m_pimpl->error_data = "";
m_pimpl->dest = NULL;
2012-05-01 20:26:48 +04:00
}
2012-06-10 19:11:32 +04:00
CurlAgent::~CurlAgent()
2012-05-01 20:26:48 +04:00
{
::curl_easy_cleanup( m_pimpl->curl );
}
ResponseLog* CurlAgent::GetLog() const
{
return m_log.get();
}
void CurlAgent::SetLog(ResponseLog *log)
{
m_log.reset( log );
}
2012-06-10 19:11:32 +04:00
std::size_t CurlAgent::HeaderCallback( void *ptr, size_t size, size_t nmemb, CurlAgent *pthis )
2012-05-01 20:26:48 +04:00
{
char *str = static_cast<char*>(ptr) ;
2012-05-05 18:39:18 +04:00
std::string line( str, str + size*nmemb ) ;
2012-05-01 20:26:48 +04:00
// Check for error (HTTP 400 and above)
if ( line.substr( 0, 5 ) == "HTTP/" && line[9] >= '4' )
pthis->m_pimpl->error = true;
if ( pthis->m_pimpl->error )
pthis->m_pimpl->error_headers += line;
if ( pthis->m_log.get() )
pthis->m_log->Write( str, size*nmemb );
2012-05-01 20:26:48 +04:00
static const std::string loc = "Location: " ;
std::size_t pos = line.find( loc ) ;
if ( pos != line.npos )
{
std::size_t end_pos = line.find( "\r\n", pos ) ;
pthis->m_pimpl->location = line.substr( loc.size(), end_pos - loc.size() ) ;
}
return size*nmemb ;
}
std::size_t CurlAgent::Receive( void* ptr, size_t size, size_t nmemb, CurlAgent *pthis )
2012-05-05 18:39:18 +04:00
{
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
pthis->m_pimpl->error_data.append( static_cast<char*>(ptr), size * nmemb ) ;
return size * nmemb ;
}
return pthis->m_pimpl->dest->Write( static_cast<char*>(ptr), size * nmemb ) ;
2012-05-05 18:39:18 +04:00
}
2012-06-10 19:11:32 +04:00
long CurlAgent::ExecCurl(
const std::string& url,
DataStream *dest,
const http::Header& hdr )
2012-05-01 20:26:48 +04:00
{
CURL *curl = m_pimpl->curl ;
assert( curl != 0 ) ;
char error[CURL_ERROR_SIZE] = {} ;
::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error ) ;
::curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
2012-06-10 19:11:32 +04:00
::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &CurlAgent::Receive ) ;
::curl_easy_setopt(curl, CURLOPT_WRITEDATA, this ) ;
m_pimpl->dest = dest ;
2012-05-01 20:26:48 +04:00
2015-07-04 00:18:59 +03:00
struct curl_slist *slist = SetHeader( m_pimpl->curl, hdr ) ;
2012-05-20 20:10:29 +04:00
CURLcode curl_code = ::curl_easy_perform(curl);
2012-05-01 20:26:48 +04:00
2015-07-04 00:18:59 +03:00
curl_slist_free_all(slist);
2013-04-28 19:50:20 +04:00
// get the HTTP response code
2012-05-01 20:26:48 +04:00
long http_code = 0;
::curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
2012-05-13 21:07:23 +04:00
Trace( "HTTP response %1%", http_code ) ;
// reset the curl buffer to prevent it from touching our "error" buffer
::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, 0 ) ;
m_pimpl->dest = NULL;
// only throw for libcurl errors
if ( curl_code != CURLE_OK )
2012-05-01 20:26:48 +04:00
{
BOOST_THROW_EXCEPTION(
Error()
<< CurlCode( curl_code )
<< Url( url )
2013-04-28 19:50:20 +04:00
<< CurlErrMsg( error )
<< HttpRequestHeaders( hdr )
) ;
2012-05-01 20:26:48 +04:00
}
return http_code ;
2012-05-01 20:26:48 +04:00
}
long CurlAgent::Request(
const std::string& method,
2012-07-16 21:58:16 +04:00
const std::string& url,
SeekStream *in,
DataStream *dest,
2012-07-16 21:58:16 +04:00
const Header& hdr )
{
Trace("HTTP %1% \"%2%\"", method, url ) ;
2012-07-16 21:58:16 +04:00
Init() ;
CURL *curl = m_pimpl->curl ;
// set common options
2012-05-06 18:27:52 +04:00
::curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method.c_str() );
if ( in )
{
::curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L ) ;
::curl_easy_setopt(curl, CURLOPT_READFUNCTION, &ReadFileCallback ) ;
::curl_easy_setopt(curl, CURLOPT_READDATA , in ) ;
::curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, static_cast<curl_off_t>( in->Size() ) ) ;
}
2012-05-06 18:27:52 +04:00
return ExecCurl( url, dest, hdr ) ;
2012-05-06 18:27:52 +04:00
}
2015-07-04 00:18:59 +03:00
static struct curl_slist* SetHeader( CURL *handle, const Header& hdr )
2012-05-05 18:39:18 +04:00
{
// set headers
struct curl_slist *curl_hdr = 0 ;
for ( Header::iterator i = hdr.begin() ; i != hdr.end() ; ++i )
2012-05-05 18:39:18 +04:00
curl_hdr = curl_slist_append( curl_hdr, i->c_str() ) ;
2015-07-04 00:18:59 +03:00
::curl_easy_setopt( handle, CURLOPT_HTTPHEADER, curl_hdr ) ;
return curl_hdr;
2012-05-05 18:39:18 +04:00
}
std::string CurlAgent::LastError() const
{
return m_pimpl->error_data ;
}
std::string CurlAgent::LastErrorHeaders() const
{
return m_pimpl->error_headers ;
}
2012-06-10 19:11:32 +04:00
std::string CurlAgent::RedirLocation() const
2012-05-01 20:26:48 +04:00
{
return m_pimpl->location ;
}
2012-06-10 19:11:32 +04:00
std::string CurlAgent::Escape( const std::string& str )
{
CURL *curl = m_pimpl->curl ;
char *tmp = curl_easy_escape( curl, str.c_str(), str.size() ) ;
std::string result = tmp ;
curl_free( tmp ) ;
return result ;
}
2012-06-10 19:11:32 +04:00
std::string CurlAgent::Unescape( const std::string& str )
{
CURL *curl = m_pimpl->curl ;
int r ;
char *tmp = curl_easy_unescape( curl, str.c_str(), str.size(), &r ) ;
std::string result = tmp ;
curl_free( tmp ) ;
return result ;
}
2012-05-05 18:39:18 +04:00
} } // end of namespace