44#include <boost/filesystem/path.hpp>
45#include <boost/filesystem/operations.hpp>
46#include <boost/asio/error.hpp>
47#include <boost/asio/io_context.hpp>
48#include <boost/asio/connect.hpp>
49#include <boost/asio/ip/tcp.hpp>
50#include <boost/asio/ip/udp.hpp>
51#include <boost/asio/streambuf.hpp>
52#include <boost/asio/read.hpp>
53#include <boost/asio/write.hpp>
54#include <boost/spirit/include/qi.hpp>
69 boost::spirit::qi::phrase_parse ( aSemicolonDelimitedUriList.begin() , aSemicolonDelimitedUriList.end() , lGrammar , boost::spirit::ascii::space , aUriList );
71 catch (
const std::exception& aExc )
73 exception::UriListParsingError lExc;
74 log ( lExc ,
"Expression " ,
Quote ( aSemicolonDelimitedUriList ) ,
" must be a semicolon delimeted list and all files must be in the form " ,
Quote (
"protocol://address" ) );
78 log (
Debug() ,
"Parsed " ,
Quote ( aSemicolonDelimitedUriList ) ,
" to:" );
80 for (
const auto& x: aUriList)
82 log (
Debug() ,
" > [" , x.first ,
"] " ,
Quote ( x.second ) );
87 void ShellExpandFilenameExpr (
const std::string& aFilenameExpr ,
const boost::filesystem::path& aParentPath , std::vector< boost::filesystem::path >& aFiles )
92 wordexp_t lShellExpansion;
93 wordexp ( aFilenameExpr.c_str() , &lShellExpansion , 0 );
95 for ( std::size_t i = 0 ; i != lShellExpansion.we_wordc ; i++ )
97 boost::filesystem::path lPath ( lShellExpansion.we_wordv[i] );
99 log (
Debug() ,
"aParentPath is " ,
Quote ( aParentPath.c_str() ) );
100 lPath = boost::filesystem::absolute ( lPath , aParentPath );
103 if ( boost::filesystem::exists ( lPath ) )
105 if ( boost::filesystem::is_regular_file ( lPath ) )
107 aFiles.push_back ( lPath );
112 wordfree ( &lShellExpansion );
114 catch (
const std::exception& aExc )
116 uhal::exception::ExpandingShellExpressionFailed lExc;
117 log ( lExc ,
"Caught exception during shell expansion: " ,
Quote ( aExc.what() ) );
121 if ( ! aFiles.size() )
123 uhal::exception::FileNotFound lExc;
124 log ( lExc ,
"No matching files for expression " ,
Quote ( aFilenameExpr ) ,
" with parent path " ,
Quote ( aParentPath.c_str() ) );
129 log (
Debug() ,
"Shell expansion of " ,
Quote ( aFilenameExpr.c_str() ) ,
" returned:" );
131 for (
const auto& lFile: aFiles)
132 log (
Debug() ,
" > [file] " , lFile.c_str() );
137 template <
bool DebugInfo >
144 log (
Info() ,
"Retrieving URL http://" , aURL );
146 catch (
const std::exception& aExc )
152 std::pair<std::string, std::string> lURLPair;
157 boost::spirit::qi::phrase_parse ( aURL.begin() ,
159 + ( boost::spirit::qi::char_ -
"/" ) >> -boost::spirit::qi::lit (
"/" ) >> + ( boost::spirit::qi::char_ ) ,
160 boost::spirit::ascii::space ,
163 catch (
const std::exception& aExc )
169 boost::system::error_code lErrorCode ( boost::asio::error::host_not_found );
171 boost::asio::io_context io_service;
173 boost::asio::ip::tcp::resolver resolver ( io_service );
174 boost::asio::ip::tcp::resolver::results_type results = resolver.resolve ( lURLPair.first ,
"http" );
176 boost::asio::ip::tcp::socket socket ( io_service );
180 boost::asio::connect ( socket, results, lErrorCode );
184 throw boost::system::system_error ( lErrorCode );
187 catch (
const std::exception& aExc )
193 boost::asio::streambuf request;
194 std::ostream request_stream ( &request );
195 request_stream <<
"GET /" << lURLPair.second <<
" HTTP/1.0\r\n";
196 request_stream <<
"Host: " << lURLPair.first <<
"\r\n";
197 request_stream <<
"Accept: */*\r\n";
198 request_stream <<
"Connection: close\r\n\r\n";
203 boost::asio::write ( socket , request , lErrorCode );
207 throw boost::system::system_error ( lErrorCode );
210 catch (
const std::exception& aExc )
216 static const int mDefaultBufferSize ( 65536 );
217 typedef std::vector<uint8_t> BufferType;
218 BufferType mBuffer ( mDefaultBufferSize , uint8_t ( 0 ) );
219 std::size_t lSize ( 0 );
226 lSize += boost::asio::read ( socket, boost::asio::buffer ( &mBuffer[lSize] , mDefaultBufferSize ) , lErrorCode );
230 if ( lErrorCode == boost::asio::error::eof )
236 throw boost::system::system_error ( lErrorCode );
240 mBuffer.insert ( mBuffer.end() , mDefaultBufferSize , uint8_t ( 0 ) );
243 catch (
const std::exception& aExc )
248 mBuffer.resize ( lSize );
250 grammars::HttpResponseGrammar lGrammar2;
251 boost::spirit::qi::phrase_parse ( mBuffer.begin() , mBuffer.end() , lGrammar2 , boost::spirit::ascii::space , aResponse );
257 log (
Info() ,
"HTTP response parsed as:\n" , aResponse );
259 catch (
const std::exception& aExc )
261 log (
Error() ,
"EXCEPTION: " , aExc.what() ,
" caught in " ,
ThisLocation() ,
" Continuing." );
266 if ( aResponse.
method !=
"HTTP" || aResponse.
status != 200 )
281 std::vector< boost::filesystem::path > lFilePaths;
284 for (
const auto& lPath: lFilePaths)
286 std::ifstream lStr ( lPath.c_str() );
288 if ( !lStr.is_open() )
290 uhal::exception::CannotOpenFile lExc;
291 log ( lExc ,
"Failed to open file " ,
Quote ( lPath.c_str() ) );
296 lStr.seekg ( 0, std::ios::end );
297 std::vector<uint8_t> lFile ( lStr.tellg() , 0 );
298 lStr.seekg ( 0, std::ios::beg );
299 lStr.read ( (
char* ) & ( lFile[0] ) , lFile.size() );
300 aCallback ( std::string (
"file" ) , lPath , lFile);
314 uhal::exception::CannotOpenFile lExc;
315 log ( lExc ,
"Failed to download file " ,
Quote ( aURL ) );
319 boost::filesystem::path lFilePath = boost::filesystem::path ( aURL );
320 aCallback ( std::string (
"http" ) , lFilePath , lHttpResponse.
content );
324 void OpenFile (
const std::string& aProtocol ,
const std::string& aFilenameExpr ,
const boost::filesystem::path& aParentPath ,
const detail::FileCallback_t& aCallback )
326 if ( aProtocol ==
"file" )
330 else if ( aProtocol ==
"http" )
336 uhal::exception::NonSupportedUriProtocol lExc;
337 log ( lExc ,
"The protocol " ,
Quote ( aProtocol ) ,
" for file " ,
Quote ( aFilenameExpr ) ,
" is not supported." );
338 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 ...