μHAL (v2.7.9)
Part of the IPbus software repository
files.hpp
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 
36 #ifndef _uhal_utilities_files_hpp_
37 #define _uhal_utilities_files_hpp_
38 
39 
40 #include <fstream>
41 #include <iostream>
42 #include <string>
43 #include <vector>
44 
45 #include <wordexp.h>
46 
47 #include <boost/filesystem/path.hpp>
48 #include <boost/filesystem/operations.hpp>
49 #include <boost/asio/error.hpp>
50 #include <boost/asio/io_service.hpp>
51 #include <boost/asio/ip/tcp.hpp>
52 #include <boost/asio/ip/udp.hpp>
53 #include <boost/asio/streambuf.hpp>
54 #include <boost/asio/read.hpp>
55 #include <boost/asio/write.hpp>
56 #include <boost/spirit/home/qi/parse.hpp>
57 #include <boost/spirit/include/qi_char_.hpp>
58 #include <boost/bind/bind.hpp>
59 
63 
64 #include "pugixml.hpp"
65 
66 #include "uhal/log/log.hpp"
67 #include "uhal/log/exception.hpp"
68 
69 #include "boost/unordered_map.hpp"
70 
71 
72 // ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
73 namespace uhal
74 {
75  namespace exception
76  {
78  UHAL_DEFINE_EXCEPTION_CLASS ( UriListParsingError , "Exception class to handle the case where the string is not a comma-delimiter list of URIs." )
80  UHAL_DEFINE_EXCEPTION_CLASS ( NonSupportedUriProtocol , "Exception class to handle the case where a URI contains a non-supported protocol." )
82  UHAL_DEFINE_EXCEPTION_CLASS ( CannotOpenFile , "Exception class to handle the case where a URI can not be opened." )
84  UHAL_DEFINE_EXCEPTION_CLASS ( FileNotFound , "Exception class to handle the case where a URI using the 'file://' protocol can not be expanded." )
86  UHAL_DEFINE_EXCEPTION_CLASS ( ExpandingShellExpressionFailed , "Exception class to handle the case where expanding a shell expression failed." )
87  }
88 
89  namespace utilities
90  {
96  void ParseSemicolonDelimitedUriList ( const std::string& aSemicolonDelimitedUriList , std::vector< std::pair<std::string, std::string> >& aUriList );
97  }
98 }
99 // ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
100 
101 
102 
103 // ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
104 namespace uhal
105 {
106  namespace utilities
107  {
114  void ShellExpandFilenameExpr ( const std::string& aFilenameExpr , const boost::filesystem::path& aParentPath , std::vector< boost::filesystem::path >& aFiles );
115  }
116 }
117 // ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
118 
119 
120 
121 // ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
122 namespace uhal
123 {
124  namespace utilities
125  {
132  template < bool DebugInfo >
133  bool HttpGet ( const std::string& aURL , HttpResponseType& aResponse )
134  {
135  if ( DebugInfo )
136  {
137  try
138  {
139  log ( Info() , "Retrieving URL http://" , aURL );
140  }
141  catch ( const std::exception& aExc )
142  {
143  log ( Error() , "Exception " , Quote ( aExc.what() ) , " caught at " , ThisLocation() ); // Just debugging so although exception is worrying, it is not critical
144  }
145  }
146 
147  std::pair<std::string, std::string> lURLPair;
148 
149  try
150  {
151  //split at the first slash
152  boost::spirit::qi::phrase_parse ( aURL.begin() ,
153  aURL.end() ,
154  + ( boost::spirit::qi::char_ - "/" ) >> -boost::spirit::qi::lit ( "/" ) >> + ( boost::spirit::qi::char_ ) ,
155  boost::spirit::ascii::space ,
156  lURLPair );
157  }
158  catch ( const std::exception& aExc )
159  {
160  // log ( Error() , "Exception " , Quote ( aExc.what() ) , " caught at " , ThisLocation() );
161  return false;
162  }
163 
164  boost::system::error_code lErrorCode ( boost::asio::error::host_not_found );
165  // The IO service everything will go through
166  boost::asio::io_service io_service;
167  // Get a list of endpoints corresponding to the server name.
168  boost::asio::ip::tcp::resolver resolver ( io_service );
169  boost::asio::ip::tcp::resolver::query query ( lURLPair.first , "http" );
170  boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve ( query );
171  boost::asio::ip::tcp::resolver::iterator end;
172  // Try each endpoint until we successfully establish a connection.
173  boost::asio::ip::tcp::socket socket ( io_service );
174 
175  try
176  {
177  while ( lErrorCode && endpoint_iterator != end )
178  {
179  socket.close();
180  socket.connect ( *endpoint_iterator++, lErrorCode );
181  }
182 
183  if ( lErrorCode )
184  {
185  throw boost::system::system_error ( lErrorCode );
186  }
187  }
188  catch ( const std::exception& aExc )
189  {
190  return false;
191  }
192 
193  // 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.
194  boost::asio::streambuf request;
195  std::ostream request_stream ( &request );
196  request_stream << "GET /" << lURLPair.second << " HTTP/1.0\r\n";
197  request_stream << "Host: " << lURLPair.first << "\r\n";
198  request_stream << "Accept: */*\r\n";
199  request_stream << "Connection: close\r\n\r\n";
200 
201  try
202  {
203  // Send the request...
204  boost::asio::write ( socket , request , lErrorCode );
205 
206  if ( lErrorCode )
207  {
208  throw boost::system::system_error ( lErrorCode );
209  }
210  }
211  catch ( const std::exception& aExc )
212  {
213  return false;
214  }
215 
216  // ... and get the reply. First we need a buffer to write the reply in to...
217  static const int mDefaultBufferSize ( 65536 );
218  typedef std::vector<uint8_t> BufferType;
219  BufferType mBuffer ( mDefaultBufferSize , uint8_t ( 0 ) );
220  std::size_t lSize ( 0 );
221 
222  // Just keep reading and, if we have not reached the EOF, extend the buffer and read some more.
223  try
224  {
225  while ( true )
226  {
227  lSize += boost::asio::read ( socket, boost::asio::buffer ( &mBuffer[lSize] , mDefaultBufferSize ) , lErrorCode );
228 
229  if ( lErrorCode )
230  {
231  if ( lErrorCode == boost::asio::error::eof )
232  {
233  break;
234  }
235  else
236  {
237  throw boost::system::system_error ( lErrorCode );
238  }
239  }
240 
241  mBuffer.insert ( mBuffer.end() , mDefaultBufferSize , uint8_t ( 0 ) );
242  }
243  }
244  catch ( const std::exception& aExc )
245  {
246  return false;
247  }
248 
249  mBuffer.resize ( lSize );
250  //Parse the recieved data into an HttpResponseType object
251  grammars::HttpResponseGrammar lGrammar2;
252  boost::spirit::qi::phrase_parse ( mBuffer.begin() , mBuffer.end() , lGrammar2 , boost::spirit::ascii::space , aResponse );
253 
254  if ( DebugInfo )
255  {
256  try
257  {
258  log ( Info() , "HTTP response parsed as:\n" , aResponse );
259  }
260  catch ( const std::exception& aExc )
261  {
262  log ( Error() , "EXCEPTION: " , aExc.what() , " caught in " , ThisLocation() , " Continuing." );
263  // Just debugging so although exception is worrying, it is not critical
264  }
265  }
266 
267  if ( aResponse.method != "HTTP" || aResponse.status != 200 )
268  {
269  return false;
270  }
271 
272  return true;
273  }
274  }
275 }
276 // ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
277 
278 
279 
280 // ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
281 namespace uhal
282 {
283  namespace utilities
284  {
291  template < typename R , typename F , typename L>
292  void OpenFileLocal ( const std::string& aFilenameExpr , const boost::filesystem::path& aParentPath , boost::_bi::bind_t<R,F,L> aBinder )
293  {
294  std::vector< boost::filesystem::path > lFilePaths;
295  uhal::utilities::ShellExpandFilenameExpr ( aFilenameExpr , aParentPath , lFilePaths );
296 
297  for ( std::vector< boost::filesystem::path >::iterator lIt2 = lFilePaths.begin() ; lIt2 != lFilePaths.end() ; ++ lIt2 )
298  {
299  std::ifstream lStr ( lIt2->c_str() );
300 
301  if ( !lStr.is_open() )
302  {
303  uhal::exception::CannotOpenFile lExc;
304  log ( lExc , "Failed to open file " , Quote ( lIt2->c_str() ) );
305  throw lExc ;
306  }
307  else
308  {
309  lStr.seekg ( 0, std::ios::end );
310  std::vector<uint8_t> lFile ( lStr.tellg() , 0 );
311  lStr.seekg ( 0, std::ios::beg );
312  lStr.read ( ( char* ) & ( lFile[0] ) , lFile.size() );
313  aBinder ( std::string ( "file" ) , *lIt2 , boost::ref ( lFile ) );
314  }
315 
316  lStr.close();
317  }
318  }
319 
320 
327  template < typename R , typename F , typename L>
328  void OpenFileHttp ( const std::string& aURL , boost::_bi::bind_t<R,F,L> aBinder )
329  {
330  HttpResponseType lHttpResponse;
331 
332  if ( ! uhal::utilities::HttpGet<true> ( aURL , lHttpResponse ) )
333  {
334  uhal::exception::CannotOpenFile lExc;
335  log ( lExc , "Failed to download file " , Quote ( aURL ) );
336  throw lExc;
337  }
338 
339  boost::filesystem::path lFilePath = boost::filesystem::path ( aURL );
340  aBinder ( std::string ( "http" ) , lFilePath , boost::ref ( lHttpResponse.content ) );
341  }
342 
343 
352  template < typename R , typename F , typename L>
353  void OpenFile ( const std::string& aProtocol , const std::string& aFilenameExpr , const boost::filesystem::path& aParentPath , boost::_bi::bind_t<R,F,L> aBinder )
354  {
355  if ( aProtocol == "file" )
356  {
357  uhal::utilities::OpenFileLocal ( aFilenameExpr , aParentPath , aBinder );
358  }
359  else if ( aProtocol == "http" )
360  {
361  uhal::utilities::OpenFileHttp ( aFilenameExpr , aBinder );
362  }
363  else
364  {
365  uhal::exception::NonSupportedUriProtocol lExc;
366  log ( lExc , "The protocol " , Quote ( aProtocol ) , " for file " , Quote ( aFilenameExpr ) , " is not supported." );
367  log ( lExc , "The supported protocols are " , Quote ( "file://" ) , " and " , Quote ( "http://" ) );
368  throw lExc;
369  }
370  }
371 
372  }
373 }
374 // ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
375 
376 
377 #endif
UHAL_DEFINE_EXCEPTION_CLASS
#define UHAL_DEFINE_EXCEPTION_CLASS(ClassName, ClassDescription)
Definition: exception.hpp:59
uhal::utilities::ShellExpandFilenameExpr
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:69
SemicolonDelimitedUriListGrammar.hpp
uhal::HttpResponseType::status
int status
the response status
Definition: HttpResponseGrammar.hpp:58
ThisLocation
#define ThisLocation()
Definition: log_inserters.location.hpp:38
uhal::utilities::OpenFileLocal
void OpenFileLocal(const std::string &aFilenameExpr, const boost::filesystem::path &aParentPath, boost::_bi::bind_t< R, F, L > aBinder)
Given a linux shell expression, open all files which match it and call the callback function on each ...
Definition: files.hpp:292
pugixml.hpp
uhal
Definition: HttpResponseGrammar.hpp:49
uhal::utilities::ParseSemicolonDelimitedUriList
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:46
uhal::Info
InfoLevel Info
Definition: LogLevels.cpp:115
uhal::log
void log(FatalLevel &aFatal, const T0 &aArg0)
Function to add a log entry at Fatal level.
Definition: log.hxx:20
HttpResponseGrammar.hpp
uhal::Error
ErrorLevel Error
Definition: LogLevels.cpp:61
uhal::utilities::HttpGet
bool HttpGet(const std::string &aURL, HttpResponseType &aResponse)
Retrieve a file by HTTP.
Definition: files.hpp:133
uhal::Quote
_Quote< T > Quote(const T &aT)
Definition: log_inserters.quote.hxx:49
URIGrammar.hpp
uhal::utilities::OpenFile
void OpenFile(const std::string &aProtocol, const std::string &aFilenameExpr, const boost::filesystem::path &aParentPath, boost::_bi::bind_t< R, F, L > aBinder)
Given a protocol and either a URL or a linux shell expression, open the file and call the callback fu...
Definition: files.hpp:353
log.hpp
uhal::HttpResponseType::content
std::vector< uint8_t > content
parsed message content
Definition: HttpResponseGrammar.hpp:64
uhal::tests::write
c write(addr, xx[0])
exception.hpp
uhal::utilities::OpenFileHttp
void OpenFileHttp(const std::string &aURL, boost::_bi::bind_t< R, F, L > aBinder)
Given a URL, retrieve the file and call the callback function on each of them.
Definition: files.hpp:328
uhal::HttpResponseType
Struct to store an http response received from a server when parsed by boost spirit.
Definition: HttpResponseGrammar.hpp:52
uhal::HttpResponseType::method
std::string method
the http transport method
Definition: HttpResponseGrammar.hpp:54