From: g0dil Date: Sun, 12 Nov 2006 08:06:21 +0000 (+0000) Subject: Finished version 0.1 of media server .. Jay !!! X-Git-Url: http://g0dil.de/git?p=mediaserv.git;a=commitdiff_plain;h=c05982737183fe02022b3e5e7948d58bc13bf953 Finished version 0.1 of media server .. Jay !!! --- diff --git a/.gitignore b/.gitignore index 2755c62..2cd2a32 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,10 @@ mediaserv +testclient libScheduler.a libSocket.a libUtils.a libServer.a +TODOS +BUGS +FIXMES + diff --git a/SConstruct b/SConstruct index b970018..3cf7202 100644 --- a/SConstruct +++ b/SConstruct @@ -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 index 66f0501..0000000 --- a/Server/Connection.cc +++ /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" - - -// Local Variables: -// mode: c++ -// End: diff --git a/Server/Connection.cci b/Server/Connection.cci deleted file mode 100644 index c09e778..0000000 --- a/Server/Connection.cci +++ /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_ - - -// Local Variables: -// mode: c++ -// End: diff --git a/Server/HTTPConnection.cc b/Server/HTTPConnection.cc new file mode 100644 index 0000000..33a4e1c --- /dev/null +++ b/Server/HTTPConnection.cc @@ -0,0 +1,115 @@ +// $Id$ +// +// Copyright (C) 2006 + +// Definition of non-inline non-template functions + +//#include "HTTPConnection.hh" +//#include "HTTPConnection.ih" + +// Custom includes +#include +#include +#include +#include + +#include +#include + +#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 + ::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::ptr helper) +{ + + try { + helper->throw_error(); + + request_.parseRequest(helper->handle(), helper->data()); + + boost::algorithm::split_iterator + i (request_.url(),boost::algorithm::first_finder(".")); + boost::algorithm::split_iterator i_end; + if (i == i_end || ++i == i_end) throw InvalidHTTPRequestException(); + bandwidth_ = boost::lexical_cast(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 + ::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::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" + + +// Local Variables: +// mode: c++ +// End: diff --git a/Server/HTTPConnection.hh b/Server/HTTPConnection.hh new file mode 100644 index 0000000..e8b097e --- /dev/null +++ b/Server/HTTPConnection.hh @@ -0,0 +1,84 @@ +// $Id$ +// +// Copyright (C) 2006 + +#ifndef HH_HTTPConnection_ +#define HH_HTTPConnection_ 1 + +// Custom includes +#include +#include +#include +#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 ptr; + typedef satcom::lib::ClientSocketHandle< + satcom::lib::MakeSocketPolicy::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::ptr); + void startStream(satcom::lib::WriteHelper::ptr); + + ClientHandle client_; + SimpleHTTPServer & server_; + HTTPLogger & logger_; + HTTPRequest request_; + boost::scoped_ptr connection_; + int fileFd_; + unsigned bandwidth_; + }; + +}} + +///////////////////////////////hh.e//////////////////////////////////////// +//#include "HTTPConnection.cci" +//#include "HTTPConnection.ct" +//#include "HTTPConnection.cti" +//#include "HTTPConnection.mpp" +#endif + + +// Local Variables: +// mode: c++ +// End: diff --git a/Server/HTTPLogger.cc b/Server/HTTPLogger.cc new file mode 100644 index 0000000..f2f880e --- /dev/null +++ b/Server/HTTPLogger.cc @@ -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" + + +// Local Variables: +// mode: c++ +// End: diff --git a/Server/HTTPLogger.hh b/Server/HTTPLogger.hh new file mode 100644 index 0000000..8d3b6c0 --- /dev/null +++ b/Server/HTTPLogger.hh @@ -0,0 +1,59 @@ +// $Id$ +// +// Copyright (C) 2006 + +#ifndef HH_HTTPLogger_ +#define HH_HTTPLogger_ 1 + +// Custom includes +#include +#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 + + +// Local Variables: +// mode: c++ +// End: diff --git a/Server/HTTPRequest.cc b/Server/HTTPRequest.cc new file mode 100644 index 0000000..4fb444e --- /dev/null +++ b/Server/HTTPRequest.cc @@ -0,0 +1,105 @@ +// $Id$ +// +// Copyright (C) 2006 + +// Definition of non-inline non-template functions + +#include "HTTPRequest.hh" +//#include "HTTPRequest.ih" + +// Custom includes +#include +#include +#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(handle).peer().host(); + } + catch (std::bad_cast const *) { + host_ = "(unidentified address)"; + } + boost::algorithm::split_iterator + line (request, boost::algorithm::first_finder("\n")); + boost::algorithm::split_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 line) +{ + boost::algorithm::split_iterator + token (line, boost::algorithm::first_finder(" ")); + boost::algorithm::split_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 line) +{ + boost::iterator_range 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" + + +// Local Variables: +// mode: c++ +// End: diff --git a/Server/HTTPRequest.cci b/Server/HTTPRequest.cci new file mode 100644 index 0000000..ff340d2 --- /dev/null +++ b/Server/HTTPRequest.cci @@ -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_ + + +// Local Variables: +// mode: c++ +// End: diff --git a/Server/HTTPRequest.hh b/Server/HTTPRequest.hh new file mode 100644 index 0000000..2a4e367 --- /dev/null +++ b/Server/HTTPRequest.hh @@ -0,0 +1,84 @@ +// $Id$ +// +// Copyright (C) 2006 + +#ifndef HH_HTTPRequest_ +#define HH_HTTPRequest_ 1 + +// Custom includes +#include +#include +#include +#include +#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 line); + void parseRequestHeader(boost::iterator_range line); + + typedef std::map 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 + + +// Local Variables: +// mode: c++ +// End: diff --git a/Server/Connection.test.cc b/Server/HTTPRequest.test.cc similarity index 75% copy from Server/Connection.test.cc copy to Server/HTTPRequest.test.cc index 76fcb4f..c75d968 100644 --- a/Server/Connection.test.cc +++ b/Server/HTTPRequest.test.cc @@ -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 #include @@ -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 index 0000000..79f0385 --- /dev/null +++ b/Server/MimeTypes.cc @@ -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" + + +// Local Variables: +// mode: c++ +// End: diff --git a/Server/MimeTypes.hh b/Server/MimeTypes.hh new file mode 100644 index 0000000..c94e836 --- /dev/null +++ b/Server/MimeTypes.hh @@ -0,0 +1,64 @@ +// $Id$ +// +// Copyright (C) 2006 + +#ifndef HH_MimeTypes_ +#define HH_MimeTypes_ 1 + +// Custom includes +#include +#include + +//#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 Map; + + static MimeTypes & instance(); + + Map mimeTypes_; + }; + + +}} + +///////////////////////////////hh.e//////////////////////////////////////// +//#include "MimeTypes.cci" +//#include "MimeTypes.ct" +//#include "MimeTypes.cti" +//#include "MimeTypes.mpp" +#endif + + +// Local Variables: +// mode: c++ +// End: diff --git a/Server/SimpleHTTPServer.cc b/Server/SimpleHTTPServer.cc new file mode 100644 index 0000000..c749c59 --- /dev/null +++ b/Server/SimpleHTTPServer.cc @@ -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" + + +// Local Variables: +// mode: c++ +// End: diff --git a/Server/SimpleHTTPServer.cci b/Server/SimpleHTTPServer.cci new file mode 100644 index 0000000..df93bb4 --- /dev/null +++ b/Server/SimpleHTTPServer.cci @@ -0,0 +1,25 @@ +// $Id$ +// +// Copyright (C) 2006 + +// Definition of inline non-template functions + +// Custom includes +#include + +#define prefix_ inline +///////////////////////////////cci.p/////////////////////////////////////// + +prefix_ g0dil::mediaserv::SimpleHTTPServer & g0dil::mediaserv::SimpleHTTPServer::instance() +{ + BOOST_ASSERT(instance_); + return *instance_; +} + +///////////////////////////////cci.e/////////////////////////////////////// +#undef prefix_ + + +// Local Variables: +// mode: c++ +// End: diff --git a/Server/SimpleHTTPServer.hh b/Server/SimpleHTTPServer.hh new file mode 100644 index 0000000..56c79c2 --- /dev/null +++ b/Server/SimpleHTTPServer.hh @@ -0,0 +1,75 @@ +// $Id$ +// +// Copyright (C) 2006 + +#ifndef HH_SimpleHTTPServer_ +#define HH_SimpleHTTPServer_ 1 + +// Custom includes +#include +#include +#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 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 + + +// Local Variables: +// mode: c++ +// End: diff --git a/Server/Connection.test.cc b/Server/SimpleHTTPServer.test.cc similarity index 72% copy from Server/Connection.test.cc copy to Server/SimpleHTTPServer.test.cc index 76fcb4f..4954975 100644 --- a/Server/Connection.test.cc +++ b/Server/SimpleHTTPServer.test.cc @@ -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 #include @@ -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 index 0000000..130abf8 --- /dev/null +++ b/Server/StreamConnection.cc @@ -0,0 +1,88 @@ +// $Id$ +// +// Copyright (C) 2006 + +// Definition of non-inline non-template functions + +#include "StreamConnection.hh" +//#include "StreamConnection.ih" + +// Custom includes +#include +#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" + + +// Local Variables: +// mode: c++ +// End: diff --git a/Server/StreamConnection.cci b/Server/StreamConnection.cci new file mode 100644 index 0000000..8035dd4 --- /dev/null +++ b/Server/StreamConnection.cci @@ -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_ + + +// Local Variables: +// mode: c++ +// End: diff --git a/Server/Connection.hh b/Server/StreamConnection.hh similarity index 64% rename from Server/Connection.hh rename to Server/StreamConnection.hh index 0a40fa3..ba71fe6 100644 --- a/Server/Connection.hh +++ b/Server/StreamConnection.hh @@ -2,26 +2,28 @@ // // Copyright (C) 2006 -#ifndef HH_Connection_ -#define HH_Connection_ 1 +#ifndef HH_StreamConnection_ +#define HH_StreamConnection_ 1 // Custom includes #include +#include + #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::policy> ClientHandle; + satcom::lib::WriteablePolicy>::policy> ClientHandle; + + typedef boost::intrusive_ptr ptr; + typedef boost::function 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 diff --git a/Server/Connection.test.cc b/Server/StreamConnection.test.cc similarity index 93% rename from Server/Connection.test.cc rename to Server/StreamConnection.test.cc index 76fcb4f..15d85d7 100644 --- a/Server/Connection.test.cc +++ b/Server/StreamConnection.test.cc @@ -8,7 +8,7 @@ //#include "Connection.test.ih" // Custom includes -#include "Connection.hh" +#include "StreamConnection.hh" #include #include diff --git a/main.cc b/main.cc index 9922bce..5c057c7 100644 --- 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 +#include + +#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 index 0000000..1365600 --- /dev/null +++ b/testclient.cc @@ -0,0 +1,45 @@ +// $Id$ +// +// Copyright (C) 2006 + +// Definition of non-inline non-template functions + +//#include "testclient.hh" +//#include "testclient.ih" + +// Custom includes +#include +#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" + + +// Local Variables: +// mode: c++ +// End: