Finished version 0.1 of media server .. Jay !!!
g0dil [Sun, 12 Nov 2006 08:06:21 +0000 (08:06 +0000)]
24 files changed:
.gitignore
SConstruct
Server/Connection.cc [deleted file]
Server/Connection.cci [deleted file]
Server/HTTPConnection.cc [new file with mode: 0644]
Server/HTTPConnection.hh [new file with mode: 0644]
Server/HTTPLogger.cc [new file with mode: 0644]
Server/HTTPLogger.hh [new file with mode: 0644]
Server/HTTPRequest.cc [new file with mode: 0644]
Server/HTTPRequest.cci [new file with mode: 0644]
Server/HTTPRequest.hh [new file with mode: 0644]
Server/HTTPRequest.test.cc [copied from Server/Connection.test.cc with 75% similarity]
Server/MimeTypes.cc [new file with mode: 0644]
Server/MimeTypes.hh [new file with mode: 0644]
Server/SimpleHTTPServer.cc [new file with mode: 0644]
Server/SimpleHTTPServer.cci [new file with mode: 0644]
Server/SimpleHTTPServer.hh [new file with mode: 0644]
Server/SimpleHTTPServer.test.cc [copied from Server/Connection.test.cc with 72% similarity]
Server/StreamConnection.cc [new file with mode: 0644]
Server/StreamConnection.cci [new file with mode: 0644]
Server/StreamConnection.hh [moved from Server/Connection.hh with 64% similarity]
Server/StreamConnection.test.cc [moved from Server/Connection.test.cc with 93% similarity]
main.cc
testclient.cc [new file with mode: 0644]

index 2755c62..2cd2a32 100644 (file)
@@ -1,5 +1,10 @@
 mediaserv
+testclient
 libScheduler.a
 libSocket.a
 libUtils.a
 libServer.a
+TODOS
+BUGS
+FIXMES
+
index b970018..3cf7202 100644 (file)
@@ -21,5 +21,8 @@ SConscript(glob.glob("*/SConscript"))
 SatSCons.StandardTargets(env)
 SatSCons.GlobalTargets(env)
 
-SatSCons.Binary(env, binary='mediaserv', sources=SatSCons.GlobSources(),
+SatSCons.Binary(env, binary='mediaserv', sources=SatSCons.GlobSources(exclude='testclient.cc'),
                LIBS = [ 'Server', 'Scheduler', 'Socket', 'Utils' ])
+
+SatSCons.Binary(env, binary='testclient', sources=['testclient.cc'],
+               LIBS = [ 'Socket', 'Utils' ])
diff --git a/Server/Connection.cc b/Server/Connection.cc
deleted file mode 100644 (file)
index 66f0501..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-// $Id$
-//
-// Copyright (C) 2006 
-
-// Definition of non-inline non-template functions
-
-#include "Connection.hh"
-//#include "Connection.ih"
-
-// Custom includes
-
-//#include "Connection.mpp"
-#define prefix_
-///////////////////////////////cc.p////////////////////////////////////////
-
-prefix_ g0dil::mediaserv::Connection::Connection(int fileFd, unsigned bytesPerSecond,
-                                                 ClientHandle client, unsigned bufferMSecs)
-    : fileFd_(fileFd), bytesPerSecond_(bytesPerSecond), client_(client),
-      bufferMSecs_(bufferMSecs), bytesWritten_(0), start_(satcom::lib::now()),
-      bufferSize_(0), terminate_(false)
-{
-    registerCallback();
-    fillBuffer();
-}
-
-prefix_ void g0dil::mediaserv::Connection::fillBuffer()
-{
-    bufferSize_ = ::read(fileFd_,buffer_,packetSize);
-    if (bufferSize_ < packetSize)
-       terminate_ = true;
-    // TODO: Check error;
-}
-
-prefix_ void g0dil::mediaserv::Connection::callback(ClientHandle client,
-                                                    satcom::lib::Scheduler::EventId)
-{
-    unsigned target (targetBytes());
-    if (target - bytesWritten_ >= bufferSize_) {
-       if (client_.write(buffer_,bufferSize_) < bufferSize_)
-           // TODO: Destroy client handle ..
-           ;
-       bytesWritten_ += bufferSize_;
-       if (terminate_) {
-           unregisterCallback();
-           // TODO: Destroy client handle
-       } else
-           fillBuffer();
-    } else {
-       unregisterCallback();
-       registerTimeout(((bufferSize_-(target-bytesWritten_))*1000)/bytesPerSecond_);
-    }
-}
-
-prefix_ void g0dil::mediaserv::Connection::timeout()
-{
-    registerCallback();
-}
-
-///////////////////////////////cc.e////////////////////////////////////////
-#undef prefix_
-//#include "Connection.mpp"
-
-\f
-// Local Variables:
-// mode: c++
-// End:
diff --git a/Server/Connection.cci b/Server/Connection.cci
deleted file mode 100644 (file)
index c09e778..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-// $Id$
-//
-// Copyright (C) 2006 
-
-// Definition of inline non-template functions
-
-// Custom includes
-#include "Utils/membind.hh"
-
-#define prefix_ inline
-///////////////////////////////cci.p///////////////////////////////////////
-
-prefix_ void g0dil::mediaserv::Connection::registerCallback()
-{
-    satcom::lib::Scheduler::instance().add(client_,satcom::lib::membind(&Connection::callback,this),
-                                          satcom::lib::Scheduler::EV_WRITE);
-}
-
-prefix_ void g0dil::mediaserv::Connection::unregisterCallback()
-{
-    satcom::lib::Scheduler::instance().remove(client_,satcom::lib::Scheduler::EV_WRITE);
-}
-
-prefix_ void g0dil::mediaserv::Connection::registerTimeout(unsigned timeout)
-{
-    satcom::lib::Scheduler::instance().timeout(timeout,satcom::lib::membind(&Connection::timeout,this));
-}
-
-prefix_ unsigned g0dil::mediaserv::Connection::targetBytes()
-{
-    return ((bufferMSecs_ + ((satcom::lib::now()-start_)/1000u))*bytesPerSecond_)/1000u;
-}
-
-///////////////////////////////cci.e///////////////////////////////////////
-#undef prefix_
-
-\f
-// Local Variables:
-// mode: c++
-// End:
diff --git a/Server/HTTPConnection.cc b/Server/HTTPConnection.cc
new file mode 100644 (file)
index 0000000..33a4e1c
--- /dev/null
@@ -0,0 +1,115 @@
+// $Id$
+//
+// Copyright (C) 2006 
+
+// Definition of non-inline non-template functions
+
+//#include "HTTPConnection.hh"
+//#include "HTTPConnection.ih"
+
+// Custom includes
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sstream>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include "Utils/membind.hh"
+
+#include "SimpleHTTPServer.hh"
+#include "StreamConnection.hh"
+#include "MimeTypes.hh"
+
+//#include "HTTPConnection.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+prefix_ g0dil::mediaserv::HTTPConnection::HTTPConnection(ClientHandle client,
+                                                         SimpleHTTPServer & server,
+                                                         HTTPLogger & logger)
+    : client_(client), server_(server), logger_(logger), fileFd_(-1), bandwidth_(0)
+{
+    satcom::lib::ReadHelper<ClientHandle>
+       ::dispatch(client_, MaxRequestSize, satcom::lib::ReadUntil("\r\n\r\n"),
+                  satcom::lib::membind(&HTTPConnection::handleRequest,this));
+}
+
+prefix_ g0dil::mediaserv::HTTPConnection::~HTTPConnection()
+{
+//     if (client_.valid()) 
+//     client_.close();
+    if (fileFd_ != -1) 
+       ::close(fileFd_);
+}
+
+prefix_ void
+g0dil::mediaserv::HTTPConnection::handleRequest(satcom::lib::ReadHelper<ClientHandle>::ptr helper)
+{
+
+    try {
+       helper->throw_error();
+
+       request_.parseRequest(helper->handle(), helper->data());
+
+       boost::algorithm::split_iterator<std::string::const_iterator> 
+           i (request_.url(),boost::algorithm::first_finder("."));
+       boost::algorithm::split_iterator<std::string::const_iterator> i_end;
+       if (i == i_end || ++i == i_end) throw InvalidHTTPRequestException();
+       bandwidth_ = boost::lexical_cast<unsigned>(std::string(i->begin(),i->end()));
+       if (++i == i_end) throw InvalidHTTPRequestException();
+       std::string const & mimeType (MimeTypes::lookup(std::string(i->begin(), i->end())));
+       if (++i != i_end) throw InvalidHTTPRequestException();
+
+       fileFd_ = ::open(request_.url().c_str(),O_RDONLY);
+       if (fileFd_ < 0) throw satcom::lib::SystemException(errno);
+       struct ::stat s;
+       if (::fstat(fileFd_,&s) < 0) throw satcom::lib::SystemException(errno);
+       
+       std::ostringstream response;
+       response << request_.version() << (request_.version().empty() ? "" : " ") << "200 OK\r\n";
+       response << "Content-Type: " << mimeType << "\r\n";
+       response << "Content-Length: " << s.st_size << "\r\n\r\n";
+       
+       satcom::lib::WriteHelper<ClientHandle>
+           ::dispatch(client_, response.str(), 
+                      satcom::lib::membind(&HTTPConnection::startStream,this));
+
+    }
+    catch (std::exception const & ex) {
+       logger_.invalidRequest(ex.what());
+       server_.done(ptr(this));
+    }
+}
+
+prefix_ void
+g0dil::mediaserv::HTTPConnection::startStream(satcom::lib::WriteHelper<ClientHandle>::ptr helper)
+{
+    try {
+       helper->throw_error();
+       
+        connection_.reset(new StreamConnection(fileFd_, bandwidth_, client_, StreamBufferMSecs, 
+                                              boost::bind(&HTTPConnection::done,this)));
+       connection_->start();
+    }
+    catch (std::exception const & ex) {
+       logger_.failedRequest(request_, ex.what());
+       server_.done(ptr(this));
+    }
+}
+
+prefix_ void g0dil::mediaserv::HTTPConnection::done()
+{
+    logger_.request(request_, connection_->bytesSent());
+    server_.done(ptr(this));
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "HTTPConnection.mpp"
+
+\f
+// Local Variables:
+// mode: c++
+// End:
diff --git a/Server/HTTPConnection.hh b/Server/HTTPConnection.hh
new file mode 100644 (file)
index 0000000..e8b097e
--- /dev/null
@@ -0,0 +1,84 @@
+// $Id$
+//
+// Copyright (C) 2006 
+
+#ifndef HH_HTTPConnection_
+#define HH_HTTPConnection_ 1
+
+// Custom includes
+#include <boost/utility.hpp>
+#include <boost/intrusive_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+#include "Socket/ClientSocketHandle.hh"
+#include "Utils/intrusive_refcount.hh"
+#include "Scheduler/ReadHelper.hh"
+#include "Scheduler/WriteHelper.hh"
+#include "HTTPLogger.hh"
+#include "HTTPRequest.hh"
+
+//#include "HTTPConnection.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace g0dil {
+namespace mediaserv {
+
+    class SimpleHTTPServer;
+    class StreamConnection;
+
+    class HTTPConnection
+       : public satcom::lib::intrusive_refcount
+    {
+    public:
+        ///////////////////////////////////////////////////////////////////////////
+        // Types
+
+       typedef boost::intrusive_ptr<HTTPConnection> ptr;
+       typedef satcom::lib::ClientSocketHandle<
+           satcom::lib::MakeSocketPolicy<satcom::lib::ConnectedCommunicationPolicy,
+                                         satcom::lib::StreamFramingPolicy,
+                                         satcom::lib::ReadablePolicy,
+                                         satcom::lib::WriteablePolicy>::policy> ClientHandle;
+
+       static const unsigned MaxRequestSize = 16384;
+       static const unsigned StreamBufferMSecs = 4000;
+
+        ///////////////////////////////////////////////////////////////////////////
+        ///\name Structors and default members
+        ///@{
+
+        HTTPConnection(ClientHandle client, SimpleHTTPServer & server, HTTPLogger & logger);
+       ~HTTPConnection();
+
+        ///@}
+        ///////////////////////////////////////////////////////////////////////////
+
+       void done();
+
+    protected:
+
+    private:
+       void handleRequest(satcom::lib::ReadHelper<ClientHandle>::ptr);
+       void startStream(satcom::lib::WriteHelper<ClientHandle>::ptr);
+       
+       ClientHandle client_;
+       SimpleHTTPServer & server_;
+       HTTPLogger & logger_;
+       HTTPRequest request_;
+       boost::scoped_ptr<StreamConnection> connection_;
+       int fileFd_;
+       unsigned bandwidth_;
+    };
+
+}}
+
+///////////////////////////////hh.e////////////////////////////////////////
+//#include "HTTPConnection.cci"
+//#include "HTTPConnection.ct"
+//#include "HTTPConnection.cti"
+//#include "HTTPConnection.mpp"
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// End:
diff --git a/Server/HTTPLogger.cc b/Server/HTTPLogger.cc
new file mode 100644 (file)
index 0000000..f2f880e
--- /dev/null
@@ -0,0 +1,94 @@
+// $Id$
+//
+// Copyright (C) 2006 
+
+// TODO: error logging
+
+// Definition of non-inline non-template functions
+
+#include "HTTPLogger.hh"
+//#include "HTTPLogger.ih"
+
+// Custom includes
+#include "Socket/INetAddressing.hh"
+#include "Socket/CommunicationPolicy.hh"
+
+//#include "HTTPLogger.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+namespace {
+
+    std::string currentDateTimeString()
+    {
+       time_t t;
+       time(&t);
+       char buffer[32];
+       strftime(buffer,32,"%d/%b/%Y:%H:%M:%S %z",localtime(&t));
+       buffer[31] = 0;
+       return std::string(buffer);
+    }
+
+}
+
+prefix_ g0dil::mediaserv::HTTPLogger::HTTPLogger(std::string logfile)
+    : logFile_(logfile.c_str(),std::ofstream::app)
+{
+    if (!logFile_)
+       throw InvalidLogFileException();
+    logFile_ << "(start)" << " - - [" << currentDateTimeString() << "] mediaserv started" 
+            << std::endl;
+}
+
+prefix_ g0dil::mediaserv::HTTPLogger::~HTTPLogger()
+{
+    logFile_ << "(shutdown)" << " - - [" << currentDateTimeString() << "] mediaserv terminated" 
+            << std::endl;
+}
+
+prefix_ void g0dil::mediaserv::HTTPLogger::invalidRequest(std::string message)
+{
+    logFile_ << "(unknown client)" << " - - [" << currentDateTimeString() << "] " 
+            << "invalid request: " << message << std::endl;
+}
+
+prefix_ void g0dil::mediaserv::HTTPLogger::invalidRequest(HTTPRequest const & request,
+                                                          std::string message)
+{
+    logFile_ << request.host() << " - - [" << currentDateTimeString() << "] ";
+    if (!request.method().empty())
+       logFile_ << "\"" << request.method() << " " << request.url() << " " << request.version() << "\" ";
+    logFile_ << "invalid request: " << message << std::endl;
+}
+
+prefix_ void g0dil::mediaserv::HTTPLogger::failedRequest(std::string message)
+{
+    logFile_ << "(unknown client)" << " - - [" << currentDateTimeString() << "] " 
+            << "failed request: " << message << std::endl;
+}
+
+prefix_ void g0dil::mediaserv::HTTPLogger::failedRequest(HTTPRequest const & request,
+                                                         std::string message)
+{
+    logFile_ << request.host() << " - - [" << currentDateTimeString() << "] ";
+    if (!request.method().empty())
+       logFile_ << "\"" << request.method() << " " << request.url() << " " << request.version() << "\" ";
+    logFile_ << "invalid request: " << message << std::endl;
+}
+
+prefix_ void g0dil::mediaserv::HTTPLogger::request(HTTPRequest const & request, 
+                                                   unsigned bytes)
+{
+    logFile_ << request.host() << " - - [" << currentDateTimeString() << "] "
+            << "\"" << request.method() << " " << request.url() << " " << request.version()
+            << "\" 200 " << bytes << " \"\" \"\"" << std::endl;
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "HTTPLogger.mpp"
+
+\f
+// Local Variables:
+// mode: c++
+// End:
diff --git a/Server/HTTPLogger.hh b/Server/HTTPLogger.hh
new file mode 100644 (file)
index 0000000..8d3b6c0
--- /dev/null
@@ -0,0 +1,59 @@
+// $Id$
+//
+// Copyright (C) 2006 
+
+#ifndef HH_HTTPLogger_
+#define HH_HTTPLogger_ 1
+
+// Custom includes
+#include <fstream>
+#include "Socket/FileHandle.hh"
+#include "HTTPRequest.hh"
+
+//#include "HTTPLogger.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace g0dil {
+namespace mediaserv {
+
+    class HTTPLogger
+    {
+    public:
+        ///////////////////////////////////////////////////////////////////////////
+        ///\name Structors and default members
+        ///@{
+
+        HTTPLogger(std::string logfile);
+        ~HTTPLogger();
+
+        ///@}
+        ///////////////////////////////////////////////////////////////////////////
+       
+       void invalidRequest(std::string message);
+       void invalidRequest(HTTPRequest const & request, std::string message);
+       void failedRequest(std::string message);
+       void failedRequest(HTTPRequest const & request, std::string message);
+       void request(HTTPRequest const & request, unsigned bytes);
+
+    protected:
+
+    private:
+       std::ofstream logFile_;
+    };
+
+    struct InvalidLogFileException : public std::exception
+    { char const * what() const throw() { return "invalid log file"; } };
+
+}}
+
+///////////////////////////////hh.e////////////////////////////////////////
+//#include "HTTPLogger.cci"
+//#include "HTTPLogger.ct"
+//#include "HTTPLogger.cti"
+//#include "HTTPLogger.mpp"
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// End:
diff --git a/Server/HTTPRequest.cc b/Server/HTTPRequest.cc
new file mode 100644 (file)
index 0000000..4fb444e
--- /dev/null
@@ -0,0 +1,105 @@
+// $Id$
+//
+// Copyright (C) 2006 
+
+// Definition of non-inline non-template functions
+
+#include "HTTPRequest.hh"
+//#include "HTTPRequest.ih"
+
+// Custom includes
+#include <boost/algorithm/string.hpp>
+#include <boost/range.hpp>
+#include "Socket/ClientSocketHandle.hh"
+#include "Socket/INetAddressing.hh"
+#include "Socket/CommunicationPolicy.hh"
+
+//#include "HTTPRequest.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+prefix_ g0dil::mediaserv::HTTPRequest::HTTPRequest()
+{}
+
+prefix_ g0dil::mediaserv::HTTPRequest::HTTPRequest(satcom::lib::FileHandle handle,
+                                                   std::string const & request)
+{
+    parseRequest(handle,request);
+}
+
+prefix_ void g0dil::mediaserv::HTTPRequest::parseRequest(satcom::lib::FileHandle handle,
+                                                         std::string const & request)
+{
+    typedef satcom::lib::ClientSocketHandle< satcom::lib::MakeSocketPolicy< 
+       satcom::lib::INet4AddressingPolicy,
+       satcom::lib::ConnectedCommunicationPolicy >::policy> IPHandle;
+    try {
+       host_ = satcom::lib::dynamic_socket_cast<IPHandle>(handle).peer().host();
+    }
+    catch (std::bad_cast const *) {
+       host_ = "(unidentified address)";
+    }
+    boost::algorithm::split_iterator<std::string::const_iterator> 
+       line (request, boost::algorithm::first_finder("\n"));
+    boost::algorithm::split_iterator<std::string::const_iterator> line_end;
+    if (line == line_end) return;
+    parseRequestURL(*line);
+    for (++line; line != line_end; ++line)
+       parseRequestHeader(*line);
+}
+
+prefix_ void g0dil::mediaserv::HTTPRequest::
+parseRequestURL(boost::iterator_range<std::string::const_iterator> line)
+{
+    boost::algorithm::split_iterator<std::string::const_iterator> 
+       token (line, boost::algorithm::first_finder(" "));
+    boost::algorithm::split_iterator<std::string::const_iterator> token_end;
+
+    if (token == token_end)
+       throw InvalidHTTPRequestException();
+    method_ = std::string(token->begin(),token->end());
+    if (method_ != "GET")
+       throw InvalidHTTPRequestException();
+    if (++token == token_end)
+       throw InvalidHTTPRequestException();
+    url_ = std::string(token->begin(),token->end());
+    if (++token == token_end)
+       return;
+    version_ = std::string(token->begin(),token->end());
+    boost::trim(version_);
+    if (! boost::starts_with(version_,"HTTP/"))
+       throw InvalidHTTPRequestException();
+    if (++token != token_end)
+       throw InvalidHTTPRequestException();
+}
+
+prefix_ void g0dil::mediaserv::HTTPRequest::
+parseRequestHeader(boost::iterator_range<std::string::const_iterator> line)
+{
+    boost::iterator_range<std::string::const_iterator> i (boost::find_first(line,":"));
+    if (i.empty())
+       throw InvalidHTTPRequestException();
+    std::string key (line.begin(),i.begin());
+    std::string value (i.end(),line.end());
+    boost::to_lower(key);
+    boost::trim(value);
+    headers_.insert(std::make_pair(key,value));
+}
+
+prefix_ std::string const & g0dil::mediaserv::HTTPRequest::operator[](std::string const & key)
+    const
+{
+    static std::string empty;
+    Headers::const_iterator i (headers_.find(key));
+    if (i == headers_.end()) return empty;
+    return i->second;
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "HTTPRequest.mpp"
+
+\f
+// Local Variables:
+// mode: c++
+// End:
diff --git a/Server/HTTPRequest.cci b/Server/HTTPRequest.cci
new file mode 100644 (file)
index 0000000..ff340d2
--- /dev/null
@@ -0,0 +1,42 @@
+// $Id$
+//
+// Copyright (C) 2006 
+
+// Definition of inline non-template functions
+
+// Custom includes
+
+#define prefix_ inline
+///////////////////////////////cci.p///////////////////////////////////////
+
+prefix_ std::string const & g0dil::mediaserv::HTTPRequest::host()
+    const
+{
+    return host_;
+}
+
+prefix_ std::string const & g0dil::mediaserv::HTTPRequest::method()
+    const
+{
+    return method_;
+}
+
+prefix_ std::string const & g0dil::mediaserv::HTTPRequest::url()
+    const
+{
+    return url_;
+}
+
+prefix_ std::string const & g0dil::mediaserv::HTTPRequest::version()
+    const
+{
+    return version_;
+}
+
+///////////////////////////////cci.e///////////////////////////////////////
+#undef prefix_
+
+\f
+// Local Variables:
+// mode: c++
+// End:
diff --git a/Server/HTTPRequest.hh b/Server/HTTPRequest.hh
new file mode 100644 (file)
index 0000000..2a4e367
--- /dev/null
@@ -0,0 +1,84 @@
+// $Id$
+//
+// Copyright (C) 2006 
+
+#ifndef HH_HTTPRequest_
+#define HH_HTTPRequest_ 1
+
+// Custom includes
+#include <exception>
+#include <string>
+#include <map>
+#include <boost/range/iterator_range.hpp>
+#include "Socket/FileHandle.hh"
+
+//#include "HTTPRequest.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace g0dil {
+namespace mediaserv {
+    
+    class HTTPRequest
+    {
+    public:
+        ///////////////////////////////////////////////////////////////////////////
+        // Types
+
+        ///////////////////////////////////////////////////////////////////////////
+        ///\name Structors and default members
+        ///@{
+
+       HTTPRequest();
+        HTTPRequest(satcom::lib::FileHandle handle, std::string const & request);
+
+        ///@}
+        ///////////////////////////////////////////////////////////////////////////
+        ///\name Accessors
+        ///@{
+       
+       std::string const & host() const;
+       std::string const & method() const;
+       std::string const & url() const;
+       std::string const & version() const;
+       std::string const & operator[](std::string const & key) const;
+
+        ///@}
+        ///////////////////////////////////////////////////////////////////////////
+        ///\name Mutators
+        ///@{
+
+       void parseRequest(satcom::lib::FileHandle handle, std::string const & request);
+
+        ///@}
+
+    protected:
+
+    private:
+       void parseRequestURL(boost::iterator_range<std::string::const_iterator> line);
+       void parseRequestHeader(boost::iterator_range<std::string::const_iterator> line);
+
+       typedef std::map<std::string,std::string> Headers;
+
+       std::string host_;
+       std::string method_;
+       std::string url_;
+       std::string version_;
+       Headers headers_;
+    };
+
+    struct InvalidHTTPRequestException : public std::exception
+    { char const * what() const throw() { return "invalid http request"; } };
+    
+}}
+
+///////////////////////////////hh.e////////////////////////////////////////
+#include "HTTPRequest.cci"
+//#include "HTTPRequest.ct"
+//#include "HTTPRequest.cti"
+//#include "HTTPRequest.mpp"
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// End:
similarity index 75%
copy from Server/Connection.test.cc
copy to Server/HTTPRequest.test.cc
index 76fcb4f..c75d968 100644 (file)
@@ -4,11 +4,11 @@
 
 // Unit tests
 
-//#include "Connection.test.hh"
-//#include "Connection.test.ih"
+//#include "HTTPRequest.test.hh"
+//#include "HTTPRequest.test.ih"
 
 // Custom includes
-#include "Connection.hh"
+#include "HTTPRequest.hh"
 
 #include <boost/test/auto_unit_test.hpp>
 #include <boost/test/test_tools.hpp>
@@ -16,7 +16,7 @@
 #define prefix_
 ///////////////////////////////cc.p////////////////////////////////////////
 
-BOOST_AUTO_UNIT_TEST(connection)
+BOOST_AUTO_UNIT_TEST(httpRequest)
 {}
 
 ///////////////////////////////cc.e////////////////////////////////////////
diff --git a/Server/MimeTypes.cc b/Server/MimeTypes.cc
new file mode 100644 (file)
index 0000000..79f0385
--- /dev/null
@@ -0,0 +1,44 @@
+// $Id$
+//
+// Copyright (C) 2006 
+
+// Definition of non-inline non-template functions
+
+#include "MimeTypes.hh"
+//#include "MimeTypes.ih"
+
+// Custom includes
+
+//#include "MimeTypes.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+prefix_ std::string const & g0dil::mediaserv::MimeTypes::lookup(std::string extension)
+{
+    static std::string defaultType ("application/octet-stream");
+    Map::iterator i (instance().mimeTypes_.find(extension));
+    if (i == instance().mimeTypes_.end())
+       return defaultType;
+    else
+       return i->second;
+}
+
+prefix_ void g0dil::mediaserv::MimeTypes::add(std::string extension, std::string mimetype)
+{
+    instance().mimeTypes_[extension] = mimetype;
+}
+
+prefix_ g0dil::mediaserv::MimeTypes & g0dil::mediaserv::MimeTypes::instance()
+{
+    static MimeTypes instance;
+    return instance;
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "MimeTypes.mpp"
+
+\f
+// Local Variables:
+// mode: c++
+// End:
diff --git a/Server/MimeTypes.hh b/Server/MimeTypes.hh
new file mode 100644 (file)
index 0000000..c94e836
--- /dev/null
@@ -0,0 +1,64 @@
+// $Id$
+//
+// Copyright (C) 2006 
+
+#ifndef HH_MimeTypes_
+#define HH_MimeTypes_ 1
+
+// Custom includes
+#include <string>
+#include <map>
+
+//#include "MimeTypes.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace g0dil {
+namespace mediaserv {
+    
+    class MimeTypes
+    {
+    public:
+        ///////////////////////////////////////////////////////////////////////////
+        // Types
+
+        ///////////////////////////////////////////////////////////////////////////
+        ///\name Structors and default members
+        ///@{
+
+        // default default constructor
+        // default copy constructor
+        // default copy assignment
+        // default destructor
+
+        // no conversion constructors
+
+        ///@}
+        ///////////////////////////////////////////////////////////////////////////
+
+       static std::string const & lookup(std::string extension);
+       static void add(std::string extension, std::string mimetype);
+
+    protected:
+
+    private:
+       typedef std::map<std::string,std::string> Map;
+
+       static MimeTypes & instance();
+       
+       Map mimeTypes_;
+    };
+       
+
+}}
+
+///////////////////////////////hh.e////////////////////////////////////////
+//#include "MimeTypes.cci"
+//#include "MimeTypes.ct"
+//#include "MimeTypes.cti"
+//#include "MimeTypes.mpp"
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// End:
diff --git a/Server/SimpleHTTPServer.cc b/Server/SimpleHTTPServer.cc
new file mode 100644 (file)
index 0000000..c749c59
--- /dev/null
@@ -0,0 +1,72 @@
+// $Id$
+//
+// Copyright (C) 2006 
+
+// Definition of non-inline non-template functions
+
+// TODO: success logging
+// TODO: limit max number of clients
+
+#include "SimpleHTTPServer.hh"
+//#include "SimpleHTTPServer.ih"
+
+// Custom includes
+#include "Utils/membind.hh"
+
+//#include "SimpleHTTPServer.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+g0dil::mediaserv::SimpleHTTPServer * g0dil::mediaserv::SimpleHTTPServer::instance_ = 0;
+
+prefix_ g0dil::mediaserv::SimpleHTTPServer::SimpleHTTPServer(ServerHandle socket,
+                                                             HTTPLogger & logger)
+    : socket_(socket), logger_(logger)
+{
+    instance_ = this;
+    satcom::lib::Scheduler::instance()
+       .add(socket_, 
+            satcom::lib::membind(&SimpleHTTPServer::newConnection,this),
+            satcom::lib::Scheduler::EV_READ);
+}
+
+prefix_ g0dil::mediaserv::SimpleHTTPServer::~SimpleHTTPServer()
+{
+    instance_ = 0;
+}
+
+prefix_ void
+g0dil::mediaserv::SimpleHTTPServer::newConnection(ServerHandle handle,
+                                                  satcom::lib::Scheduler::EventId event)
+{
+    if (event != satcom::lib::Scheduler::EV_READ) {
+       logger_.failedRequest("unexpected event on server socket .. shuting down ..");
+       satcom::lib::Scheduler::instance().terminate();
+       return;
+    }
+    try {
+       ServerHandle::ClientSocketHandle client (socket_.accept());
+       if (! client.valid()) {
+           logger_.failedRequest("accept() would block !?");
+           return;
+       }
+       connections_.insert(HTTPConnection::ptr(new HTTPConnection(client,*this,logger_)));
+    }
+    catch (std::exception & ex) {
+       logger_.failedRequest(ex.what());
+    }
+}
+
+prefix_ void g0dil::mediaserv::SimpleHTTPServer::done(HTTPConnection::ptr connection)
+{
+    connections_.erase(connection);
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "SimpleHTTPServer.mpp"
+
+\f
+// Local Variables:
+// mode: c++
+// End:
diff --git a/Server/SimpleHTTPServer.cci b/Server/SimpleHTTPServer.cci
new file mode 100644 (file)
index 0000000..df93bb4
--- /dev/null
@@ -0,0 +1,25 @@
+// $Id$
+//
+// Copyright (C) 2006 
+
+// Definition of inline non-template functions
+
+// Custom includes
+#include <boost/assert.hpp>
+
+#define prefix_ inline
+///////////////////////////////cci.p///////////////////////////////////////
+
+prefix_ g0dil::mediaserv::SimpleHTTPServer & g0dil::mediaserv::SimpleHTTPServer::instance()
+{
+    BOOST_ASSERT(instance_);
+    return *instance_;
+}
+
+///////////////////////////////cci.e///////////////////////////////////////
+#undef prefix_
+
+\f
+// Local Variables:
+// mode: c++
+// End:
diff --git a/Server/SimpleHTTPServer.hh b/Server/SimpleHTTPServer.hh
new file mode 100644 (file)
index 0000000..56c79c2
--- /dev/null
@@ -0,0 +1,75 @@
+// $Id$
+//
+// Copyright (C) 2006 
+
+#ifndef HH_SimpleHTTPServer_
+#define HH_SimpleHTTPServer_ 1
+
+// Custom includes
+#include <set>
+#include <boost/utility.hpp>
+#include "Socket/ServerSocketHandle.hh"
+#include "Socket/CommunicationPolicy.hh"
+#include "Socket/FramingPolicy.hh"
+#include "Socket/ReadWritePolicy.hh"
+#include "Scheduler/Scheduler.hh"
+
+#include "HTTPLogger.hh"
+#include "HTTPConnection.hh"
+
+//#include "SimpleHTTPServer.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace g0dil {
+namespace mediaserv {
+
+    class SimpleHTTPServer
+       : boost::noncopyable
+    {
+    public:
+        ///////////////////////////////////////////////////////////////////////////
+        // Types
+
+       typedef HTTPConnection::ClientHandle::ServerSocketHandle ServerHandle;
+
+        ///////////////////////////////////////////////////////////////////////////
+        ///\name Structors and default members
+        ///@{
+
+        SimpleHTTPServer(ServerHandle socket, HTTPLogger & logger);
+        ~SimpleHTTPServer();
+
+       static SimpleHTTPServer & instance();
+
+        ///@}
+        ///////////////////////////////////////////////////////////////////////////
+
+       void done(HTTPConnection::ptr connection);
+
+    protected:
+
+    private:
+       void newConnection(ServerHandle handle, satcom::lib::Scheduler::EventId event);
+
+       typedef std::set<HTTPConnection::ptr> Connections;
+
+       ServerHandle socket_;
+       HTTPLogger & logger_;
+       Connections connections_;
+
+       static SimpleHTTPServer * instance_;
+    };
+
+}}
+
+///////////////////////////////hh.e////////////////////////////////////////
+#include "SimpleHTTPServer.cci"
+//#include "SimpleHTTPServer.ct"
+//#include "SimpleHTTPServer.cti"
+//#include "SimpleHTTPServer.mpp"
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// End:
similarity index 72%
copy from Server/Connection.test.cc
copy to Server/SimpleHTTPServer.test.cc
index 76fcb4f..4954975 100644 (file)
@@ -4,11 +4,11 @@
 
 // Unit tests
 
