summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVegard Storheil Eriksen <zyp@jvnv.net>2011-03-02 22:25:09 +0100
committerVegard Storheil Eriksen <zyp@jvnv.net>2011-03-02 22:25:09 +0100
commit0c2f90ee5c713fcb3aedb236fcebe7dd6d323ba3 (patch)
tree5cd273e39a9679afd75a97bdb2bb6d1f839b0dc9
parent0649a5c78eeef6eba731ee1c698e18554cad5670 (diff)
Add ability to serve parts of a file.
-rw-r--r--http_connection.cpp64
-rw-r--r--http_connection.h8
-rw-r--r--http_static.cpp8
-rw-r--r--music.cpp31
4 files changed, 100 insertions, 11 deletions
diff --git a/http_connection.cpp b/http_connection.cpp
index 041cd05..daba10b 100644
--- a/http_connection.cpp
+++ b/http_connection.cpp
@@ -1,17 +1,24 @@
#include "http_connection.h"
#include <iostream>
+#include <algorithm>
#include <boost/bind.hpp>
#include <boost/format.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <boost/filesystem/fstream.hpp>
namespace response_map_init {
typedef std::pair<int, std::string> P;
const P m[] = {
P(200, "OK"),
+ P(206, "Partial Content"),
P(400, "Bad Request"),
P(403, "Forbidden"),
P(404, "Not Found"),
+ P(416, "Requested Range Not Satisfiable"),
P(500, "Internal Server Error"),
P(501, "Not Implemented")
};
@@ -52,6 +59,59 @@ void HTTP::Connection::send_data(std::istream& stream) {
}
}
+void HTTP::Connection::send_file(const fs::path& filename) {
+ //req->add_header("Content-Type", "application/octet-stream");
+
+ fs::ifstream is(filename, std::ios::in | std::ios::binary);
+ is.seekg(0, std::ios::end);
+ std::size_t length = is.tellg();
+ is.seekg(0, std::ios::beg);
+
+ // Simple Range-parser.
+ // TODO: Support other formats than a single range.
+ if(headers.count("Range")) {
+ std::string range_str = headers["Range"];
+
+ if(!boost::starts_with(range_str, "bytes=")) {
+ // Broken header? We can't cope with that.
+ return;
+ }
+
+ range_str = range_str.substr(6);
+
+ std::string::iterator it = std::find(range_str.begin(), range_str.end(), '-');
+
+ std::size_t begin = boost::lexical_cast<int>(std::string(range_str.begin(), it));
+ std::size_t end = boost::lexical_cast<int>(std::string(it + 1, range_str.end())) + 1;
+
+ // Is the range past the end of the file?
+ if(end > length) {
+ end = length;
+ }
+
+ // Do the file contain requested range?
+ if(end && end < begin || begin > length) {
+ send_error(416);
+ }
+
+ add_header("Content-Range", boost::str(boost::format("bytes %d-%d/%d") % begin % (end - 1) % length));
+ add_header("Content-Length", boost::str(boost::format("%d") % (end - begin)));
+
+ write_headers(206);
+
+ is.seekg(begin);
+
+ char* buf = new char[end - begin];
+ is.read(buf, end - begin);
+ send_data(buf, is.gcount());
+ delete buf;
+
+ } else {
+ add_header("Content-Length", boost::str(boost::format("%d") % length));
+ send_data(is);
+ }
+}
+
HTTP::Connection::Connection(boost::asio::io_service& io_service) : socket(io_service) {
headers_written = false;
}
@@ -114,13 +174,13 @@ std::string HTTP::Connection::pop_path_base() {
return base_path.back();
}
-void HTTP::Connection::write_headers() {
+void HTTP::Connection::write_headers(int code) {
if(headers_written) {
return;
}
headers_written = true;
- boost::asio::write(socket, boost::asio::buffer(std::string("HTTP/1.1 200 OK\r\n")));
+ boost::asio::write(socket, boost::asio::buffer(boost::str(boost::format("HTTP/1.1 %d %s\r\n") % code % response_map.find(code)->second)));
for(HeaderList::iterator it = response_headers.begin(); it != response_headers.end(); it++) {
boost::asio::write(socket, boost::asio::buffer(boost::str(boost::format("%s: %s\r\n") % it->first % it->second)));
diff --git a/http_connection.h b/http_connection.h
index d810254..f5cd01a 100644
--- a/http_connection.h
+++ b/http_connection.h
@@ -11,6 +11,9 @@
#include <boost/enable_shared_from_this.hpp>
using boost::asio::ip::tcp;
+#include <boost/filesystem.hpp>
+namespace fs = boost::filesystem;
+
#include <boost/function.hpp>
namespace HTTP {
@@ -54,6 +57,9 @@ namespace HTTP {
void send_data(const void* data, std::size_t size);
void send_data(std::istream& stream);
+ //! Send file.
+ void send_file(const fs::path& filename);
+
private:
typedef std::vector<std::pair<std::string, std::string> > HeaderList;
@@ -76,7 +82,7 @@ namespace HTTP {
bool parse_request(boost::asio::streambuf& buf);
//! Write response headers.
- void write_headers();
+ void write_headers(int code = 200);
//! Response headers written?
bool headers_written;
diff --git a/http_static.cpp b/http_static.cpp
index 7f56744..a0856f4 100644
--- a/http_static.cpp
+++ b/http_static.cpp
@@ -19,12 +19,8 @@ void HTTP::Static::operator()(Connection::p connection) {
}
if(fs::is_regular_file(path)) {
- fs::ifstream is(path, std::ios::in | std::ios::binary);
- is.seekg(0, std::ios::end);
- connection->add_header("content-length", boost::str(boost::format("%d") % is.tellg()));
- is.seekg(0, std::ios::beg);
-
- connection->send_data(is);
+ connection->send_file(path);
+
} else if(fs::exists(path)) {
connection->send_error(403);
} else {
diff --git a/music.cpp b/music.cpp
index ee9f2ba..e277ee5 100644
--- a/music.cpp
+++ b/music.cpp
@@ -247,8 +247,19 @@ MusicDirectory::MusicDirectory(const fs::path root) {
}
void MusicTrack::render(HTTP::Connection::p req) {
- req->add_header("content-type", "application/octet-stream");
-
+ req->add_header("Content-Type", "audio/mpeg");
+
+ int skip = 0;
+ int range = 0;
+
+ if(req->headers.count("Range")) {
+ if(req->headers["Range"] == "bytes=0-1024") {
+ range = 1025;
+ } else {
+ skip = 1025;
+ }
+ }
+
if(req->args.count("decoder") && req->args.count("encoder")) {
Decoder::p decoder = Decoder::get(req->args["decoder"], path.string());
Encoder::p encoder = Encoder::get(req->args["encoder"], decoder);
@@ -256,6 +267,19 @@ void MusicTrack::render(HTTP::Connection::p req) {
// TODO: Make an encoder-to-istream adapter to get rid of this:
char data[0x10000];
std::streamsize size = 1;
+
+ if(skip) {
+ size = encoder->read(data, skip);
+ }
+
+ if(range) {
+ size = encoder->read(data, range);
+ if(size > 0) {
+ req->send_data(data, size);
+ }
+ return;
+ }
+
while(size) {
size = encoder->read(data, 0x10000);
if(size > 0)
@@ -263,11 +287,14 @@ void MusicTrack::render(HTTP::Connection::p req) {
}
} else {
+ req->send_file(path);
+ /*
fs::ifstream is(path, std::ios::in | std::ios::binary);
is.seekg(0, std::ios::end);
req->add_header("content-length", boost::str(boost::format("%d") % is.tellg()));
is.seekg(0, std::ios::beg);
req->send_data(is);
+ */
}
}