mirror of https://github.com/vitalif/grive2
NSURLSession-based HTTP agent
parent
ffb744a59b
commit
1ec7d119e2
|
@ -14,6 +14,7 @@ bgrive/bgrive
|
|||
grive/grive
|
||||
libgrive/btest
|
||||
*.cmake
|
||||
.vscode/
|
||||
|
||||
debian/debhelper-build-stamp
|
||||
debian/files
|
||||
|
|
|
@ -23,7 +23,11 @@
|
|||
#include "base/Drive.hh"
|
||||
#include "drive2/Syncer2.hh"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "http/NSURLSessionAgent.hh"
|
||||
#else
|
||||
#include "http/CurlAgent.hh"
|
||||
#endif
|
||||
#include "protocol/AuthAgent.hh"
|
||||
#include "protocol/OAuth2.hh"
|
||||
#include "json/Val.hh"
|
||||
|
@ -164,7 +168,11 @@ int Main( int argc, char **argv )
|
|||
|
||||
Log( "config file name %1%", config.Filename(), log::verbose );
|
||||
|
||||
#ifdef __APPLE__
|
||||
std::unique_ptr<http::Agent> http( new http::NSURLSessionAgent );
|
||||
#else
|
||||
std::unique_ptr<http::Agent> http( new http::CurlAgent );
|
||||
#endif
|
||||
if ( vm.count( "log-http" ) )
|
||||
http->SetLog( new http::ResponseLog( vm["log-http"].as<std::string>(), ".txt" ) );
|
||||
|
||||
|
|
|
@ -3,13 +3,19 @@ project(libgrive)
|
|||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
|
||||
|
||||
find_package(LibGcrypt REQUIRED)
|
||||
find_package(CURL REQUIRED)
|
||||
if (NOT APPLE)
|
||||
find_package(CURL REQUIRED)
|
||||
endif(NOT APPLE)
|
||||
find_package(Backtrace)
|
||||
find_package(Boost 1.40.0 COMPONENTS program_options filesystem unit_test_framework regex system REQUIRED)
|
||||
find_package(BFD)
|
||||
find_package(CppUnit)
|
||||
find_package(Iberty)
|
||||
|
||||
if (APPLE)
|
||||
find_library(MAC_FRAME_FOUNDATION Foundation ${CMAKE_SYSTEM_FRAMEWORK_PATH} REQUIRED)
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(YAJL REQUIRED yajl)
|
||||
|
||||
|
@ -66,13 +72,17 @@ file (GLOB LIBGRIVE_SRC
|
|||
src/util/log/*.cc
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
file (GLOB LIBGRIVE_SRC_OBJC src/http/*.mm)
|
||||
endif(APPLE)
|
||||
|
||||
add_definitions(
|
||||
-DTEST_DATA="${libgrive_SOURCE_DIR}/test/data/"
|
||||
-DSRC_DIR="${libgrive_SOURCE_DIR}/src"
|
||||
)
|
||||
|
||||
#add_library( grive SHARED ${LIBGRIVE_SRC} ${OPT_SRC} )
|
||||
add_library( grive STATIC ${LIBGRIVE_SRC} ${OPT_SRC} )
|
||||
#add_library( grive SHARED ${LIBGRIVE_SRC} ${OPT_SRC} ${LIBGRIVE_SRC_OBJC} )
|
||||
add_library( grive STATIC ${LIBGRIVE_SRC} ${OPT_SRC} ${LIBGRIVE_SRC_OBJC} )
|
||||
|
||||
target_link_libraries( grive
|
||||
${YAJL_LIBRARIES}
|
||||
|
@ -85,6 +95,9 @@ target_link_libraries( grive
|
|||
${IBERTY_LIBRARY}
|
||||
${OPT_LIBS}
|
||||
)
|
||||
if (APPLE)
|
||||
target_link_libraries(grive ${MAC_FRAME_FOUNDATION})
|
||||
endif(APPLE)
|
||||
|
||||
#set_target_properties(grive PROPERTIES
|
||||
# SOVERSION 0 VERSION ${GRIVE_VERSION}
|
||||
|
@ -108,7 +121,6 @@ endif()
|
|||
IF ( CPPUNIT_FOUND )
|
||||
message("-- Building unitary tests along with the library and the binary")
|
||||
set( OPT_INCS ${CPPUNIT_INCLUDE_DIR} )
|
||||
|
||||
# list of test source files here
|
||||
file(GLOB TEST_SRC
|
||||
test/base/*.cc
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
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.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Agent.hh"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace gr {
|
||||
|
||||
class DataStream;
|
||||
|
||||
namespace http {
|
||||
|
||||
/*! \brief agent to provide HTTP access
|
||||
|
||||
This class provides functions to send HTTP request in many methods (e.g.
|
||||
get, post and put). Normally the HTTP response is returned in a Receivable.
|
||||
*/
|
||||
class NSURLSessionAgent : public Agent {
|
||||
public:
|
||||
NSURLSessionAgent();
|
||||
~NSURLSessionAgent();
|
||||
ResponseLog *GetLog() const;
|
||||
void SetLog( ResponseLog *log );
|
||||
void SetProgressReporter( Progress *progress );
|
||||
long Request( const std::string &method,
|
||||
const std::string &url,
|
||||
SeekStream *in,
|
||||
DataStream *dest,
|
||||
const Header &hdr,
|
||||
u64_t downloadFileBytes = 0 );
|
||||
std::string LastError() const;
|
||||
std::string LastErrorHeaders() const;
|
||||
std::string RedirLocation() const;
|
||||
std::string Escape( const std::string &str );
|
||||
std::string Unescape( const std::string &str );
|
||||
|
||||
private:
|
||||
void Init();
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> m_pimpl;
|
||||
std::unique_ptr<ResponseLog> m_log;
|
||||
Progress *m_pb;
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace gr
|
|
@ -0,0 +1,147 @@
|
|||
#include "NSURLSessionAgent.hh"
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
#include "Error.hh"
|
||||
#include "Header.hh"
|
||||
#include "util/log/Log.hh"
|
||||
|
||||
namespace gr {
|
||||
namespace http {
|
||||
|
||||
struct NSURLSessionAgent::Impl {
|
||||
NSURLSession *session;
|
||||
std::string location;
|
||||
bool error;
|
||||
std::string error_headers;
|
||||
std::string error_data;
|
||||
DataStream *dest;
|
||||
u64_t total_download, total_upload;
|
||||
};
|
||||
|
||||
NSURLSessionAgent::NSURLSessionAgent()
|
||||
: Agent(), m_pimpl( new Impl ), m_pb( 0 ) {
|
||||
m_pimpl->session = NSURLSession.sharedSession;
|
||||
}
|
||||
|
||||
NSURLSessionAgent::~NSURLSessionAgent() {
|
||||
}
|
||||
|
||||
void NSURLSessionAgent::Init() {
|
||||
m_pimpl->error = false;
|
||||
m_pimpl->error_headers = "";
|
||||
m_pimpl->error_data = "";
|
||||
m_pimpl->dest = NULL;
|
||||
m_pimpl->total_download = m_pimpl->total_upload = 0;
|
||||
}
|
||||
|
||||
ResponseLog *NSURLSessionAgent::GetLog() const {
|
||||
return m_log.get();
|
||||
}
|
||||
|
||||
void NSURLSessionAgent::SetLog( ResponseLog *log ) {
|
||||
m_log.reset( log );
|
||||
}
|
||||
|
||||
void NSURLSessionAgent::SetProgressReporter( Progress *progress ) {
|
||||
m_pb = progress;
|
||||
}
|
||||
|
||||
long NSURLSessionAgent::Request( const std::string &method,
|
||||
const std::string &url,
|
||||
SeekStream *in,
|
||||
DataStream *dest,
|
||||
const Header &hdr,
|
||||
u64_t downloadFileBytes ) {
|
||||
Trace( "HTTP %1% \"%2%\"", method, url );
|
||||
|
||||
Init();
|
||||
m_pimpl->total_download = downloadFileBytes;
|
||||
m_pimpl->dest = dest;
|
||||
|
||||
NSURL *nsurl =
|
||||
[NSURL URLWithString:[NSString stringWithUTF8String:url.c_str()]];
|
||||
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:nsurl];
|
||||
req.HTTPMethod = [NSString stringWithUTF8String:method.c_str()];
|
||||
|
||||
const auto colon = std::string( ":" );
|
||||
for ( auto it = hdr.begin(); it != hdr.end(); it++ ) {
|
||||
const auto index = it->find_first_of( colon );
|
||||
[req setValue:[[NSString
|
||||
stringWithUTF8String:it->substr( index + 1 ).c_str()]
|
||||
stringByTrimmingCharactersInSet:
|
||||
NSCharacterSet.whitespaceAndNewlineCharacterSet]
|
||||
forHTTPHeaderField:
|
||||
[[NSString stringWithUTF8String:it->substr( 0, index ).c_str()]
|
||||
stringByTrimmingCharactersInSet:
|
||||
NSCharacterSet.whitespaceAndNewlineCharacterSet]];
|
||||
}
|
||||
|
||||
if ( in ) {
|
||||
char *ptr = static_cast<char *>( malloc( in->Size() ) );
|
||||
std::size_t n = in->Read( ptr, in->Size() );
|
||||
assert( n == in->Size() );
|
||||
req.HTTPBody = [NSData dataWithBytes:ptr
|
||||
length:( NSUInteger )in->Size()];
|
||||
}
|
||||
|
||||
__block NSInteger statusCode = 0;
|
||||
dispatch_semaphore_t sema = dispatch_semaphore_create( 0 );
|
||||
[[m_pimpl->session
|
||||
dataTaskWithRequest:req
|
||||
completionHandler:^(
|
||||
NSData *data, NSURLResponse *response, NSError *error ) {
|
||||
m_pimpl->total_download += data.length;
|
||||
if ( error ) {
|
||||
m_pimpl->error = true;
|
||||
m_pimpl->error_data.append(
|
||||
error.localizedDescription.UTF8String,
|
||||
error.localizedDescription.length );
|
||||
m_pimpl->error_headers = hdr.Str();
|
||||
return;
|
||||
}
|
||||
assert( data.bytes != nil );
|
||||
statusCode = ( ( NSHTTPURLResponse * )response ).statusCode;
|
||||
if ( statusCode < 200 || statusCode > 299 ) {
|
||||
m_pimpl->error = true;
|
||||
m_pimpl->error_data.append(
|
||||
static_cast<const char *>( data.bytes ) );
|
||||
}
|
||||
m_pimpl->dest->Write( static_cast<const char *>( data.bytes ),
|
||||
data.length );
|
||||
dispatch_semaphore_signal( sema );
|
||||
}] resume];
|
||||
dispatch_semaphore_wait( sema, dispatch_time( DISPATCH_TIME_NOW, 30e9 ) );
|
||||
|
||||
if ( m_pimpl->error ) {
|
||||
BOOST_THROW_EXCEPTION( Error()
|
||||
<< Url( url ) << HttpRequestHeaders( hdr )
|
||||
<< HttpResponseCode( statusCode )
|
||||
<< HttpResponseText( m_pimpl->error_data ) );
|
||||
}
|
||||
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
std::string NSURLSessionAgent::LastError() const {
|
||||
return m_pimpl->error_data;
|
||||
}
|
||||
|
||||
std::string NSURLSessionAgent::LastErrorHeaders() const {
|
||||
return m_pimpl->error_headers;
|
||||
}
|
||||
|
||||
std::string NSURLSessionAgent::RedirLocation() const {
|
||||
return m_pimpl->location;
|
||||
}
|
||||
|
||||
std::string NSURLSessionAgent::Escape( const std::string &str ) {
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string NSURLSessionAgent::Unescape( const std::string &str ) {
|
||||
return str;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue