Commit 516991c1 authored by Paweł Wegner's avatar Paweł Wegner

ICloudProvider::downloadFileAsync: added range parameter.

parent e89dc66d
......@@ -62,7 +62,7 @@ ICloudProvider::GetItemRequest::Pointer MockProvider::getItemAsync(
}
ICloudProvider::DownloadFileRequest::Pointer MockProvider::downloadFileAsync(
IItem::Pointer item, IDownloadFileCallback::Pointer callback) {
IItem::Pointer item, IDownloadFileCallback::Pointer callback, Range) {
return util::make_unique<MockDownloadFileRequest>(item, std::move(callback));
}
......
......@@ -150,8 +150,9 @@ class MockProvider : public ICloudProvider {
IItem::Pointer, IListDirectoryCallback::Pointer) override;
GetItemRequest::Pointer getItemAsync(const std::string& absolute_path,
GetItemCallback) override;
DownloadFileRequest::Pointer downloadFileAsync(
IItem::Pointer, IDownloadFileCallback::Pointer) override;
DownloadFileRequest::Pointer downloadFileAsync(IItem::Pointer,
IDownloadFileCallback::Pointer,
Range) override;
UploadFileRequest::Pointer uploadFileAsync(
IItem::Pointer, const std::string& filename,
IUploadFileCallback::Pointer) override;
......
......@@ -285,9 +285,9 @@ ICloudProvider::GetItemRequest::Pointer CloudProvider::getItemAsync(
}
ICloudProvider::DownloadFileRequest::Pointer CloudProvider::downloadFileAsync(
IItem::Pointer file, IDownloadFileCallback::Pointer callback) {
IItem::Pointer file, IDownloadFileCallback::Pointer callback, Range range) {
return std::make_shared<cloudstorage::DownloadFileRequest>(
shared_from_this(), std::move(file), std::move(callback),
shared_from_this(), std::move(file), std::move(callback), range,
std::bind(&CloudProvider::downloadFileRequest, this, _1, _2))
->run();
}
......@@ -357,7 +357,7 @@ std::pair<std::string, std::string> CloudProvider::credentialsFromString(
ICloudProvider::DownloadFileRequest::Pointer CloudProvider::getThumbnailAsync(
IItem::Pointer item, IDownloadFileCallback::Pointer callback) {
return std::make_shared<cloudstorage::DownloadFileRequest>(
shared_from_this(), item, std::move(callback),
shared_from_this(), item, std::move(callback), FullRange,
std::bind(&CloudProvider::getThumbnailRequest, this, _1, _2))
->run();
}
......@@ -412,7 +412,8 @@ ICloudProvider::DownloadFileRequest::Pointer CloudProvider::downloadFileAsync(
IItem::Pointer item, const std::string& filename,
DownloadFileCallback callback) {
return downloadFileAsync(
item, util::make_unique<::DownloadFileCallback>(filename, callback));
item, util::make_unique<::DownloadFileCallback>(filename, callback),
FullRange);
}
ICloudProvider::DownloadFileRequest::Pointer CloudProvider::getThumbnailAsync(
......
......@@ -63,8 +63,9 @@ class CloudProvider : public ICloudProvider,
IItem::Pointer, IListDirectoryCallback::Pointer) override;
GetItemRequest::Pointer getItemAsync(const std::string& absolute_path,
GetItemCallback) override;
DownloadFileRequest::Pointer downloadFileAsync(
IItem::Pointer, IDownloadFileCallback::Pointer) override;
DownloadFileRequest::Pointer downloadFileAsync(IItem::Pointer,
IDownloadFileCallback::Pointer,
Range) override;
UploadFileRequest::Pointer uploadFileAsync(
IItem::Pointer, const std::string&,
IUploadFileCallback::Pointer) override;
......
......@@ -319,16 +319,16 @@ IHttpServer::IResponse::Pointer MegaNz::HttpServerCallback::handle(
{"Accept-Ranges", "bytes"},
{"Content-Disposition",
"inline; filename=\"" + std::string(node->getName()) + "\""}};
util::range range = {0, node->getSize()};
Range range = {0, (uint64_t)node->getSize()};
if (const char* range_str = request.header("Range")) {
range = util::parse_range(range_str);
if (range.size == -1) range.size = node->getSize() - range.start;
if (range.start + range.size > node->getSize() || range.start == -1 ||
range.size < 0)
if (range.size_ == Range::Full)
range.size_ = node->getSize() - range.start_;
if (range.start_ + range.size_ > (uint64_t)node->getSize())
return util::response_from_string(request, IHttpRequest::RangeInvalid, {},
"invalid range");
std::stringstream stream;
stream << "bytes " << range.start << "-" << range.start + range.size - 1
stream << "bytes " << range.start_ << "-" << range.start_ + range.size_ - 1
<< "/" << node->getSize();
headers["Content-Range"] = stream.str();
code = IHttpRequest::Partial;
......@@ -338,10 +338,10 @@ IHttpServer::IResponse::Pointer MegaNz::HttpServerCallback::handle(
std::weak_ptr<CloudProvider>(provider_->shared_from_this()));
auto resolver = provider_->downloadResolver(
provider_->toItem(node.get()),
util::make_unique<HttpDataCallback>(buffer), range.start, range.size);
util::make_unique<HttpDataCallback>(buffer), range);
provider_->addStreamRequest(download_request);
auto data = util::make_unique<HttpData>(buffer, provider_, download_request);
auto response = request.response(code, headers, range.size, std::move(data));
auto response = request.response(code, headers, range.size_, std::move(data));
buffer->response_ = response.get();
response->completed([buffer]() {
std::unique_lock<std::mutex> lock(buffer->response_mutex_);
......@@ -547,9 +547,9 @@ ICloudProvider::ListDirectoryRequest::Pointer MegaNz::listDirectoryAsync(
}
ICloudProvider::DownloadFileRequest::Pointer MegaNz::downloadFileAsync(
IItem::Pointer item, IDownloadFileCallback::Pointer callback) {
IItem::Pointer item, IDownloadFileCallback::Pointer callback, Range range) {
auto r = std::make_shared<Request<EitherError<void>>>(shared_from_this());
r->set(downloadResolver(item, callback));
r->set(downloadResolver(item, callback, range));
return r->run();
}
......@@ -787,8 +787,7 @@ MegaNz::listDirectoryPageAsync(IItem::Pointer item, const std::string&,
std::function<void(Request<EitherError<void>>::Pointer)>
MegaNz::downloadResolver(IItem::Pointer item,
IDownloadFileCallback::Pointer callback, int64_t start,
int64_t size) {
IDownloadFileCallback::Pointer callback, Range range) {
return [=](Request<EitherError<void>>::Pointer r) {
ensureAuthorized<EitherError<void>>(
r, std::bind(&IDownloadFileCallback::done, callback.get(), _1), [=] {
......@@ -802,8 +801,10 @@ MegaNz::downloadResolver(IItem::Pointer item,
this);
listener->download_callback_ = callback;
r->subrequest(listener);
mega_->startStreaming(node.get(), start,
size == -1 ? node->getSize() - start : size,
mega_->startStreaming(node.get(), range.start_,
range.size_ == Range::Full
? (uint64_t)node->getSize() - range.start_
: range.size_,
listener.get());
});
};
......
......@@ -70,8 +70,9 @@ class MegaNz : public CloudProvider {
const std::string& id, GetItemDataCallback callback) override;
ListDirectoryRequest::Pointer listDirectoryAsync(
IItem::Pointer, IListDirectoryCallback::Pointer) override;
DownloadFileRequest::Pointer downloadFileAsync(
IItem::Pointer, IDownloadFileCallback::Pointer) override;
DownloadFileRequest::Pointer downloadFileAsync(IItem::Pointer,
IDownloadFileCallback::Pointer,
Range) override;
UploadFileRequest::Pointer uploadFileAsync(
IItem::Pointer, const std::string&,
IUploadFileCallback::Pointer) override;
......@@ -90,8 +91,7 @@ class MegaNz : public CloudProvider {
IItem::Pointer, const std::string&, ListDirectoryPageCallback) override;
std::function<void(Request<EitherError<void>>::Pointer)> downloadResolver(
IItem::Pointer item, IDownloadFileCallback::Pointer, int64_t start = 0,
int64_t size = -1);
IItem::Pointer item, IDownloadFileCallback::Pointer, Range);
void login(Request<EitherError<void>>::Pointer,
AuthorizeRequest::AuthorizeCompleted);
......
......@@ -111,7 +111,7 @@ ICloudProvider::GetItemDataRequest::Pointer YandexDisk::getItemDataAsync(
}
ICloudProvider::DownloadFileRequest::Pointer YandexDisk::downloadFileAsync(
IItem::Pointer item, IDownloadFileCallback::Pointer callback) {
IItem::Pointer item, IDownloadFileCallback::Pointer callback, Range range) {
auto r = std::make_shared<Request<EitherError<void>>>(shared_from_this());
r->set([=](Request<EitherError<void>>::Pointer r) {
auto output = std::make_shared<std::stringstream>();
......@@ -135,7 +135,13 @@ ICloudProvider::DownloadFileRequest::Pointer YandexDisk::downloadFileAsync(
auto stream = std::make_shared<std::ostream>(wrapper.get());
std::string url = json["href"].asString();
r->sendRequest(
[=](util::Output) { return http()->create(url, "GET"); },
[=](util::Output) {
auto r = http()->create(url, "GET");
if (range != FullRange)
r->setHeaderParameter("Range",
util::range_to_string(range));
return r;
},
[=](EitherError<util::Output> e) {
(void)wrapper;
if (e.left()) {
......
......@@ -40,8 +40,9 @@ class YandexDisk : public CloudProvider {
GetItemDataRequest::Pointer getItemDataAsync(
const std::string& id, GetItemDataCallback callback) override;
DownloadFileRequest::Pointer downloadFileAsync(
IItem::Pointer, IDownloadFileCallback::Pointer) override;
DownloadFileRequest::Pointer downloadFileAsync(IItem::Pointer,
IDownloadFileCallback::Pointer,
Range) override;
UploadFileRequest::Pointer uploadFileAsync(
IItem::Pointer, const std::string&,
IUploadFileCallback::Pointer) override;
......
......@@ -169,7 +169,7 @@ ICloudProvider::GetItemDataRequest::Pointer YouTube::getItemDataAsync(
}
ICloudProvider::DownloadFileRequest::Pointer YouTube::downloadFileAsync(
IItem::Pointer item, IDownloadFileCallback::Pointer callback) {
IItem::Pointer item, IDownloadFileCallback::Pointer callback, Range range) {
auto r = std::make_shared<Request<EitherError<void>>>(shared_from_this());
r->set([=](Request<EitherError<void>>::Pointer r) {
std::string url = item->url();
......@@ -177,19 +177,25 @@ ICloudProvider::DownloadFileRequest::Pointer YouTube::downloadFileAsync(
auto wrapper = std::make_shared<DownloadStreamWrapper>(std::bind(
&IDownloadFileCallback::receivedData, callback.get(), _1, _2));
auto stream = std::make_shared<std::ostream>(wrapper.get());
r->sendRequest([=](util::Output) { return http()->create(url, "GET"); },
[=](EitherError<util::Output> e) {
(void)wrapper;
if (e.left()) {
callback->done(e.left());
r->done(e.left());
} else {
callback->done(nullptr);
r->done(nullptr);
}
},
stream, std::bind(&IDownloadFileCallback::progress,
callback.get(), _1, _2));
r->sendRequest(
[=](util::Output) {
auto r = http()->create(url, "GET");
if (range != FullRange)
r->setHeaderParameter("Range", util::range_to_string(range));
return r;
},
[=](EitherError<util::Output> e) {
(void)wrapper;
if (e.left()) {
callback->done(e.left());
r->done(e.left());
} else {
callback->done(nullptr);
r->done(nullptr);
}
},
stream,
std::bind(&IDownloadFileCallback::progress, callback.get(), _1, _2));
};
if (item->type() == IItem::FileType::Audio) {
r->subrequest(getItemDataAsync(item->id(), [=](EitherError<IItem> e) {
......
......@@ -45,8 +45,9 @@ class YouTube : public CloudProvider {
IItem::Pointer, const std::string&, ListDirectoryPageCallback) override;
ListDirectoryRequest::Pointer listDirectoryAsync(
IItem::Pointer item, IListDirectoryCallback::Pointer callback) override;
DownloadFileRequest::Pointer downloadFileAsync(
IItem::Pointer, IDownloadFileCallback::Pointer) override;
DownloadFileRequest::Pointer downloadFileAsync(IItem::Pointer,
IDownloadFileCallback::Pointer,
Range) override;
private:
IHttpRequest::Pointer getItemDataRequest(const std::string&,
......
......@@ -244,7 +244,8 @@ class ICloudProvider {
* @return object representing the pending request
*/
virtual DownloadFileRequest::Pointer downloadFileAsync(
IItem::Pointer item, IDownloadFileCallback::Pointer) = 0;
IItem::Pointer item, IDownloadFileCallback::Pointer,
Range = FullRange) = 0;
/**
* Uploads the file provided by callback.
......
......@@ -51,6 +51,17 @@ struct Token {
std::string access_token_;
};
struct Range {
static constexpr uint64_t Begin = 0;
static constexpr uint64_t Full = -1;
uint64_t start_;
uint64_t size_;
};
const Range FullRange = {Range::Begin, Range::Full};
const Range InvalidRange = {-1ULL, 0};
/**
* Class representing pending request. When there is no reference to the
* request, it's immediately cancelled.
......
......@@ -32,6 +32,7 @@ namespace cloudstorage {
DownloadFileRequest::DownloadFileRequest(std::shared_ptr<CloudProvider> p,
IItem::Pointer file,
ICallback::Pointer callback,
Range range,
RequestFactory request_factory)
: Request(p),
stream_wrapper_(
......@@ -39,7 +40,12 @@ DownloadFileRequest::DownloadFileRequest(std::shared_ptr<CloudProvider> p,
set([=](Request::Pointer request) {
auto response_stream = std::make_shared<std::ostream>(&stream_wrapper_);
sendRequest(
[=](util::Output input) { return request_factory(*file, *input); },
[=](util::Output input) {
auto request = request_factory(*file, *input);
if (range != FullRange)
request->setHeaderParameter("Range", util::range_to_string(range));
return request;
},
[=](EitherError<util::Output> e) {
if (e.left()) {
callback->done(e.left());
......
......@@ -46,7 +46,8 @@ class DownloadFileRequest : public Request<EitherError<void>> {
using ICallback = IDownloadFileCallback;
DownloadFileRequest(std::shared_ptr<CloudProvider>, IItem::Pointer file,
ICallback::Pointer, RequestFactory request_factory);
ICallback::Pointer, Range,
RequestFactory request_factory);
~DownloadFileRequest();
private:
......
......@@ -24,6 +24,7 @@
#include "Utility.h"
#include "IItem.h"
#include "IRequest.h"
#include <json/json.h>
#include <algorithm>
......@@ -51,8 +52,6 @@ const std::unordered_map<std::string, std::string> MIME_TYPE = {
{"3gp", "video/3gpp"}, {"3g2", "video/3gpp2"},
{"mp4", "video/mp4"}, {"mkv", "video/webm"}};
namespace util {
namespace {
unsigned char from_hex(unsigned char ch) {
......@@ -74,6 +73,14 @@ std::string to_lower(std::string str) {
} // namespace
bool operator==(const Range& r1, const Range& r2) {
return std::tie(r1.start_, r1.size_) == std::tie(r2.start_, r2.size_);
}
bool operator!=(const Range& r1, const Range& r2) { return !(r1 == r2); }
namespace util {
std::string remove_whitespace(const std::string& str) {
std::string result;
for (char c : str)
......@@ -81,18 +88,25 @@ std::string remove_whitespace(const std::string& str) {
return result;
}
range parse_range(const std::string& r) {
Range parse_range(const std::string& r) {
std::string str = remove_whitespace(r);
size_t l = strlen("bytes=");
if (str.substr(0, l) != "bytes=") return {-1, -1};
if (str.substr(0, l) != "bytes=") return InvalidRange;
std::string n1, n2;
size_t it = l;
while (it < r.length() && str[it] != '-') n1 += str[it++];
it++;
while (it < r.length()) n2 += str[it++];
auto begin = n1.empty() ? -1 : atoll(n1.c_str());
auto end = n2.empty() ? -1 : atoll(n2.c_str());
return {begin, end == -1 ? -1 : (end - begin + 1)};
auto begin = n1.empty() ? 0 : (uint64_t)atoll(n1.c_str());
auto end = n2.empty() ? Range::Full : (uint64_t)atoll(n2.c_str());
return {begin, end == Range::Full ? Range::Full : (end - begin + 1)};
}
std::string range_to_string(Range r) {
std::stringstream stream;
stream << "bytes=" << r.start_ << "-";
if (r.size_ != Range::Full) stream << r.size_ - r.start_ + 1;
return stream.str();
}
std::string address(const std::string& url, uint16_t port) {
......
......@@ -31,6 +31,11 @@
namespace cloudstorage {
struct Range;
bool operator==(const Range&, const Range&);
bool operator!=(const Range&, const Range&);
namespace util {
using Output = std::shared_ptr<std::ostream>;
......@@ -41,12 +46,9 @@ std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
struct range {
int64_t start, size;
};
std::string remove_whitespace(const std::string& str);
range parse_range(const std::string& str);
Range parse_range(const std::string& str);
std::string range_to_string(Range);
std::string address(const std::string& url, uint16_t port);
std::string to_mime_type(const std::string& extension);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment