μHAL (v2.8.22)
Part of the IPbus software repository
Loading...
Searching...
No Matches
files.cpp
Go to the documentation of this file.
1/*
2---------------------------------------------------------------------------
3
4 This file is part of uHAL.
5
6 uHAL is a hardware access library and programming framework
7 originally developed for upgrades of the Level-1 trigger of the CMS
8 experiment at CERN.
9
10 uHAL is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 uHAL is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with uHAL. If not, see <http://www.gnu.org/licenses/>.
22
23
24 Andrew Rose, Imperial College, London
25 email: awr01 <AT> imperial.ac.uk
26
27 Marc Magrans de Abril, CERN
28 email: marc.magrans.de.abril <AT> cern.ch
29
30 Tom Williams, Rutherford Appleton Laboratory, Oxfordshire
31 email: tom.williams <AT> cern.ch
32
33---------------------------------------------------------------------------
34*/
35
37
38
39#include <fstream>
40#include <iostream>
41
42#include <wordexp.h>
43
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>
55
56#include "uhal/log/log.hpp"
57
58
59namespace uhal
60{
61 namespace utilities
62 {
63
64 void ParseSemicolonDelimitedUriList ( const std::string& aSemicolonDelimitedUriList , std::vector< std::pair<std::string, std::string> >& aUriList )
65 {
66 try
67 {
69 boost::spirit::qi::phrase_parse ( aSemicolonDelimitedUriList.begin() , aSemicolonDelimitedUriList.end() , lGrammar , boost::spirit::ascii::space , aUriList );
70 }
71 catch ( const std::exception& aExc )
72 {
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" ) );
75 throw lExc;
76 }
77
78 log ( Debug() , "Parsed " , Quote ( aSemicolonDelimitedUriList ) , " to:" );
79
80 for (const auto& x: aUriList)
81 {
82 log ( Debug() , " > [" , x.first , "] " , Quote ( x.second ) );
83 }
84 }
85
86
87 void ShellExpandFilenameExpr ( const std::string& aFilenameExpr , const boost::filesystem::path& aParentPath , std::vector< boost::filesystem::path >& aFiles )
88 {
89 try
90 {
91 //struct which will store the shell expansions of the expression
92 wordexp_t lShellExpansion;
93 wordexp ( aFilenameExpr.c_str() , &lShellExpansion , 0 );
94
95 for ( std::size_t i = 0 ; i != lShellExpansion.we_wordc ; i++ )
96 {
97 boost::filesystem::path lPath ( lShellExpansion.we_wordv[i] );
98 log ( Debug() , "lPath was " , Quote ( lPath.c_str() ) );
99 log ( Debug() , "aParentPath is " , Quote ( aParentPath.c_str() ) );
100 lPath = boost::filesystem::absolute ( lPath , aParentPath );
101 log ( Debug() , "lPath now " , Quote ( lPath.c_str() ) );
102
103 if ( boost::filesystem::exists ( lPath ) )
104 {
105 if ( boost::filesystem::is_regular_file ( lPath ) )
106 {
107 aFiles.push_back ( lPath );
108 }
109 }
110 }
111
112 wordfree ( &lShellExpansion );
113 }
114 catch ( const std::exception& aExc )
115 {
116 uhal::exception::ExpandingShellExpressionFailed lExc;
117 log ( lExc , "Caught exception during shell expansion: " , Quote ( aExc.what() ) );
118 throw lExc;
119 }
120
121 if ( ! aFiles.size() )
122 {
123 uhal::exception::FileNotFound lExc;
124 log ( lExc , "No matching files for expression " , Quote ( aFilenameExpr ) , " with parent path " , Quote ( aParentPath.c_str() ) );
125 throw lExc;
126 }
127 else
128 {
129 log ( Debug() , "Shell expansion of " , Quote ( aFilenameExpr.c_str() ) , " returned:" );
130
131 for (const auto& lFile: aFiles)
132 log ( Debug() , " > [file] " , lFile.c_str() );
133 }
134 }
135
136
137 template < bool DebugInfo >
138 bool HttpGet ( const std::string& aURL , HttpResponseType& aResponse )
139 {
140 if ( DebugInfo )
141 {
142 try
143 {
144 log ( Info() , "Retrieving URL http://" , aURL );
145 }
146 catch ( const std::exception& aExc )
147 {
148 log ( Error() , "Exception " , Quote ( aExc.what() ) , " caught at " , ThisLocation() ); // Just debugging so although exception is worrying, it is not critical
149 }
150 }
151
152 std::pair<std::string, std::string> lURLPair;
153
154 try
155 {
156 //split at the first slash
157 boost::spirit::qi::phrase_parse ( aURL.begin() ,
158 aURL.end() ,
159 + ( boost::spirit::qi::char_ - "/" ) >> -boost::spirit::qi::lit ( "/" ) >> + ( boost::spirit::qi::char_ ) ,
160 boost::spirit::ascii::space ,
161 lURLPair );
162 }
163 catch ( const std::exception& aExc )
164 {
165 // log ( Error() , "Exception " , Quote ( aExc.what() ) , " caught at " , ThisLocation() );
166 return false;
167 }
168
169 boost::system::error_code lErrorCode ( boost::asio::error::host_not_found );
170 // The IO service everything will go through
171 boost::asio::io_context io_service;
172 // Get a list of endpoints corresponding to the server name.
173 boost::asio::ip::tcp::resolver resolver ( io_service );
174 boost::asio::ip::tcp::resolver::results_type results = resolver.resolve ( lURLPair.first , "http" );
175 // Try each endpoint until we successfully establish a connection.
176 boost::asio::ip::tcp::socket socket ( io_service );
177
178 try
179 {
180 boost::asio::connect ( socket, results, lErrorCode );
181
182 if ( lErrorCode )
183 {
184 throw boost::system::system_error ( lErrorCode );
185 }
186 }
187 catch ( const std::exception& aExc )
188 {
189 return false;
190 }
191
192 // Form the request. We specify the "Connection: close" header so that the server will close the socket after transmitting the response. This will allow us to treat all data up until the EOF as the content.
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";
199
200 try
201 {
202 // Send the request...
203 boost::asio::write ( socket , request , lErrorCode );
204
205 if ( lErrorCode )
206 {
207 throw boost::system::system_error ( lErrorCode );
208 }
209 }
210 catch ( const std::exception& aExc )
211 {
212 return false;
213 }
214
215 // ... and get the reply. First we need a buffer to write the reply in to...
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 );
220
221 // Just keep reading and, if we have not reached the EOF, extend the buffer and read some more.
222 try
223 {
224 while ( true )
225 {
226 lSize += boost::asio::read ( socket, boost::asio::buffer ( &mBuffer[lSize] , mDefaultBufferSize ) , lErrorCode );
227
228 if ( lErrorCode )
229 {
230 if ( lErrorCode == boost::asio::error::eof )
231 {
232 break;
233 }
234 else
235 {
236 throw boost::system::system_error ( lErrorCode );
237 }
238 }
239
240 mBuffer.insert ( mBuffer.end() , mDefaultBufferSize , uint8_t ( 0 ) );
241 }
242 }
243 catch ( const std::exception& aExc )
244 {
245 return false;
246 }
247
248 mBuffer.resize ( lSize );
249 //Parse the recieved data into an HttpResponseType object
250 grammars::HttpResponseGrammar lGrammar2;
251 boost::spirit::qi::phrase_parse ( mBuffer.begin() , mBuffer.end() , lGrammar2 , boost::spirit::ascii::space , aResponse );
252
253 if ( DebugInfo )
254 {
255 try
256 {
257 log ( Info() , "HTTP response parsed as:\n" , aResponse );
258 }
259 catch ( const std::exception& aExc )
260 {
261 log ( Error() , "EXCEPTION: " , aExc.what() , " caught in " , ThisLocation() , " Continuing." );
262 // Just debugging so although exception is worrying, it is not critical
263 }
264 }
265
266 if ( aResponse.method != "HTTP" || aResponse.status != 200 )
267 {
268 return false;
269 }
270
271 return true;
272 }
273
274
275 template bool HttpGet<false> ( const std::string& , HttpResponseType& );
276 template bool HttpGet<true> ( const std::string& , HttpResponseType& );
277
278
279 void OpenFileLocal ( const std::string& aFilenameExpr , const boost::filesystem::path& aParentPath , const detail::FileCallback_t& aCallback)
280 {
281 std::vector< boost::filesystem::path > lFilePaths;
282 uhal::utilities::ShellExpandFilenameExpr ( aFilenameExpr , aParentPath , lFilePaths );
283
284 for (const auto& lPath: lFilePaths)
285 {
286 std::ifstream lStr ( lPath.c_str() );
287
288 if ( !lStr.is_open() )
289 {
290 uhal::exception::CannotOpenFile lExc;
291 log ( lExc , "Failed to open file " , Quote ( lPath.c_str() ) );
292 throw lExc ;
293 }
294 else
295 {
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);
301 }
302
303 lStr.close();
304 }
305 }
306
307
308 void OpenFileHttp ( const std::string& aURL , const detail::FileCallback_t& aCallback )
309 {
310 HttpResponseType lHttpResponse;
311
312 if ( ! uhal::utilities::HttpGet<true> ( aURL , lHttpResponse ) )
313 {
314 uhal::exception::CannotOpenFile lExc;
315 log ( lExc , "Failed to download file " , Quote ( aURL ) );
316 throw lExc;
317 }
318
319 boost::filesystem::path lFilePath = boost::filesystem::path ( aURL );
320 aCallback ( std::string ( "http" ) , lFilePath , lHttpResponse.content );
321 }
322
323
324 void OpenFile ( const std::string& aProtocol , const std::string& aFilenameExpr , const boost::filesystem::path& aParentPath , const detail::FileCallback_t& aCallback )
325 {
326 if ( aProtocol == "file" )
327 {
328 uhal::utilities::OpenFileLocal ( aFilenameExpr , aParentPath , aCallback );
329 }
330 else if ( aProtocol == "http" )
331 {
332 uhal::utilities::OpenFileHttp ( aFilenameExpr , aCallback );
333 }
334 else
335 {
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://" ) );
339 throw lExc;
340 }
341 }
342 }
343}
344
#define ThisLocation()
std::function< void(const std::string &, const boost::filesystem::path &, std::vector< uint8_t > &aCallback)> FileCallback_t
Definition: files.hpp:102
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.
Definition: files.cpp:64
bool HttpGet(const std::string &aURL, HttpResponseType &aResponse)
Retrieve a file by HTTP.
Definition: files.cpp:138
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 ...
Definition: files.cpp:279
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.
Definition: files.cpp:87
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.
Definition: files.cpp:308
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...
Definition: files.cpp:324
DebugLevel Debug
Definition: LogLevels.cpp:133
_Quote< T > Quote(const T &aT)
ErrorLevel Error
Definition: LogLevels.cpp:61
void log(FatalLevel &aFatal, const T0 &aArg0)
Function to add a log entry at Fatal level.
Definition: log.hxx:18
InfoLevel Info
Definition: LogLevels.cpp:115
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 ...