diff options
| author | randomdan <randomdan@localhost> | 2014-03-20 23:49:45 +0000 | 
|---|---|---|
| committer | randomdan <randomdan@localhost> | 2014-03-20 23:49:45 +0000 | 
| commit | a9523a398c57733cde6b736c14129288095491fa (patch) | |
| tree | c634070b88a1832c8bfb6945570778e7719c1b01 | |
| parent | Add a stream presenter, allows output of any source stream (diff) | |
| download | project2-a9523a398c57733cde6b736c14129288095491fa.tar.bz2 project2-a9523a398c57733cde6b736c14129288095491fa.tar.xz project2-a9523a398c57733cde6b736c14129288095491fa.zip | |
Support range requests and seekable streams
Untangle streams/streamPresenter/writableContent
Pass the ExecContext into more bits of content interface
| -rw-r--r-- | project2/cgi/cgiRequestContext.cpp | 24 | ||||
| -rw-r--r-- | project2/cgi/cgiRequestContext.h | 2 | ||||
| -rw-r--r-- | project2/cgi/cgiResultWritable.cpp | 26 | ||||
| -rw-r--r-- | project2/cgi/testCgi.cpp | 1 | ||||
| -rw-r--r-- | project2/common/presenterCache.cpp | 2 | ||||
| -rw-r--r-- | project2/common/range.h | 22 | ||||
| -rw-r--r-- | project2/common/stream.cpp | 22 | ||||
| -rw-r--r-- | project2/common/stream.h | 9 | ||||
| -rw-r--r-- | project2/common/transform.h | 9 | ||||
| -rw-r--r-- | project2/json/presenter.cpp | 4 | ||||
| -rw-r--r-- | project2/json/presenter.h | 4 | ||||
| -rw-r--r-- | project2/mail/sendmailTask.cpp | 4 | ||||
| -rw-r--r-- | project2/streams/streamPresenter.cpp | 31 | ||||
| -rw-r--r-- | project2/xml/transformHtml.cpp | 4 | ||||
| -rw-r--r-- | project2/xml/transformHtml.h | 4 | ||||
| -rw-r--r-- | project2/xml/transformText.cpp | 4 | ||||
| -rw-r--r-- | project2/xml/transformText.h | 4 | ||||
| -rw-r--r-- | project2/xml/xmlPresenter.cpp | 4 | ||||
| -rw-r--r-- | project2/xml/xmlPresenter.h | 4 | 
19 files changed, 133 insertions, 51 deletions
| diff --git a/project2/cgi/cgiRequestContext.cpp b/project2/cgi/cgiRequestContext.cpp index d38a256..2827aa7 100644 --- a/project2/cgi/cgiRequestContext.cpp +++ b/project2/cgi/cgiRequestContext.cpp @@ -8,6 +8,7 @@  #include <map>  #include <cgicc/Cgicc.h>  #include <curl/curl.h> +#include <boost/lexical_cast.hpp>  #include <boost/uuid/uuid_io.hpp>  #include <boost/uuid/uuid_generators.hpp> @@ -120,3 +121,26 @@ CgiRequestContext::getParameter(const VariableType & p) const  	return cgi(p);  } +boost::optional<RangeRequest> +CgiRequestContext::getRequestRange() const +{ +	const auto r = cgienv.getenv("HTTP_RANGE"); +	std::vector<std::string> rangeParts; +	boost::split(rangeParts, r, boost::is_any_of("=-"), boost::algorithm::token_compress_off); + +	if (rangeParts.size() < 3 || (rangeParts[1].empty() && rangeParts[2].empty())) { +		return boost::optional<RangeRequest>(); +	} +	RangeRequest range; +	range.Unit = rangeParts[0]; + +	if (!rangeParts[1].empty()) { +		range.Start = boost::lexical_cast<RangePos>(rangeParts[1]); +	} +	if (!rangeParts[2].empty()) { +		range.End = boost::lexical_cast<RangePos>(rangeParts[2]); +	} + +	return range; +} + diff --git a/project2/cgi/cgiRequestContext.h b/project2/cgi/cgiRequestContext.h index 57c233d..9501a20 100644 --- a/project2/cgi/cgiRequestContext.h +++ b/project2/cgi/cgiRequestContext.h @@ -6,6 +6,7 @@  #include "cgiEnvInput.h"  #include "cgiRouter.h"  #include "execContext.h" +#include <range.h>  #include <cgicc/CgiEnvironment.h>  #include <cgicc/Cgicc.h>  #include <lazyPointer.h> @@ -40,6 +41,7 @@ class CgiRequestContext : public ExecContext {  		ETags getRequestETags() const;  		time_t getRequestModifiedSince() const;  		std::string getCookieValue(const std::string & name) const; +		boost::optional<RangeRequest> getRequestRange() const;  		LazyPointer<Router> router;  		boost::posix_time::ptime startTime; diff --git a/project2/cgi/cgiResultWritable.cpp b/project2/cgi/cgiResultWritable.cpp index 357a57c..1364456 100644 --- a/project2/cgi/cgiResultWritable.cpp +++ b/project2/cgi/cgiResultWritable.cpp @@ -4,8 +4,32 @@  class WritableToCgiResult : public TransformImpl<WritableContent, CgiResult> {  	public:  		void transform(const WritableContent * wc, CgiResult * cr, ExecContext * ec) const { -			cr->header->addHeader("Content-Type", Glib::ustring::compose("%1; charset=%2", wc->getContentType(), cr->encoding)); +			cr->header->addHeader("Content-Type", Glib::ustring::compose("%1; charset=%2", wc->getContentType(ec), cr->encoding));  			cr->header->addHeader("Cache-control", "no-cache"); +			auto size = wc->getSizeInBytes(ec); +			if (size) { +				auto range = wc->getRange(ec); +				if (range) { +					cr->header->addHeader("Status", "206 Partial Content"); +					cr->header->addHeader("Content-Length", Glib::ustring::compose("%1", range->End - range->Start + 1)); +					cr->header->addHeader("Content-Range", Glib::ustring::compose("%1 %2-%3/%4", +								range->Unit, range->Start, range->End, *size)); +				} +				else { +					cr->header->addHeader("Content-Length", Glib::ustring::compose("%1", *size)); +				} +			} +			if (wc->isSeekable()) { +				cr->header->addHeader("Accept-Ranges", "bytes"); +			} +			auto modifiedTime = wc->getModifiedTime(ec); +			if (modifiedTime) { +				char buf[100]; +				struct tm stm; +				gmtime_r(&*modifiedTime, &stm); +				strftime(buf, sizeof(buf), "%a, %d %b %Y %T %z", &stm); +				cr->header->addHeader("Last-Modified", buf); +			}  			cr->header->render(cr->stream);  			wc->writeTo(cr->stream, cr->encoding, ec);  		} diff --git a/project2/cgi/testCgi.cpp b/project2/cgi/testCgi.cpp index 6644102..0c2b472 100644 --- a/project2/cgi/testCgi.cpp +++ b/project2/cgi/testCgi.cpp @@ -125,6 +125,7 @@ TESTOPT("HTTP_REFERER", "", "Referrer")  TESTOPT("HTTP_COOKIE", "", "Cookie")  TESTOPT("HTTPS", "No", "HTTPS?")  TESTOPT("HTTP_IF_MODIFIED_SINCE", "", "Client cached copy timestamp") +TESTOPT("HTTP_RANGE", "", "Range requested by client '{unit}=[start]-[end]'")  ("urlListFile", Options::value(&urlListFile, ""), "Load URL list from this file")  ("runCount", Options::value(&runCount, 1), "Repeat run this many times")  ("help", ShowHelpComponent::Option(), "Print usage and exit")("h")  diff --git a/project2/common/presenterCache.cpp b/project2/common/presenterCache.cpp index 31fdc8f..1b56db8 100644 --- a/project2/common/presenterCache.cpp +++ b/project2/common/presenterCache.cpp @@ -16,7 +16,7 @@ PresenterCache::flushCache()  class WriteToCache : public TransformImpl<WritableContent, PresenterCache> {  	public:  		void transform(const WritableContent * wc, PresenterCache * pc, ExecContext * ec) const { -			wc->writeTo(pc->writeCache(wc->getContentType(), pc->encoding, ec), pc->encoding, ec); +			wc->writeTo(pc->writeCache(wc->getContentType(ec), pc->encoding, ec), pc->encoding, ec);  		}  };  DECLARE_TRANSFORM(WriteToCache); diff --git a/project2/common/range.h b/project2/common/range.h new file mode 100644 index 0000000..d7606b4 --- /dev/null +++ b/project2/common/range.h @@ -0,0 +1,22 @@ +#ifndef RANGE_H +#define RANGE_H + +#include <boost/optional.hpp> + +typedef unsigned long long RangePos; +typedef boost::optional<RangePos> OptionalRangePos; + +struct RangeRequest { +	std::string Unit; +	OptionalRangePos Start; +	OptionalRangePos End; +}; + +struct RangeResponse { +	std::string Unit; +	RangePos Start; +	RangePos End; +}; + +#endif + diff --git a/project2/common/stream.cpp b/project2/common/stream.cpp new file mode 100644 index 0000000..0db9225 --- /dev/null +++ b/project2/common/stream.cpp @@ -0,0 +1,22 @@ +#include "stream.h" +#include <boost/bind.hpp> + +Glib::ustring +Stream::getContentType(ExecContext *) const +{ +	return "application/octet-stream"; +} + +size_t +Stream::write(std::ostream * os, const char * data, size_t len) +{ +	os->write(data, len); +	return len; +} + +void +Stream::writeTo(std::ostream & os, const std::string &, ExecContext * ec) const +{ +	runStream(boost::bind(&Stream::write, &os, _1, _2), ec); +} + diff --git a/project2/common/stream.h b/project2/common/stream.h index ad3ee79..bf0dc41 100644 --- a/project2/common/stream.h +++ b/project2/common/stream.h @@ -3,15 +3,22 @@  #include "sourceObject.h"  #include <boost/function.hpp> +#include "transform.h"  class ExecContext; -class Stream : public SourceObject { +class Stream : public SourceObject, public WritableContent {  	public:  		template<typename... X> Stream(const X &... x) : SourceObject(x...) { }  		typedef boost::function<size_t(const char *, size_t)> Sink;  		virtual void runStream(const Sink &, ExecContext *) const = 0; +		virtual Class getContentClass(ExecContext *) const { return ClassData; } +		virtual Glib::ustring getContentType(ExecContext *) const; +		virtual void writeTo(std::ostream & os, const std::string &, ExecContext * ec) const; + +	protected: +		static size_t write(std::ostream * os, const char * data, size_t len);  };  typedef boost::intrusive_ptr<Stream> StreamPtr; diff --git a/project2/common/transform.h b/project2/common/transform.h index 978e7df..7a46c7f 100644 --- a/project2/common/transform.h +++ b/project2/common/transform.h @@ -5,6 +5,7 @@  #include "intrusivePtrBase.h"  #include "scriptLoader.h"  #include <map> +#include "range.h"  class ExecContext; @@ -75,8 +76,12 @@ class WritableContent {  			ClassPretty = 2,  			ClassData = 3  		}; -		virtual Class getContentClass() const = 0; -		virtual Glib::ustring getContentType() const = 0; +		virtual Class getContentClass(ExecContext *) const = 0; +		virtual Glib::ustring getContentType(ExecContext *) const = 0; +		virtual boost::optional<unsigned long long> getSizeInBytes(ExecContext *) const { return boost::optional<unsigned long long>(); } +		virtual boost::optional<time_t> getModifiedTime(ExecContext *) const { return boost::optional<time_t>(); } +		virtual boost::optional<RangeResponse> getRange(ExecContext *) const { return boost::optional<RangeResponse>(); } +		virtual bool isSeekable() const { return false; }  		virtual void writeTo(std::ostream &, const std::string & encoding, ExecContext *) const = 0;  }; diff --git a/project2/json/presenter.cpp b/project2/json/presenter.cpp index 5b769c9..c07c71e 100644 --- a/project2/json/presenter.cpp +++ b/project2/json/presenter.cpp @@ -119,13 +119,13 @@ JsonPresenter::operator const WritableContent * () const  }  Glib::ustring -JsonPresenter::getContentType() const +JsonPresenter::getContentType(ExecContext *) const  {  	return contentType;  }  WritableContent::Class -JsonPresenter::getContentClass() const +JsonPresenter::getContentClass(ExecContext *) const  {  	return ClassData;  } diff --git a/project2/json/presenter.h b/project2/json/presenter.h index 5ccecb2..dbb2b7a 100644 --- a/project2/json/presenter.h +++ b/project2/json/presenter.h @@ -27,8 +27,8 @@ class JsonPresenter : public MultiRowSetPresenter, public ContentPresenter, publ  		void finishArray(bool) const;  		operator const json::Object *() const;  		operator const WritableContent * () const; -		Glib::ustring getContentType() const; -		Class getContentClass() const; +		Glib::ustring getContentType(ExecContext *) const; +		Class getContentClass(ExecContext *) const;  		void writeTo(std::ostream & o, const std::string & encoding, ExecContext *) const;  	private: diff --git a/project2/mail/sendmailTask.cpp b/project2/mail/sendmailTask.cpp index bd4094e..3b3253a 100644 --- a/project2/mail/sendmailTask.cpp +++ b/project2/mail/sendmailTask.cpp @@ -137,10 +137,10 @@ class TransformWritableContentToEmail : public TransformImpl<WritableContent, Se  		}  		void transform(const WritableContent * wc, SendMailTask::Parts * parts, ExecContext * ec) const  		{ -			parts->parts.insert(new BoundaryBegin(wc->getContentType(), encoding, wc->getContentClass())); +			parts->parts.insert(new BoundaryBegin(wc->getContentType(ec), encoding, wc->getContentClass(ec)));  			std::stringstream str;  			wc->writeTo(str, encoding, ec); -			parts->parts.insert(new MimeContent(str.str(), wc->getContentClass())); +			parts->parts.insert(new MimeContent(str.str(), wc->getContentClass(ec)));  			SendMailTask::MailPart::mimeIdx += 1;  		}  		void configure(ScriptNodePtr s, ExecContext * ec) diff --git a/project2/streams/streamPresenter.cpp b/project2/streams/streamPresenter.cpp index 3dde21c..229799c 100644 --- a/project2/streams/streamPresenter.cpp +++ b/project2/streams/streamPresenter.cpp @@ -3,38 +3,14 @@  #include <transform.h>  #include <stream.h> -class StreamPresenter : public Presenter, public WritableContent, public SourceOf<WritableContent> { +class StreamPresenter : public Presenter, public SourceOf<WritableContent> {  	public:  		StreamPresenter(ScriptNodePtr e, ObjectSource os, ExecContext * ) : -			Presenter(os), -			contentType(e, "contenttype", "application/octet-stream") +			Presenter(os)  		{  			e->script->loader.addLoadTarget(e, Storer::into<ElementLoader>(&stream));  		} -		WritableContent::Class getContentClass() const -		{ -			return ClassData; -		} - -		Glib::ustring getContentType() const -		{ -			return contentType(NULL); -		} - -		static size_t write(std::ostream * os, const char * data, size_t len) -		{ -			os->write(data, len); -			return len; -		} - -		void writeTo(std::ostream & os, const std::string&, ExecContext * ec) const -		{ -			if (stream) { -				stream->runStream(boost::bind(&StreamPresenter::write, &os, _1, _2), ec); -			} -		} -  		void addNewArray(const Glib::ustring&, bool) const  		{  		} @@ -61,11 +37,10 @@ class StreamPresenter : public Presenter, public WritableContent, public SourceO  		operator const WritableContent * () const  		{ -			return this; +			return stream.get();  		}  	private: -		Variable contentType;  		StreamPtr stream;  }; diff --git a/project2/xml/transformHtml.cpp b/project2/xml/transformHtml.cpp index adf534b..2232a4f 100644 --- a/project2/xml/transformHtml.cpp +++ b/project2/xml/transformHtml.cpp @@ -49,13 +49,13 @@ HtmlDocument::writeTo(std::ostream & o, const std::string & encoding, ExecContex  }  Glib::ustring -HtmlDocument::getContentType() const +HtmlDocument::getContentType(ExecContext *) const  {  	return contentType;  }  WritableContent::Class -HtmlDocument::getContentClass() const { +HtmlDocument::getContentClass(ExecContext *) const {  	return ClassPretty;  } diff --git a/project2/xml/transformHtml.h b/project2/xml/transformHtml.h index d572244..b8fa82e 100644 --- a/project2/xml/transformHtml.h +++ b/project2/xml/transformHtml.h @@ -11,8 +11,8 @@ class HtmlDocument : public SourceOf<HtmlDocument>, public WritableContent, publ  		htmlDocPtr doc;  		operator const HtmlDocument * () const;  		operator const WritableContent * () const; -		Class getContentClass() const; -		Glib::ustring getContentType() const; +		Class getContentClass(ExecContext *) const; +		Glib::ustring getContentType(ExecContext *) const;  		void writeTo(std::ostream &, const std::string & encoding, ExecContext *) const;  		Glib::ustring contentType;  		Glib::ustring method; diff --git a/project2/xml/transformText.cpp b/project2/xml/transformText.cpp index f92e8ef..edf8a8b 100644 --- a/project2/xml/transformText.cpp +++ b/project2/xml/transformText.cpp @@ -26,13 +26,13 @@ TextDocument::writeTo(std::ostream & o, const std::string & encoding, ExecContex  }  Glib::ustring -TextDocument::getContentType() const +TextDocument::getContentType(ExecContext *) const  {  	return contentType(NULL);  }  WritableContent::Class -TextDocument::getContentClass() const { +TextDocument::getContentClass(ExecContext *) const {  	return ClassPlain;  } diff --git a/project2/xml/transformText.h b/project2/xml/transformText.h index 417ceb1..57e19f9 100644 --- a/project2/xml/transformText.h +++ b/project2/xml/transformText.h @@ -11,8 +11,8 @@ class TextDocument : public SourceOf<TextDocument>, public WritableContent, publ  		Glib::ustring doc;  		operator const TextDocument * () const;  		operator const WritableContent * () const; -		Class getContentClass() const; -		Glib::ustring getContentType() const; +		Class getContentClass(ExecContext *) const; +		Glib::ustring getContentType(ExecContext *) const;  		void writeTo(std::ostream &, const std::string & encoding, ExecContext *) const;  		Variable contentType;  }; diff --git a/project2/xml/xmlPresenter.cpp b/project2/xml/xmlPresenter.cpp index 10ff3e6..e7abc3a 100644 --- a/project2/xml/xmlPresenter.cpp +++ b/project2/xml/xmlPresenter.cpp @@ -250,12 +250,12 @@ XmlPresenter::finishArray(bool objects) const  }  Glib::ustring -XmlPresenter::getContentType() const { +XmlPresenter::getContentType(ExecContext *) const {  	return contentType;  }  WritableContent::Class -XmlPresenter::getContentClass() const { +XmlPresenter::getContentClass(ExecContext *) const {  	return ClassData;  } diff --git a/project2/xml/xmlPresenter.h b/project2/xml/xmlPresenter.h index c94e29a..6e3dac2 100644 --- a/project2/xml/xmlPresenter.h +++ b/project2/xml/xmlPresenter.h @@ -42,8 +42,8 @@ class XmlPresenter : public Presenter, public ContentPresenter, public SourceOf<  		operator const xmlDoc * () const;  		operator const boost::shared_ptr<xmlpp::Document> * () const;  		operator const WritableContent * () const; -		Class getContentClass() const; -		Glib::ustring getContentType() const; +		Class getContentClass(ExecContext *) const; +		Glib::ustring getContentType(ExecContext *) const;  		void writeTo(std::ostream &, const std::string & enc, ExecContext *) const;  		void finalizeContent() const;  		void init(ExecContext *); | 
