summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrandomdan <randomdan@localhost>2014-03-20 23:49:45 +0000
committerrandomdan <randomdan@localhost>2014-03-20 23:49:45 +0000
commit64034e574e9fd150d269193afaf1d544bb26dfe0 (patch)
treec634070b88a1832c8bfb6945570778e7719c1b01
parentAdd a stream presenter, allows output of any source stream (diff)
downloadproject2-64034e574e9fd150d269193afaf1d544bb26dfe0.tar.bz2
project2-64034e574e9fd150d269193afaf1d544bb26dfe0.tar.xz
project2-64034e574e9fd150d269193afaf1d544bb26dfe0.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.cpp24
-rw-r--r--project2/cgi/cgiRequestContext.h2
-rw-r--r--project2/cgi/cgiResultWritable.cpp26
-rw-r--r--project2/cgi/testCgi.cpp1
-rw-r--r--project2/common/presenterCache.cpp2
-rw-r--r--project2/common/range.h22
-rw-r--r--project2/common/stream.cpp22
-rw-r--r--project2/common/stream.h9
-rw-r--r--project2/common/transform.h9
-rw-r--r--project2/json/presenter.cpp4
-rw-r--r--project2/json/presenter.h4
-rw-r--r--project2/mail/sendmailTask.cpp4
-rw-r--r--project2/streams/streamPresenter.cpp31
-rw-r--r--project2/xml/transformHtml.cpp4
-rw-r--r--project2/xml/transformHtml.h4
-rw-r--r--project2/xml/transformText.cpp4
-rw-r--r--project2/xml/transformText.h4
-rw-r--r--project2/xml/xmlPresenter.cpp4
-rw-r--r--project2/xml/xmlPresenter.h4
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 *);