44#include <boost/filesystem/path.hpp>
45#include <boost/filesystem/operations.hpp>
46#include <boost/asio/error.hpp>
47#include <boost/asio/io_service.hpp>
48#include <boost/asio/ip/tcp.hpp>
49#include <boost/asio/ip/udp.hpp>
50#include <boost/asio/streambuf.hpp>
51#include <boost/asio/read.hpp>
52#include <boost/asio/write.hpp>
53#include <boost/spirit/include/qi.hpp>
68 boost::spirit::qi::phrase_parse ( aSemicolonDelimitedUriList.begin() , aSemicolonDelimitedUriList.end() , lGrammar , boost::spirit::ascii::space , aUriList );
70 catch (
const std::exception& aExc )
72 exception::UriListParsingError lExc;
73 log ( lExc ,
"Expression " ,
Quote ( aSemicolonDelimitedUriList ) ,
" must be a semicolon delimeted list and all files must be in the form " ,
Quote (
"protocol://address" ) );
77 log (
Debug() ,
"Parsed " ,
Quote ( aSemicolonDelimitedUriList ) ,
" to:" );
79 for (
const auto& x: aUriList)
81 log (
Debug() ,
" > [" , x.first ,
"] " ,
Quote ( x.second ) );
86 void ShellExpandFilenameExpr (
const std::string& aFilenameExpr ,
const boost::filesystem::path& aParentPath , std::vector< boost::filesystem::path >& aFiles )
91 wordexp_t lShellExpansion;
92 wordexp ( aFilenameExpr.c_str() , &lShellExpansion , 0 );
94 for ( std::size_t i = 0 ; i != lShellExpansion.we_wordc ; i++ )
96 boost::filesystem::path lPath ( lShellExpansion.we_wordv[i] );
98 log (
Debug() ,
"aParentPath is " ,
Quote ( aParentPath.c_str() ) );
99 lPath = boost::filesystem::absolute ( lPath , aParentPath );
102 if ( boost::filesystem::exists ( lPath ) )
104 if ( boost::filesystem::is_regular_file ( lPath ) )
106 aFiles.push_back ( lPath );
111 wordfree ( &lShellExpansion );
113 catch (
const std::exception& aExc )
115 uhal::exception::ExpandingShellExpressionFailed lExc;
116 log ( lExc ,
"Caught exception during shell expansion: " ,
Quote ( aExc.what() ) );
120 if ( ! aFiles.size() )
122 uhal::exception::FileNotFound lExc;
123 log ( lExc ,
"No matching files for expression " ,
Quote ( aFilenameExpr ) ,
" with parent path " ,
Quote ( aParentPath.c_str() ) );
128 log (
Debug() ,
"Shell expansion of " ,
Quote ( aFilenameExpr.c_str() ) ,
" returned:" );
130 for (
const auto& lFile: aFiles)
131 log (
Debug() ,
" > [file] " , lFile.c_str() );
136 template <
bool DebugInfo >
143 log (
Info() ,
"Retrieving URL http://" , aURL );
145 catch (
const std::exception& aExc )
151 std::pair<std::string, std::string> lURLPair;
156 boost::spirit::qi::phrase_parse ( aURL.begin() ,
158 + ( boost::spirit::qi::char_ -
"/" ) >> -boost::spirit::qi::lit (
"/" ) >> + ( boost::spirit::qi::char_ ) ,
159 boost::spirit::ascii::space ,
162 catch (
const std::exception& aExc )
168 boost::system::error_code lErrorCode ( boost::asio::error::host_not_found );
170 boost::asio::io_service io_service;
172 boost::asio::ip::tcp::resolver resolver ( io_service );
173 boost::asio::ip::tcp::resolver::query query ( lURLPair.first ,
"http" );
174 boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve ( query );
175 boost::asio::ip::tcp::resolver::iterator end;
177 boost::asio::ip::tcp::socket socket ( io_service );
181 while ( lErrorCode && endpoint_iterator != end )
184 socket.connect ( *endpoint_iterator++, lErrorCode );
189 throw boost::system::system_error ( lErrorCode );
192 catch (
const std::exception& aExc )
198 boost::asio::streambuf request;
199 std::ostream request_stream ( &request );
200 request_stream <<
"GET /" << lURLPair.second <<
" HTTP/1.0\r\n";
201 request_stream <<
"Host: " << lURLPair.first <<
"\r\n";
202 request_stream <<
"Accept: */*\r\n";
203 request_stream <<
"Connection: close\r\n\r\n";
208 boost::asio::write ( socket , request , lErrorCode );
212 throw boost::system::system_error ( lErrorCode );
215 catch (
const std::exception& aExc )
221 static const int mDefaultBufferSize ( 65536 );
222 typedef std::vector<uint8_t> BufferType;
223 BufferType mBuffer ( mDefaultBufferSize , uint8_t ( 0 ) );
224 std::size_t lSize ( 0 );
231 lSize += boost::asio::read ( socket, boost::asio::buffer ( &mBuffer[lSize] , mDefaultBufferSize ) , lErrorCode );
235 if ( lErrorCode == boost::asio::error::eof )
241 throw boost::system::system_error ( lErrorCode );
245 mBuffer.insert ( mBuffer.end() , mDefaultBufferSize , uint8_t ( 0 ) );
248 catch (
const std::exception& aExc )
253 mBuffer.resize ( lSize );
255 grammars::HttpResponseGrammar lGrammar2;
256 boost::spirit::qi::phrase_parse ( mBuffer.begin() , mBuffer.end() , lGrammar2 , boost::spirit::ascii::space , aResponse );
262 log (
Info() ,
"HTTP response parsed as:\n" , aResponse );
264 catch (
const std::exception& aExc )
266 log (
Error() ,
"EXCEPTION: " , aExc.what() ,
" caught in " ,
ThisLocation() ,
" Continuing." );
271 if ( aResponse.
method !=
"HTTP" || aResponse.
status != 200 )
286 std::vector< boost::filesystem::path > lFilePaths;
289 for (
const auto& lPath: lFilePaths)
291 std::ifstream lStr ( lPath.c_str() );
293 if ( !lStr.is_open() )
295 uhal::exception::CannotOpenFile lExc;
296 log ( lExc ,
"Failed to open file " ,
Quote ( lPath.c_str() ) );
301 lStr.seekg ( 0, std::ios::end );
302 std::vector<uint8_t> lFile ( lStr.tellg() , 0 );
303 lStr.seekg ( 0, std::ios::beg );
304 lStr.read ( (
char* ) & ( lFile[0] ) , lFile.size() );
305 aCallback ( std::string (
"file" ) , lPath , lFile);
319 uhal::exception::CannotOpenFile lExc;
320 log ( lExc ,
"Failed to download file " ,
Quote ( aURL ) );
324 boost::filesystem::path lFilePath = boost::filesystem::path ( aURL );
325 aCallback ( std::string (
"http" ) , lFilePath , lHttpResponse.
content );
329 void OpenFile (
const std::string& aProtocol ,
const std::string& aFilenameExpr ,
const boost::filesystem::path& aParentPath ,
const detail::FileCallback_t& aCallback )
331 if ( aProtocol ==
"file" )
335 else if ( aProtocol ==
"http" )
341 uhal::exception::NonSupportedUriProtocol lExc;
342 log ( lExc ,
"The protocol " ,
Quote ( aProtocol ) ,
" for file " ,
Quote ( aFilenameExpr ) ,
" is not supported." );
343 log ( lExc ,
"The supported protocols are " ,
Quote (
"file://" ) ,
" and " ,
Quote (
"http://" ) );
std::function< void(const std::string &, const boost::filesystem::path &, std::vector< uint8_t > &aCallback)> FileCallback_t
void ParseSemicolonDelimitedUriList(const std::string &aSemicolonDelimitedUriList, std::vector< std::pair< std::string, std::string > > &aUriList)
Parse a semicolon delimited list of URIs into a vector of protocol/address pairs.
bool HttpGet(const std::string &aURL, HttpResponseType &aResponse)
Retrieve a file by HTTP.
void OpenFileLocal(const std::string &aFilenameExpr, const boost::filesystem::path &aParentPath, const detail::FileCallback_t &aCallback)
Given a linux shell expression, open all files which match it and call the callback function on each ...
template bool HttpGet< true >(const std::string &, HttpResponseType &)
void ShellExpandFilenameExpr(const std::string &aFilenameExpr, const boost::filesystem::path &aParentPath, std::vector< boost::filesystem::path > &aFiles)
Perform shell expansion of a linux shell expression ( e.g.
template bool HttpGet< false >(const std::string &, HttpResponseType &)
void OpenFileHttp(const std::string &aURL, const detail::FileCallback_t &aCallback)
Given a URL, retrieve the file and call the callback function on each of them.
void OpenFile(const std::string &aProtocol, const std::string &aFilenameExpr, const boost::filesystem::path &aParentPath, const detail::FileCallback_t &aCallback)
Given a protocol and either a URL or a linux shell expression, open the file and call the callback fu...
_Quote< T > Quote(const T &aT)
void log(FatalLevel &aFatal, const T0 &aArg0)
Function to add a log entry at Fatal level.
Struct to store an http response received from a server when parsed by boost spirit.
std::string method
the http transport method
int status
the response status
std::vector< uint8_t > content
parsed message content
The BOOST::SPIRIT grammar for parsing the Semicolon delimited URI list into a vector of protocol-URI ...