-//#include "Connection.test.hh"
-//#include "Connection.test.ih"
+//#include "SimpleHTTPServer.test.hh"
+//#include "SimpleHTTPServer.test.ih"
 
 // Custom includes
-#include "Connection.hh"
+#include "SimpleHTTPServer.hh"
 
 #include <boost/test/auto_unit_test.hpp>
 #include <boost/test/test_tools.hpp>
@@ -16,7 +16,7 @@
 #define prefix_
 ///////////////////////////////cc.p////////////////////////////////////////
 
-BOOST_AUTO_UNIT_TEST(connection)
+BOOST_AUTO_UNIT_TEST(simpleHttpServer)
 {}
 
 ///////////////////////////////cc.e////////////////////////////////////////
diff --git a/Server/StreamConnection.cc b/Server/StreamConnection.cc
new file mode 100644 (file)
index 0000000..130abf8
--- /dev/null
@@ -0,0 +1,88 @@
+// $Id$
+//
+// Copyright (C) 2006 
+
+// Definition of non-inline non-template functions
+
+#include "StreamConnection.hh"
+//#include "StreamConnection.ih"
+
+// Custom includes
+#include <unistd.h>
+#include "SimpleHTTPServer.hh"
+
+//#include "StreamConnection.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+prefix_ g0dil::mediaserv::StreamConnection::StreamConnection(int fileFd,
+                                                             unsigned bytesPerSecond,
+                                                             ClientHandle client,
+                                                             unsigned bufferMSecs,
+                                                             Callback callback)
+    : fileFd_(fileFd), bytesPerSecond_(bytesPerSecond), client_(client),
+      bufferMSecs_(bufferMSecs), callback_(callback), bytesWritten_(0),
+      start_(satcom::lib::now()), bufferSize_(0), terminate_(false) 
+{
+    fillBuffer();
+}
+
+
+prefix_ void g0dil::mediaserv::StreamConnection::start()
+{
+    registerCallback();
+}
+
+prefix_ g0dil::mediaserv::StreamConnection::~StreamConnection()
+{
+    unregisterCallback();
+    // FIXME: unregisterTimeout
+}
+
+prefix_ void g0dil::mediaserv::StreamConnection::fillBuffer()
+{
+    bufferSize_ = ::read(fileFd_,buffer_,packetSize);
+    if (bufferSize_ < 0)
+       callback_();
+    else if (bufferSize_ < packetSize)
+       terminate_ = true;
+}
+
+prefix_ void g0dil::mediaserv::StreamConnection::callback(ClientHandle client,
+                                                    satcom::lib::Scheduler::EventId event)
+{
+    if (event != satcom::lib::Scheduler::EV_WRITE) {
+       callback_();
+       return;
+    }
+    unsigned target (targetBytes());
+    if (target - bytesWritten_ >= bufferSize_) {
+       if (client_.write(buffer_,bufferSize_) < bufferSize_) {
+           callback_();
+           return;
+       }
+       bytesWritten_ += bufferSize_;
+       if (terminate_) {
+           callback_();
+           return;
+       } else
+           fillBuffer();
+    } else {
+       unregisterCallback();
+       registerTimeout(((bufferSize_-(target-bytesWritten_))*1000)/bytesPerSecond_);
+    }
+}
+
+prefix_ void g0dil::mediaserv::StreamConnection::timeout()
+{
+    registerCallback();
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "StreamConnection.mpp"
+
+\f
+// Local Variables:
+// mode: c++
+// End:
diff --git a/Server/StreamConnection.cci b/Server/StreamConnection.cci
new file mode 100644 (file)
index 0000000..8035dd4
--- /dev/null
@@ -0,0 +1,47 @@
+// $Id$
+//
+// Copyright (C) 2006 
+
+// Definition of inline non-template functions
+
+// Custom includes
+#include "Utils/membind.hh"
+
+#define prefix_ inline
+///////////////////////////////cci.p///////////////////////////////////////
+
+prefix_ unsigned g0dil::mediaserv::StreamConnection::bytesSent()
+{
+    return bytesWritten_;
+}
+
+prefix_ void g0dil::mediaserv::StreamConnection::registerCallback()
+{
+    satcom::lib::Scheduler::instance()
+       .add(client_,satcom::lib::membind(&StreamConnection::callback,this),
+            satcom::lib::Scheduler::EV_WRITE);
+}
+
+prefix_ void g0dil::mediaserv::StreamConnection::unregisterCallback()
+{
+    satcom::lib::Scheduler::instance().remove(client_,satcom::lib::Scheduler::EV_WRITE);
+}
+
+prefix_ void g0dil::mediaserv::StreamConnection::registerTimeout(unsigned timeout)
+{
+    satcom::lib::Scheduler::instance()
+       .timeout(timeout,satcom::lib::membind(&StreamConnection::timeout,this));
+}
+
+prefix_ unsigned g0dil::mediaserv::StreamConnection::targetBytes()
+{
+    return ((bufferMSecs_ + ((satcom::lib::now()-start_)/1000u))*bytesPerSecond_)/1000u;
+}
+
+///////////////////////////////cci.e///////////////////////////////////////
+#undef prefix_
+
+\f
+// Local Variables:
+// mode: c++
+// End:
similarity index 64%
rename from Server/Connection.hh
rename to Server/StreamConnection.hh
index 0a40fa3..ba71fe6 100644 (file)
@@ -2,26 +2,28 @@
 //
 // Copyright (C) 2006 
 
-#ifndef HH_Connection_
-#define HH_Connection_ 1
+#ifndef HH_StreamConnection_
+#define HH_StreamConnection_ 1
 
 // Custom includes
 #include <boost/utility.hpp>
+#include <boost/intrusive_ptr.hpp>
+
 #include "Utils/MicroTime.hh"
+#include "Utils/intrusive_refcount.hh"
 #include "Scheduler/Scheduler.hh"
 #include "Socket/ClientSocketHandle.hh"
 #include "Socket/CommunicationPolicy.hh"
 #include "Socket/FramingPolicy.hh"
-#include "Socket/BufferingPolicy.hh"
 #include "Socket/ReadWritePolicy.hh"
 
-//#include "Connection.mpp"
+//#include "StreamConnection.mpp"
 ///////////////////////////////hh.p////////////////////////////////////////
 
 namespace g0dil {
 namespace mediaserv {
 
-    class Connection : boost::noncopyable
+    class StreamConnection : public satcom::lib::intrusive_refcount
     {
     public:
         ///////////////////////////////////////////////////////////////////////////
@@ -30,8 +32,10 @@ namespace mediaserv {
        typedef satcom::lib::ClientSocketHandle<
            satcom::lib::MakeSocketPolicy<satcom::lib::ConnectedCommunicationPolicy,
                                          satcom::lib::StreamFramingPolicy,
-                                         satcom::lib::WriteablePolicy,
-                                         satcom::lib::SocketBufferingPolicy>::policy> ClientHandle;
+                                         satcom::lib::WriteablePolicy>::policy> ClientHandle;
+
+       typedef boost::intrusive_ptr<StreamConnection> ptr;
+       typedef boost::function<void ()> Callback;
 
        static const unsigned packetSize = 1400;
 
@@ -39,10 +43,15 @@ namespace mediaserv {
         ///\name Structors and default members
         ///@{
 
-        Connection(int fileFd, unsigned bytesPerSecond, ClientHandle client,
-                  unsigned bufferMSecs);
+        StreamConnection(int fileFd, unsigned bytesPerSecond, ClientHandle client,
+                        unsigned bufferMSecs, Callback callback);
+        ~StreamConnection();
 
         ///@}
+        ///////////////////////////////////////////////////////////////////////////
+
+       void start();
+       unsigned bytesSent();
 
     protected:
 
@@ -61,6 +70,7 @@ namespace mediaserv {
        unsigned bytesPerSecond_;
        ClientHandle client_;
        unsigned bufferMSecs_;
+       Callback callback_;
 
        unsigned bytesWritten_;
        satcom::lib::MicroTime start_;
@@ -72,10 +82,10 @@ namespace mediaserv {
 }}
 
 ///////////////////////////////hh.e////////////////////////////////////////
-#include "Connection.cci"
-//#include "Connection.ct"
-//#include "Connection.cti"
-//#include "Connection.mpp"
+#include "StreamConnection.cci"
+//#include "StreamConnection.ct"
+//#include "StreamConnection.cti"
+//#include "StreamConnection.mpp"
 #endif
 
 \f
similarity index 93%
rename from Server/Connection.test.cc
rename to Server/StreamConnection.test.cc
index 76fcb4f..15d85d7 100644 (file)
@@ -8,7 +8,7 @@
 //#include "Connection.test.ih"
 
 // Custom includes
-#include "Connection.hh"
+#include "StreamConnection.hh"
 
 #include <boost/test/auto_unit_test.hpp>
 #include <boost/test/test_tools.hpp>
diff --git a/main.cc b/main.cc
index 9922bce..5c057c7 100644 (file)
--- a/main.cc
+++ b/main.cc
@@ -2,19 +2,59 @@
 //
 // Copyright (C) 2006 
 
+// TODO: open log-file as non-root
+// TODO: open socket as root ?
+// TODO: so best should be: start mediaserv as root and then drop privileges
+// TODO: mime-db
+
 // Definition of non-inline non-template functions
 
 //#include "main.hh"
 //#include "main.ih"
 
 // Custom includes
+#include <unistd.h>
+#include <iostream>
+
+#include "Server/HTTPLogger.hh"
+#include "Server/SimpleHTTPServer.hh"
+#include "Socket/TCPSocketHandle.hh"
 
 //#include "main.mpp"
 #define prefix_
 ///////////////////////////////cc.p////////////////////////////////////////
 
+namespace {
+    void errfail(char const * fn)
+    {
+       std::cerr << fn << ": (" << errno << ") " << strerror(errno) << "\n";
+       exit(1);
+    }
+       
+}
+
 int main(int argc, char** argv)
 {
+    //try {
+       g0dil::mediaserv::HTTPLogger logger ("log/access.log");
+       //try {
+           if (chdir("wwwroot") < 0) errfail("chdir");
+           if (chroot(".") < 0) errfail("chroot");
+           if (setreuid(getuid(),getuid()) < 0) errfail("setreuid");
+           satcom::lib::TCPv4ServerSocketHandle socket (argv[1]);
+           socket.blocking(false);
+           socket.protocol().reuseaddr(true);
+           g0dil::mediaserv::SimpleHTTPServer server (socket,logger);
+           satcom::lib::Scheduler::instance().process();
+//     }
+//     catch (std::exception const & ex) {
+//         logger.failedRequest(ex.what());
+//     }
+//     }
+//     catch (std::exception const & ex) {
+//     std::cerr << ex.what() << "\n";
+//     exit(1);
+//     }
     return 0;
 }
 
diff --git a/testclient.cc b/testclient.cc
new file mode 100644 (file)
index 0000000..1365600
--- /dev/null
@@ -0,0 +1,45 @@
+// $Id$
+//
+// Copyright (C) 2006 
+
+// Definition of non-inline non-template functions
+
+//#include "testclient.hh"
+//#include "testclient.ih"
+
+// Custom includes
+#include <iostream>
+#include "Socket/TCPSocketHandle.hh"
+#include "Utils/MicroTime.hh"
+
+//#include "testclient.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+int main(int argc, char** argv)
+{
+    satcom::lib::TCPv4ClientSocketHandle handle (argv[1]);
+    handle.write(argv[2]);
+    handle.write(" HTTP/1.1\r\n");
+    if (argc>3) {
+       handle.write("Host: ");
+       handle.write(argv[3]);
+       handle.write("\r\n");
+    }
+    handle.write("\r\n");
+    unsigned size = 0;
+    satcom::lib::MicroTime offset = satcom::lib::now();
+    while (handle) {
+       size += handle.read().size();
+       std::cout << satcom::lib::now()-offset << " " << size << "\n";
+    }
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "testclient.mpp"
+
+\f
+// Local Variables:
+// mode: c++
+// End: