Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

mercilessly hack up moost::http to do progressive downloads, with abs…

…olutely no regard for the nice structure it originally had
  • Loading branch information...
commit 6e9584d08cd6c6c096a1d986e885e5cc21545bd4 1 parent a050887
@RJ authored
View
83 playdar-daemon/deps/moost_http/include/moost/http/connection.hpp
@@ -26,6 +26,11 @@ class connection
explicit connection(boost::asio::io_service& io_service,
request_handler_base<RequestHandler>& handler);
+ ~connection()
+ {
+ cout << "DTOR connection" << endl;
+ }
+
/// Get the socket associated with the connection.
boost::asio::ip::tcp::socket& socket();
@@ -39,7 +44,8 @@ class connection
/// Handle completion of a write operation.
void handle_write(const boost::system::error_code& e);
-
+ /// Handle completion of headers-sent, then wait on body.
+ void handle_write_stream (const boost::system::error_code& e,boost::shared_ptr<StreamingStrategy> ss, char * scratch);
/// Strand to ensure the connection's handlers are not called concurrently.
boost::asio::io_service::strand strand_;
@@ -81,6 +87,7 @@ void connection<RequestHandler>::start()
boost::asio::placeholders::bytes_transferred)));
}
+
template<class RequestHandler>
void connection<RequestHandler>::handle_read(const boost::system::error_code& e,
std::size_t bytes_transferred)
@@ -94,10 +101,24 @@ void connection<RequestHandler>::handle_read(const boost::system::error_code& e,
if (result)
{
request_handler_.handle_request_base(request_, reply_);
- boost::asio::async_write(socket_, reply_.to_buffers(),
- strand_.wrap(
- boost::bind(&connection<RequestHandler>::handle_write, connection<RequestHandler>::shared_from_this(),
- boost::asio::placeholders::error)));
+ if(!reply_.streaming()) // normal request
+ {
+ // send all data, then call the shutdown handler
+ boost::asio::async_write(socket_, reply_.to_buffers(),
+ strand_.wrap(
+ boost::bind(&connection<RequestHandler>::handle_write, connection<RequestHandler>::shared_from_this(),
+ boost::asio::placeholders::error)));
+ }
+ else // streaming enabled, use the streamingstrategy.
+ {
+ boost::shared_ptr<StreamingStrategy> ss = reply_.get_ss();
+ cout << "sending headers.." << endl;
+ boost::asio::async_write(socket_, reply_.to_buffers(false),
+ strand_.wrap(
+ boost::bind(&connection<RequestHandler>::handle_write_stream, connection<RequestHandler>::shared_from_this(),
+ boost::asio::placeholders::error, ss, (char*)0)));
+ }
+
}
else if (!result)
{
@@ -145,6 +166,58 @@ void connection<RequestHandler>::handle_write(const boost::system::error_code& e
// destructor closes the socket.
}
+/// Used when handler sends headers first, then streams body.
+template<class RequestHandler>
+void connection<RequestHandler>::handle_write_stream
+ (const boost::system::error_code& e,
+ boost::shared_ptr<StreamingStrategy> ss,
+ char * scratch)
+{
+ //cout << "handle_write_stream" << endl;
+ if(scratch)
+ {
+ // free previous buffer
+ free(scratch);
+ }
+ else
+ {
+ // scratch is 0 the first time.
+ cout << "Initiating ss delivery.." << endl;
+ }
+
+ if (!e)
+ {
+ const size_t maxbuf = 4096 * 2;
+ char * buf = (char*)malloc(maxbuf);
+ int len, total=0;
+ //cout << "Reading SS...." << endl;
+ //cout << "-> " << ss->debug() << endl;
+ len = ss->read_bytes(buf, maxbuf);
+ if(len > 0)
+ {
+ total += len;
+ //cout << "Sending " << len << " bytes.. " << endl;
+ boost::asio::async_write(socket_, boost::asio::buffer(buf, len),
+ strand_.wrap(
+ boost::bind(&connection<RequestHandler>::handle_write_stream, connection<RequestHandler>::shared_from_this(),
+ boost::asio::placeholders::error, ss, buf)));
+ return;
+ }
+ // end of stream..
+ cout << "EOS(" << ss->debug() << ") Served: "
+ << total << " bytes" << endl;
+ // Initiate graceful connection closure.
+ boost::system::error_code ignored_ec;
+ socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
+ }
+ else
+ {
+ cout << "handle_write_stream error for " << ss->debug()
+ << endl;
+ }
+}
+
+
}} // moost::http
#endif // __MOOST_HTTP_CONNECTION_HPP__
View
31 playdar-daemon/deps/moost_http/include/moost/http/reply.hpp
@@ -4,9 +4,13 @@
#include <string>
#include <vector>
#include <boost/asio.hpp>
+#include <boost/shared_ptr.hpp>
#include "moost/http/header.hpp"
+#include "application/application.h"
+#include "resolvers/streaming_strategy.h"
+
namespace moost { namespace http {
/// A reply to be sent to a client.
@@ -42,10 +46,35 @@ struct reply
/// Convert the reply into a vector of buffers. The buffers do not own the
/// underlying memory blocks, therefore the reply object must remain valid and
/// not be changed until the write operation has completed.
- std::vector<boost::asio::const_buffer> to_buffers();
+ std::vector<boost::asio::const_buffer> to_buffers(bool inc_body = true);
/// Get a stock reply.
static reply stock_reply(status_type status);
+
+ // only needed if we stream response:
+
+ /// true if handler will stream body after headers sent
+ /// false means entire body prepared up-front.
+ bool m_streaming;
+ size_t m_streaming_len;
+ boost::shared_ptr<StreamingStrategy> m_ss;
+
+ void set_streaming(boost::shared_ptr<StreamingStrategy> ss,
+ size_t len)
+ {
+ m_streaming=true;
+ m_streaming_len = len;
+ m_ss = ss;
+ }
+ // get streaming strategy, for streaming response
+ boost::shared_ptr<StreamingStrategy> get_ss()
+ {
+ return m_ss;
+ }
+
+ size_t streaming_length() { return m_streaming_len; }
+ bool streaming() { return m_streaming; }
+
};
}} // moost::http
View
14 playdar-daemon/deps/moost_http/include/moost/http/request_handler_base.hpp
@@ -2,9 +2,14 @@
#define __MOOST_HTTP_REQUEST_HANDLER_BASE_HPP__
#include <string>
+#include <deque>
#include <boost/lexical_cast.hpp>
#include <boost/noncopyable.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition.hpp>
+
#include "moost/http/reply.hpp"
#include "moost/http/request.hpp"
@@ -26,13 +31,20 @@ struct request_handler_base
static_cast< RequestHandler * >(this)->handle_request(req, rep);
- rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
+ size_t clen = rep.content.size();
+ if(rep.streaming())
+ {
+ clen = rep.streaming_length();
+ }
+ rep.headers[0].value = boost::lexical_cast<std::string>(clen);
}
void handle_request(const request& req, reply& rep)
{
// default base implementation does nothing
}
+
+
};
}} // moost::http
View
9 playdar-daemon/deps/moost_http/src/http/reply.cpp
@@ -89,7 +89,9 @@ const char crlf[] = { '\r', '\n' };
} // misc_strings
-std::vector<boost::asio::const_buffer> reply::to_buffers()
+/// if inc_body is false, only headers are returned.
+/// (used when streaming responses is enabled)
+std::vector<boost::asio::const_buffer> reply::to_buffers(bool inc_body /*=true*/)
{
std::vector<boost::asio::const_buffer> buffers;
buffers.push_back(status_strings::to_buffer(status));
@@ -102,7 +104,10 @@ std::vector<boost::asio::const_buffer> reply::to_buffers()
buffers.push_back(boost::asio::buffer(misc_strings::crlf));
}
buffers.push_back(boost::asio::buffer(misc_strings::crlf));
- buffers.push_back(boost::asio::buffer(content));
+ if(inc_body)
+ {
+ buffers.push_back(boost::asio::buffer(content));
+ }
return buffers;
}
View
39 playdar-daemon/src/playdar/playdar_request_handler.cpp
@@ -11,6 +11,7 @@
#include <moost/http/filesystem_request_handler.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/thread.hpp>
+#include <boost/shared_ptr.hpp>
#include "playdar_request_handler.h"
#include "library/library.h"
@@ -73,6 +74,12 @@ playdar_request_handler::handle_request(const moost::http::request& req, moost::
{
serve_static_file(req, rep);
}
+ else if(parts[1]=="streaming")
+ {
+ boost::shared_ptr<StreamingStrategy> ss(new LocalFileStreamingStrategy("/home/rj/fakemp3/01-Train Train-Jag.mp3"));
+ rep.set_streaming(ss, 1234);
+ return;
+ }
else if(parts[1]=="api" && querystring.count("method")==1)
{
handle_rest_api(querystring, req, rep);
@@ -319,7 +326,8 @@ playdar_request_handler::serve_static_file(const moost::http::request& req, moos
frh.handle_request(req, rep);
}
-// Serves the music file based on a SID (from a playableitem resulting from a query)
+// Serves the music file based on a SID
+// (from a playableitem resulting from a query)
void
playdar_request_handler::serve_sid(const moost::http::request& req, moost::http::reply& rep, source_uid sid)
{
@@ -328,29 +336,16 @@ playdar_request_handler::serve_sid(const moost::http::request& req, moost::http:
cout << "-> PlayableItem: " << pip->artist() << " - " << pip->track() << endl;
boost::shared_ptr<StreamingStrategy> ss = pip->streaming_strategy();
cout << "-> " << ss->debug() << endl;
-
- rep.status = moost::http::reply::ok;
- char buf[16384];
- int len, total=0;
- cout << "INFO Serving track from '"<< pip->source() <<"'" << endl;
- cout << "Reading...." << endl;
- while ((len = ss->read_bytes((char*)&buf, sizeof(buf)))>0)
- {
- total+=len;
- cout << "Appending " << len << " bytes.. " << endl;
- // TODO moost::http doesnt support streaming response to user
- // you have to prepare it all up-front for now.
- rep.content.append(buf, len);
- }
- cout << "Sending " << total << " bytes...." << endl;
- rep.headers.resize(2);
- // headers added by moost anyway ?
- rep.headers[0].name = "Content-Length";
- rep.headers[0].value = total;//pip->size();
- rep.headers[1].name = "Content-Type";
- rep.headers[1].value = pip->mimetype();
+ // hand off the streaming strategy for the http server to do:
+ rep.set_streaming(ss, pip->size());
+ return;
}
+/*
+ Serves a file based on fid from library.
+ Might be useful if browsing your local library and you want
+ to play tracks without searching and generating SIDs etc.
+*/
void
playdar_request_handler::serve_track(const moost::http::request& req, moost::http::reply& rep, int tid)
{
Please sign in to comment.
Something went wrong with that request. Please try again.