40 m_first_image_timeout(3U) ,
41 m_image_repeat_timeout(1U) ,
42 m_gateway(GNet::Address::defaultAddress()) ,
43 m_more_verbose(true) ,
49 unsigned int first_image_timeout ,
G::EpochTime image_repeat_timeout ,
unsigned int refresh ,
52 m_idle_timeout = idle_timeout ;
53 m_first_image_timeout = std::max( 1U , first_image_timeout ) ;
54 m_image_repeat_timeout = image_repeat_timeout ;
57 m_more_verbose = more_verbose ;
61 unsigned int first_image_timeout ,
G::EpochTime image_repeat_timeout ,
unsigned int refresh ,
62 const std::string & type )
64 m_idle_timeout = idle_timeout ;
65 m_first_image_timeout = std::max( 1U , first_image_timeout ) ;
66 m_image_repeat_timeout = image_repeat_timeout ;
71 bool Gv::HttpServerConfig::integral(
const G::Url & url ,
const std::string & key )
79 typedef G::Url::Map Map ;
80 Map params = url.
pmap() ;
81 std::vector<std::string> errors ;
82 for( Map::iterator p = params.begin() ; p != params.end() ; ++p )
84 if( (*p).first !=
"streaming" &&
85 (*p).first !=
"refresh" &&
86 (*p).first !=
"scale" &&
87 (*p).first !=
"monochrome" &&
88 (*p).first !=
"type" &&
89 (*p).first !=
"quick" &&
90 (*p).first !=
"wait" )
92 errors.push_back( (*p).first ) ;
99 if( integral(url,
"streaming") )
102 if( integral(url,
"refresh") )
105 if( integral(url,
"scale") )
108 if( integral(url,
"monochrome") )
113 else if( url.
parameter(
"type",
"") ==
"jpeg" )
115 else if( url.
parameter(
"type",
"") ==
"pnm" )
117 else if( url.
parameter(
"type",
"") ==
"any" )
119 else if( !url.
parameter(
"type",
"").empty() )
120 G_WARNING(
"Gv::HttpServerConfig::init: ignoring unknown image type in url: [" + url.
parameter(
"type",
"") +
"]" ) ;
122 if( integral(url,
"quick") )
125 if( integral(url,
"wait") )
131 return m_idle_timeout ;
136 return m_first_image_timeout ;
141 return m_more_verbose ;
166 return m_image_repeat_timeout ;
176 return m_monochrome ;
188 m_any_channel(false) ,
189 m_with_specials(false)
195 std::string extension = path.
extension() ;
196 if( extension ==
"js" )
return "text/javascript" ;
197 if( extension ==
"css" )
return "text/css" ;
198 if( extension ==
"html" )
return "text/html" ;
199 if( extension ==
"png" )
return "image/png" ;
200 if( extension ==
"jpeg" )
return "image/jpeg" ;
201 if( extension ==
"jpg" )
return "image/jpeg" ;
202 if( extension ==
"pnm" )
return "image/x-portable-anymap" ;
203 if( extension ==
"pgm" )
return "image/x-portable-anymap" ;
204 if( extension ==
"pbm" )
return "image/x-portable-anymap" ;
205 if( extension ==
"ppm" )
return "image/x-portable-anymap" ;
206 return std::string() ;
211 m_any_channel = true ;
212 m_with_specials = true ;
217 return m_any_channel ;
222 if( channel_name ==
"*" )
223 m_any_channel = true ;
225 m_channels.push_back( channel_name ) ;
242 return "invalid absolute path" ;
243 G::Path path = normalise( path_in ) ;
245 if( m_files.find(path.
str()) != m_files.end() )
249 return std::string() ;
254 G::Path path = normalise( path_in ) ;
255 m_file_types[path.
str()] = type ;
260 m_default_resource = name ;
262 if( name.find(
'_') == 0U )
264 if( !m_any_channel && std::find(m_channels.begin(),m_channels.end(),name) == m_channels.end() )
265 return "no such channel: [" + name +
"]" ;
269 if( m_files.find(name) == m_files.end() && !m_any_file )
270 return "no such file: [" + name +
"]" ;
272 return std::string() ;
277 return !m_any_channel && !m_any_file && m_files.empty() && m_channels.empty() ;
280 Gv::HttpServerResources::ResourceType Gv::HttpServerResources::resourceType(
const G::Path & url_path )
const
282 std::string basename = url_path ==
"/" ? m_default_resource : url_path.
basename() ;
285 else if( m_with_specials && basename.find(
"__") == 0U )
287 else if( basename.find(
"_") == 0U )
300 std::string basename = url_path ==
"/" ? m_default_resource : url_path.
basename() ;
301 if( basename.find(
'_') == 0U && basename.find(
"__") != 0U )
302 return basename.substr( 1U ) ;
304 return std::string() ;
309 return resourceType(url_path) == t_channel ;
314 return resourceType(url_path) == t_special ;
319 return resourceType(url_path) == t_file ;
324 return fileResourcePair(url_path).first ;
329 return fileResourcePair(url_path).second ;
332 bool Gv::HttpServerResources::readable(
const G::Path & path )
337 f.open( path.
str().c_str() ) ;
342 G::Path Gv::HttpServerResources::normalise(
const G::Path & path_in )
346 if( !parts.empty() && parts.at(0U) ==
"/" )
347 parts.erase( parts.begin() ) ;
351 G::Path Gv::HttpServerResources::normalise(
const G::Path & path_in ,
const std::string & default_ )
354 if( parts.size() == 1U && parts.at(0U) ==
"/" )
356 if( !parts.empty() && parts.at(0U) ==
"/" )
357 parts.erase( parts.begin() ) ;
363 G_ASSERT( resourceType(url_path_in) == t_file ) ;
364 std::pair<std::string,std::string> result ;
368 G::Path url_path = normalise( url_path_in , m_default_resource ) ;
373 reason =
"no base directory configured" ;
375 else if( url_path ==
G::Path() || url_path ==
"." || url_path.
str().find(
"..") == 0U )
378 reason =
"invalid path" ;
380 else if( m_any_file )
383 std::string file_key = url_path.
str() ;
384 Map::const_iterator file_type_p = m_file_types.find( file_key ) ;
385 std::string type = file_type_p == m_file_types.end() ? guessType(file_key) : (*file_type_p).second ;
386 if( readable(file_path) )
388 result.first = file_path.
str() ;
389 result.second = type ;
393 reason =
"not readable: [" + file_path.
str() +
"]" ;
399 std::string file_key = url_path.
str() ;
400 Map::const_iterator file_type_p = m_file_types.find( file_key ) ;
401 std::string type = file_type_p == m_file_types.end() ? guessType(file_key) : (*file_type_p).second ;
402 Map::const_iterator file_p = m_files.find( file_key ) ;
403 if( file_p == m_files.end() )
406 reason = std::string(
"not in the allowed file list: [") + file_key +
"]" ;
408 else if( readable(file_path) )
410 result.first = file_path.
str() ;
411 result.second = type ;
415 reason =
"not readable: [" + file_path.
str() +
"]" ;
418 if( !reason.empty() )
420 result.first = std::string() ;
421 result.second = reason ;
430 out <<
"directory: path=[" << m_dir <<
"]\n" ;
434 out <<
"file: <any>\n" ;
436 for( Map::const_iterator file_p = m_files.begin() ; file_p != m_files.end() ; ++file_p )
438 out <<
"file: filename=[" << (*file_p).first <<
"]\n" ;
440 for( Map::const_iterator file_type_p = m_file_types.begin() ; file_type_p != m_file_types.end() ; ++file_type_p )
442 out <<
"file-type: filename=[" << (*file_type_p).first <<
"] type=[" << (*file_type_p).second <<
"]\n" ;
444 for( G::StringArray::const_iterator channel_p = m_channels.begin() ; channel_p != m_channels.end() ; ++channel_p )
446 out <<
"channel: name=[" << *channel_p <<
"]\n" ;
450 out <<
"channel: <any>\n" ;
452 out <<
"default: [" << m_default_resource <<
"]\n" ;
459 std::ostringstream ss ;
462 for( G::StringArray::iterator p = lines.begin() ; p != lines.end() ; ++p )
465 G_LOG(
"Gv::HttpServerResources::log: server resource: " << *p ) ;
473 const std::string & channel_name ,
unsigned int reopen_timeout ) :
476 m_reopen_timeout(reopen_timeout) ,
477 m_reopen_timer(*this,&
HttpServerInput::onTimeout,static_cast<GNet::EventHandler&>(*this))
494 void Gv::HttpServerInput::attach()
496 G_ASSERT( m_fd != -1 ) ;
497 G_LOG(
"Gv::HttpServerInput::attach: starting channel [" << name() <<
"] fd=" << m_fd ) ;
501 void Gv::HttpServerInput::detach()
503 G_ASSERT( m_fd != -1 ) ;
504 G_LOG(
"Gv::HttpServerInput::detach: stopping channel [" << name() <<
"] fd=" << m_fd ) ;
515 void Gv::HttpServerInput::onException( std::exception & )
520 void Gv::HttpServerInput::readEvent()
522 if( !handleReadEvent() )
526 G_LOG(
"Gv::HttpServerInput::readEvent: channel failed [" << name() <<
"]" ) ;
527 if( m_reopen_timeout != 0U )
528 m_reopen_timer.startTimer( m_reopen_timeout ) ;
532 void Gv::HttpServerInput::onTimeout()
534 G_DEBUG(
"Gv::HttpServerInput::onTimeout: checking for channel [" << name() <<
"]" ) ;
537 m_reopen_timer.startTimer( m_reopen_timeout ) ;
544 m_converter(converter) ,
545 m_resources(&resources) ,
546 m_reopen_timeout(reopen_timeout)
550 for( G::StringArray::iterator p = list.begin() ; p != list.end() ; ++p )
552 addChannel( *p ,
true ) ;
553 G_LOG(
"Gv::HttpServerSources::refresh: channel _" << (m_list.size()-1U) <<
"=[" << *p <<
"]" ) ;
558 m_converter(converter) ,
559 m_resources(nullptr) ,
562 G_DEBUG(
"Gv::HttpServerSources::addSource: adding non-channel source [" << source.
name() <<
"]" ) ;
563 m_list.push_back( Pair(&source,
nullptr) ) ;
573 typedef std::vector<Pair> List ;
574 for( List::iterator p = m_list.begin() ; p != m_list.end() ; ++p )
580 return m_resources ==
nullptr || m_resources->empty() || m_resources->channelResource(url_path) ;
585 G_ASSERT( valid(url_path) ) ;
589 if( m_resources !=
nullptr && m_resources->anyChannel() )
592 for( G::StringArray::iterator p = channels.begin() ; p != channels.end() ; ++p )
594 Pair pair = findByName( *p ) ;
595 if( pair.first ==
nullptr )
597 if( addChannel( *p ,
false ) )
598 G_LOG(
"Gv::HttpServerSources::refresh: adding channel " << (m_list.size()-1U) <<
"=[" << *p <<
"]" ) ;
605 Pair pair(
nullptr ,
nullptr ) ;
606 if( m_resources ==
nullptr || m_resources->empty() )
608 if( m_list.empty() )
throw std::runtime_error(
"no sources configured" ) ;
610 pair = findByName( source_name ) ;
611 if( pair.first ==
nullptr )
612 pair = m_list.front() ;
616 std::string channel_name = m_resources->channelName( url_path ) ;
620 pair = findByNumber( channel_number ) ;
624 std::string channel_name = m_resources->channelName( url_path ) ;
625 pair = findByName( channel_name ) ;
631 if( pair.second !=
nullptr ) pair.second->start() ;
632 return source_holder.
set( pair.first , pair.second?pair.second->info():std::string() ) ;
635 bool Gv::HttpServerSources::addChannel(
const std::string & channel_name ,
bool do_throw )
640 m_list.push_back( Pair(p,p) ) ;
643 catch( std::exception & )
645 G_DEBUG(
"Gv::HttpServerSources::addChannel: invalid channel [" << channel_name <<
"]" ) ;
646 if( do_throw ) throw ;
651 Gv::HttpServerSources::Pair Gv::HttpServerSources::findByName(
const std::string & source_name )
653 typedef std::vector<Pair> List ;
654 for( List::iterator p = m_list.begin() ; p != m_list.end() ; ++p )
656 ImageInputSource * source = (*p).first ;
657 if( source->name() == source_name )
660 return Pair(
nullptr ,
nullptr ) ;
663 Gv::HttpServerSources::Pair Gv::HttpServerSources::findByNumber(
size_t channel_number )
665 if( channel_number < m_list.size() )
666 return m_list.at( channel_number ) ;
668 return Pair(
nullptr ,
nullptr ) ;
681 if( m_source !=
nullptr )
682 m_source->removeImageInputHandler( m_handler ) ;
687 return m_source ==
nullptr ? std::string() : m_source->name() ;
697 if( m_source !=
nullptr )
698 m_source->resend( m_handler ) ;
708 bool new_source = m_source != source ;
711 if( m_source !=
nullptr )
715 if( m_source !=
nullptr )
std::string str() const
Returns the path string.
unsigned int refresh() const
Returns the value for the http refresh header.
A subsecond-resolution timestamp based on a time_t.
static std::string printable(const std::string &in, char escape= '\\')
Returns a printable represention of the given input string.
void setDirectory(const G::Path &)
Sets a directory for file resources.
std::string channelName(const G::Path &url_path) const
Returns the channel name for the given channel-like url path.
bool specialResource(const G::Path &url_path) const
Returns true if the url path is for a special resource (eg. "/__").
bool set(ImageInputSource *, const std::string &info)
Sets the source pointer. Used by Gv::HttpServerSources.
~HttpServerSource()
Destructor.
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
bool streaming() const
Returns true if streaming using multipart/x-mixed-replace.
The GNet::Address class encapsulates a TCP/UDP transport address.
GNet::Address gateway() const
Returns the gateway network address, or the default address if none.
static std::vector< std::string > list(std::vector< std::string > *others=nullptr)
Returns a list of channel names.
std::vector< std::string > StringArray
A std::vector of std::strings.
std::string type() const
Returns the required content-type.
A class which acquires the process's special privileges on construction and releases them on destruct...
void resend()
Calls resend() on the source.
virtual void dropRead(Descriptor fd)=0
Removes the given event source descriptor from the list of read sources.
std::string extension() const
Returns the path's basename extension, ie.
bool valid(const std::string &url_path)
Returns true if the given url path looks like it is for an input-source rather than a file...
A simple parser for URLs.
void addChannelAny()
Adds the wildcard channel.
Gr::ImageConverter & converter()
Returns the image-converter reference, as passed in to the constructor.
A class that encapsulates a network file descriptor and hides knowledge of its o/s-spefific error val...
std::string parameter(std::string key, std::string default_=std::string()) const
Returns the decode()d value of the named parameter, or a default value.
std::string info() const
Returns information on the channel publisher, or the empty string for non-channel sources...
bool fileResource(const G::Path &url_path) const
Returns true if the url path is for a file (eg. "/index.html").
void init(unsigned int idle_timeout, unsigned int first_image_timeout, G::EpochTime repeat_timeout, unsigned int refresh_header, const GNet::Address &gateway, bool more_verbose)
Initialises the configuration from command-line parameters.
bool quick() const
Returns true if requesting the most-recent data from the channel rather than waiting for the next upd...
bool has(const std::string &key) const
Returns true if the named parameter is present.
unsigned int idleTimeout() const
Returns the connection idle timeout.
std::vector< std::string > channels() const
Returns a list of added channel names.
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
virtual void addRead(Descriptor fd, EventHandler &handler)=0
Adds the given event source descriptor and associated handler to the read list.
HttpServerConfig()
Default constructor for reasonable default values.
std::string fileResourcePath(const G::Path &url_path) const
Returns the file-system path for the given url path, or the empty string if there is no matching reso...
bool empty() const
Returns true if just default-constructed with no "add" or "set" methods called.
std::string fileResourceType(const G::Path &url_path) const
Returns the content-type path for the given url path, or the empty string if there is no matching res...
bool moreVerbose() const
Returns true for more verbosity.
void addFileAny()
Allow any file in the directory.
bool isAbsolute() const
Returns !isRelative().
Map pmap() const
Returns the decode()d parameters as a multimap.
void addFileType(const G::Path &, const std::string &type)
Adds a filename-to-type mapping that overrides the extension mapping.
StringArray split() const
Spits the path into a list of component parts (ignoring "." parts unless the whole path is "...
Used by a ImageInputHandler-derived object to hold a ImageInputSource pointer.
G::EpochTime imageRepeatTimeout() const
Returns the repeat timeout.
Path collapsed() const
Returns the path with "foo/.." and "." parts removed, so far as is possible without changing the mean...
std::string setDefault(const std::string &)
Sets the default resource. Returns a warning string.
bool anyChannel() const
Returns true if addChannelAny() called.
HttpServerResources()
Default constructor.
static std::string guessType(const G::Path &path)
Returns a probable content-type based on the filename, or the empty string.
std::pair< std::string, std::string > fileResourcePair(const G::Path &url_path) const
Returns fileResourcePath() and fileResourceType() as a pair.
static std::string join(const std::string &sep, const StringArray &strings)
Concatenates an array of strings.
std::string name() const
Returns the source name.
~HttpServerSources()
Destructor.
A configuration structure for resources that Gv::HttpServerPeer makes available.
unsigned int firstImageTimeout() const
Returns the first-image timeout.
An image format converter that can convert to and from the raw and jpeg formats (only), with scaling and monochrome options.
bool select(const std::string &url_path, HttpServerSource &)
Looks up the required input source and deposits its pointer into the supplied holder.
static bool isUInt(const std::string &s)
Returns true if the string can be converted into an unsigned integer without throwing an exception...
std::string addFile(const G::Path &)
Adds a file resource.
int scale() const
Returns the required scale factor.
HttpServerSource(ImageInputHandler &)
Constructor.
void addChannel(const std::string &channel_name)
Adds a channel resource.
A Path object represents a file system path.
static Path join(const StringArray &parts)
Builds a path from a set of parts.
static EventLoop & instance()
Returns a reference to an instance of the class, if any.
bool monochrome() const
Returns the monochrome flag.
ImageInputSource * get()
Returns the source pointer.
bool channelResource(const G::Path &url_path) const
Returns true if the url path is for a channel (eg. "/_foo").
void log() const
Emits diagnostic logging for the configured resources.
HttpServerSources(Gr::ImageConverter &, const HttpServerResources &, unsigned int channel_reopen_timeout)
Constructor.
static void splitIntoFields(const std::string &in, StringArray &out, const std::string &seperators, char escape= '\0', bool remove_escapes=true)
Splits the string into fields.