Commit 847d6068 authored by Paweł Wegner's avatar Paweł Wegner

Request: create stringstream output by default.

parent 0b7cbdb4
......@@ -41,7 +41,6 @@ void move(typename Request<T>::Pointer r, IHttp* http, std::string metadata_url,
std::function<void(EitherError<void>)> complete) {
if (lst->empty()) return complete(nullptr);
auto parent = lst->back();
auto output = std::make_shared<std::stringstream>();
lst->pop_back();
r->sendRequest(
[=](util::Output stream) {
......@@ -54,13 +53,12 @@ void move(typename Request<T>::Pointer r, IHttp* http, std::string metadata_url,
*stream << json;
return request;
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
if (e.left())
complete(e.left());
else
move<T>(r, http, metadata_url, lst, source, destination, complete);
},
output);
});
}
} // namespace
......
......@@ -102,7 +102,6 @@ void rename(typename Request<T>::Pointer r, IHttp* http, std::string region,
ItemId dest_id, ItemId source_id,
std::function<void(EitherError<void>)> complete) {
auto finalize = [=]() {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output) {
return http->create("https://" + source_id.first + ".s3." + region +
......@@ -110,17 +109,15 @@ void rename(typename Request<T>::Pointer r, IHttp* http, std::string region,
escapePath(source_id.second),
"DELETE");
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
if (e.left())
complete(e.left());
else
complete(nullptr);
},
output);
});
};
auto rename_one = [=](std::function<void(EitherError<void>)> complete,
bool directory = true) {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output) {
auto request = http->create(
......@@ -133,13 +130,12 @@ void rename(typename Request<T>::Pointer r, IHttp* http, std::string region,
escapePath("/" + source_id.first + "/" + source_id.second));
return request;
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
if (e.left())
complete(e.left());
else
finalize();
},
output);
});
};
if (source_id.second.empty() || source_id.second.back() == '/') {
auto item = std::make_shared<Item>(
......@@ -290,7 +286,6 @@ ICloudProvider::DeleteItemRequest::Pointer AmazonS3::deleteItemAsync(
r->set(
[=](Request<EitherError<void>>::Pointer r) {
auto release = [=] {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output) {
auto data = extract(item->id());
......@@ -299,10 +294,9 @@ ICloudProvider::DeleteItemRequest::Pointer AmazonS3::deleteItemAsync(
escapePath(data.second),
"DELETE");
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
r->done(e.left() ? e.left() : nullptr);
},
output);
});
};
if (item->type() == IItem::FileType::Directory) {
r->subrequest(r->provider()->listDirectoryAsync(
......@@ -344,42 +338,35 @@ ICloudProvider::GetItemDataRequest::Pointer AmazonS3::getItemDataAsync(
request->setParameter("delimiter", "/");
return request;
};
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
factory,
[=](EitherError<util::Output> e) {
if (e.left()) return r->done(e.left());
std::stringstream sstream;
sstream << output->rdbuf();
tinyxml2::XMLDocument document;
if (document.Parse(sstream.str().c_str(), sstream.str().size()) !=
tinyxml2::XML_SUCCESS)
return r->done(Error{IHttpRequest::Failure, "invalid xml"});
auto node = document.RootElement();
auto size = IItem::UnknownSize;
auto timestamp = IItem::UnknownTimeStamp;
if (auto contents_element = node->FirstChildElement("Contents")) {
if (auto size_element =
contents_element->FirstChildElement("Size"))
if (auto text = size_element->GetText())
size = std::atoll(text);
if (auto time_element =
contents_element->FirstChildElement("LastModified"))
if (auto text = time_element->GetText())
timestamp = util::parse_time(text);
}
auto type = data.second.back() == '/' ? IItem::FileType::Directory
: IItem::FileType::Unknown;
auto item = std::make_shared<Item>(
getFilename(data.second), id,
type == IItem::FileType::Directory ? IItem::UnknownSize
: size,
timestamp, type);
if (item->type() != IItem::FileType::Directory)
item->set_url(getUrl(*item));
r->done(EitherError<IItem>(item));
},
output);
r->sendRequest(factory, [=](EitherError<Response> e) {
if (e.left()) return r->done(e.left());
std::stringstream sstream;
sstream << e.right()->output().rdbuf();
tinyxml2::XMLDocument document;
if (document.Parse(sstream.str().c_str(), sstream.str().size()) !=
tinyxml2::XML_SUCCESS)
return r->done(Error{IHttpRequest::Failure, "invalid xml"});
auto node = document.RootElement();
auto size = IItem::UnknownSize;
auto timestamp = IItem::UnknownTimeStamp;
if (auto contents_element = node->FirstChildElement("Contents")) {
if (auto size_element = contents_element->FirstChildElement("Size"))
if (auto text = size_element->GetText()) size = std::atoll(text);
if (auto time_element =
contents_element->FirstChildElement("LastModified"))
if (auto text = time_element->GetText())
timestamp = util::parse_time(text);
}
auto type = data.second.back() == '/' ? IItem::FileType::Directory
: IItem::FileType::Unknown;
auto item = std::make_shared<Item>(
getFilename(data.second), id,
type == IItem::FileType::Directory ? IItem::UnknownSize : size,
timestamp, type);
if (item->type() != IItem::FileType::Directory)
item->set_url(getUrl(*item));
r->done(EitherError<IItem>(item));
});
},
callback);
return r->run();
......
......@@ -68,35 +68,33 @@ ICloudProvider::GetItemDataRequest::Pointer Box::getItemDataAsync(
auto r = std::make_shared<Request<EitherError<IItem>>>(shared_from_this());
r->set(
[=](Request<EitherError<IItem>>::Pointer r) {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[this, id](util::Output) {
return http()->create(endpoint() + "/2.0/files/" + id, "GET");
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
if (e.left()) {
r->sendRequest(
[this, id](util::Output) {
return http()->create(endpoint() + "/2.0/folders/" + id,
"GET");
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
if (e.left())
r->done(e.left());
else {
try {
Json::Value response;
*output >> response;
e.right()->output() >> response;
r->done(toItem(response));
} catch (std::exception) {
r->done(Error{IHttpRequest::Failure, output->str()});
r->done(Error{IHttpRequest::Failure,
e.right()->output().str()});
}
}
},
output);
});
}
},
output);
});
},
callback);
return r->run();
......
......@@ -39,7 +39,6 @@ namespace cloudstorage {
namespace {
void upload(Request<EitherError<IItem>>::Pointer r, int sent,
IUploadFileCallback* callback, Json::Value response) {
auto output = std::make_shared<std::stringstream>();
int size = callback->size();
auto length = std::make_shared<int>(0);
if (sent >= size)
......@@ -58,17 +57,17 @@ void upload(Request<EitherError<IItem>>::Pointer r, int sent,
stream->write(buffer.data(), *length);
return request;
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
if (e.left()) return r->done(e.left());
try {
Json::Value json;
*output >> json;
e.right()->output() >> json;
upload(r, sent + *length, callback, json);
} catch (std::exception) {
r->done(Error{IHttpRequest::Failure, output->str()});
r->done(Error{IHttpRequest::Failure, e.right()->output().str()});
}
},
output, nullptr,
nullptr, nullptr,
[=](uint32_t, uint32_t now) { callback->progress(size, sent + now); });
}
} // namespace
......@@ -86,7 +85,6 @@ ICloudProvider::UploadFileRequest::Pointer OneDrive::uploadFileAsync(
auto callback = cb.get();
r->set(
[=](Request<EitherError<IItem>>::Pointer r) {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output) {
return http()->create(
......@@ -94,17 +92,17 @@ ICloudProvider::UploadFileRequest::Pointer OneDrive::uploadFileAsync(
util::Url::escape(filename) + ":/upload.createSession",
"POST");
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
if (e.left()) return r->done(e.left());
try {
Json::Value response;
*output >> response;
e.right()->output() >> response;
upload(r, 0, callback, response);
} catch (std::exception) {
r->done(Error{IHttpRequest::Failure, output->str()});
r->done(
Error{IHttpRequest::Failure, e.right()->output().str()});
}
},
output);
});
},
[=](EitherError<IItem> e) { cb->done(e); });
return r->run();
......
......@@ -93,7 +93,6 @@ ICloudProvider::UploadFileRequest::Pointer YandexDisk::uploadFileAsync(
path += filename;
auto upload_url = [=](Request<EitherError<IItem>>::Pointer r,
std::function<void(EitherError<std::string>)> f) {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output) {
auto request =
......@@ -101,24 +100,22 @@ ICloudProvider::UploadFileRequest::Pointer YandexDisk::uploadFileAsync(
request->setParameter("path", path);
return request;
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
if (e.left()) f(e.left());
try {
Json::Value response;
*output >> response;
e.right()->output() >> response;
f(response["href"].asString());
} catch (std::exception) {
f(Error{IHttpRequest::Failure, output->str()});
f(Error{IHttpRequest::Failure, e.right()->output().str()});
}
},
output);
});
};
auto upload = [=](Request<EitherError<IItem>>::Pointer r, std::string url,
std::function<void(EitherError<IItem>)> f) {
auto wrapper = std::make_shared<UploadStreamWrapper>(
std::bind(&IUploadFileCallback::putData, callback.get(), _1, _2),
callback->size());
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output input) {
auto request = http()->create(url, "PUT");
......@@ -127,14 +124,14 @@ ICloudProvider::UploadFileRequest::Pointer YandexDisk::uploadFileAsync(
input->rdbuf(wrapper.get());
return request;
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
if (e.left()) return f(e.left());
IItem::Pointer item = util::make_unique<Item>(
filename, path, wrapper->size_, std::chrono::system_clock::now(),
IItem::FileType::Unknown);
f(item);
},
output, nullptr,
nullptr, nullptr,
std::bind(&IUploadFileCallback::progress, callback.get(), _1, _2));
};
r->set(
......
......@@ -140,19 +140,19 @@ ICloudProvider::GetItemDataRequest::Pointer YouTube::getItemDataAsync(
IItem::UnknownTimeStamp, IItem::FileType::Directory);
return r->done(i);
}
auto response_stream = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output input) { return getItemDataRequest(id, *input); },
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
if (e.left()) return r->done(e.left());
auto id_data = from_string(id);
try {
r->done(getItemDataResponse(*response_stream, id_data.audio));
r->done(
getItemDataResponse(e.right()->output(), id_data.audio));
} catch (std::exception) {
r->done(Error{IHttpRequest::Failure, response_stream->str()});
r->done(
Error{IHttpRequest::Failure, e.right()->output().str()});
}
},
response_stream);
});
},
callback);
return r->run();
......
......@@ -39,21 +39,20 @@ CreateDirectoryRequest::CreateDirectoryRequest(std::shared_ptr<CloudProvider> p,
callback(e);
return done(e);
}
auto output = std::make_shared<std::stringstream>();
sendRequest(
[=](util::Output stream) {
return provider()->createDirectoryRequest(*parent, name, *stream);
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
if (e.left()) return request->done(e.left());
try {
request->done(provider()->createDirectoryResponse(*parent, name,
*output));
request->done(provider()->createDirectoryResponse(
*parent, name, e.right()->output()));
} catch (std::exception) {
request->done(Error{IHttpRequest::Failure, output->str()});
request->done(
Error{IHttpRequest::Failure, e.right()->output().str()});
}
},
output);
});
},
callback);
}
......
......@@ -33,18 +33,16 @@ DeleteItemRequest::DeleteItemRequest(std::shared_ptr<CloudProvider> p,
: Request(p) {
set(
[=](Request::Pointer request) {
auto output = std::make_shared<std::stringstream>();
sendRequest(
[=](util::Output stream) {
return provider()->deleteItemRequest(*item, *stream);
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
if (e.left())
request->done(e.left());
else
request->done(nullptr);
},
output);
});
},
callback);
}
......
......@@ -48,7 +48,7 @@ DownloadFileRequest::DownloadFileRequest(std::shared_ptr<CloudProvider> p,
util::range_to_string(range));
return request;
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
if (e.left())
request->done(e.left());
else
......@@ -91,7 +91,7 @@ DownloadFileFromUrlRequest::DownloadFileFromUrlRequest(
r->setHeaderParameter("Range", util::range_to_string(range));
return r;
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
if (e.left())
cb(e.left());
else
......
......@@ -33,22 +33,20 @@ GetItemDataRequest::GetItemDataRequest(std::shared_ptr<CloudProvider> p,
: Request(p) {
set(
[=](Request::Pointer request) {
auto response_stream = std::make_shared<std::stringstream>();
sendRequest(
[=](util::Output input) {
return provider()->getItemDataRequest(id, *input);
},
[=](EitherError<util::Output> r) {
[=](EitherError<Response> r) {
if (r.left()) return request->done(r.left());
try {
request->done(
provider()->getItemDataResponse(*response_stream));
provider()->getItemDataResponse(r.right()->output()));
} catch (std::exception) {
request->done(
Error{IHttpRequest::Failure, response_stream->str()});
Error{IHttpRequest::Failure, r.right()->output().str()});
}
},
response_stream);
});
},
callback);
}
......
......@@ -36,8 +36,8 @@ GetItemUrlRequest::GetItemUrlRequest(std::shared_ptr<CloudProvider> p,
if (item->type() == IItem::FileType::Directory)
return r->done(Error{IHttpRequest::ServiceUnavailable,
"url not provided for directory"});
if (!provider()->getItemUrlRequest(
*item, *std::make_shared<std::stringstream>())) {
std::stringstream dummy;
if (!provider()->getItemUrlRequest(*item, dummy)) {
auto url = static_cast<Item*>(item.get())->url();
if (url.empty())
return r->done(
......@@ -45,25 +45,25 @@ GetItemUrlRequest::GetItemUrlRequest(std::shared_ptr<CloudProvider> p,
else
return r->done(url);
}
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output input) {
return provider()->getItemUrlRequest(*item, *input);
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
try {
if (e.left())
r->done(e.left());
else {
auto url = provider()->getItemUrlResponse(*item, *output);
auto url = provider()->getItemUrlResponse(
*item, e.right()->output());
static_cast<Item*>(item.get())->set_url(url);
r->done(url);
}
} catch (std::exception) {
r->done(Error{IHttpRequest::Failure, output->str()});
r->done(
Error{IHttpRequest::Failure, e.right()->output().str()});
}
},
output);
});
},
callback);
}
......
......@@ -36,25 +36,28 @@ ListDirectoryPageRequest::ListDirectoryPageRequest(
[=](Request<EitherError<PageData>>::Pointer r) {
if (directory->type() != IItem::FileType::Directory)
return r->done(Error{IHttpRequest::Bad, "file not a directory"});
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output input) {
return r->provider()->listDirectoryRequest(*directory, token,
*input);
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
if (e.left()) {
if (!fault_tolerant(e.left()->code_))
return r->done(e.left());
else
return r->done(PageData{{}, ""});
}
std::string next_token;
auto lst = r->provider()->listDirectoryResponse(
*directory, *output, next_token);
r->done(PageData{lst, next_token});
},
output);
try {
std::string next_token;
auto lst = r->provider()->listDirectoryResponse(
*directory, e.right()->output(), next_token);
r->done(PageData{lst, next_token});
} catch (std::exception) {
r->done(
Error{IHttpRequest::Failure, e.right()->output().str()});
}
});
},
completed);
}
......
......@@ -50,18 +50,17 @@ ListDirectoryRequest::~ListDirectoryRequest() { cancel(); }
void ListDirectoryRequest::work(IItem::Pointer directory,
std::string page_token, ICallback* callback,
std::function<bool(int)> fault_tolerant) {
auto output_stream = std::make_shared<std::stringstream>();
auto request = this->shared_from_this();
sendRequest(
[=](util::Output i) {
return provider()->listDirectoryRequest(*directory, page_token, *i);
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
try {
if (e.right() || fault_tolerant(e.left()->code_)) {
std::string page_token = "";
for (auto& t : request->provider()->listDirectoryResponse(
*directory, *output_stream, page_token)) {
*directory, e.right()->output(), page_token)) {
callback->receivedItem(t);
result_.push_back(t);
}
......@@ -74,10 +73,10 @@ void ListDirectoryRequest::work(IItem::Pointer directory,
request->done(e.left());
}
} catch (std::exception) {
request->done(Error{IHttpRequest::Failure, output_stream->str()});
request->done(
Error{IHttpRequest::Failure, e.right()->output().str()});
}
},
output_stream);
});
}
} // namespace cloudstorage
......@@ -37,21 +37,20 @@ MoveItemRequest::MoveItemRequest(std::shared_ptr<CloudProvider> p,
if (destination->type() != IItem::FileType::Directory)
return request->done(
Error{IHttpRequest::Forbidden, "destination not a directory"});
auto output = std::make_shared<std::stringstream>();
sendRequest(
[=](util::Output stream) {
return p->moveItemRequest(*source, *destination, *stream);
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
if (e.left()) return request->done(e.left());
try {
request->done(
p->moveItemResponse(*source, *destination, *output));
request->done(p->moveItemResponse(*source, *destination,
e.right()->output()));
} catch (std::exception) {
request->done(Error{IHttpRequest::Failure, output->str()});
request->done(
Error{IHttpRequest::Failure, e.right()->output().str()});
}
},
output);
});
},
callback);
}
......
......@@ -34,20 +34,20 @@ RenameItemRequest::RenameItemRequest(std::shared_ptr<CloudProvider> p,
: Request(p) {
set(
[=](Request::Pointer request) {
auto output = std::make_shared<std::stringstream>();
sendRequest(
[=](util::Output stream) {
return p->renameItemRequest(*item, name, *stream);
},
[=](EitherError<util::Output> e) {
[=](EitherError<Response> e) {
if (e.left()) return request->done(e.left());
try {
request->done(p->renameItemResponse(*item, name, *output));
} catch (std::exception e) {
request->done(Error{IHttpRequest::Failure, output->str()});
request->done(
p->renameItemResponse(*item, name, e.right()->output()));
} catch (std::exception) {
request->done(
Error{IHttpRequest::Failure, e.right()->output().str()});
}
},
output);
});
},
callback);
}
......
......@@ -34,6 +34,12 @@ using namespace std::placeholders;
namespace cloudstorage {
Response::Response(IHttpRequest::Response r) : http_(r) {}
std::stringstream& Response::output() {
return static_cast<std::stringstream&>(*http_.output_stream_.get());
}
template <class T>
Request<T>::Wrapper::Wrapper(typename Request<T>::Pointer r) : request_(r) {}
......@@ -203,12 +209,13 @@ void Request<T>::sendRequest(RequestFactory factory, RequestCompleted complete,
auto request = this->shared_from_this();
auto input = std::make_shared<std::stringstream>(),
error_stream = std::make_shared<std::stringstream>();
if (!output) output = std::make_shared<std::stringstream>();
auto r = factory(input);
authorize(r);
send(r.get(),
[=](IHttpRequest::Response response) {
if (provider()->isSuccess(response.http_code_, response.headers_))
return complete(output);
return complete(Response(response));
if (this->reauthorize(response.http_code_, response.headers_)) {
this->reauthorize([=](EitherError<void> e) {
if (e.left()) {
......@@ -228,7 +235,7 @@ void Request<T>::sendRequest(RequestFactory factory, RequestCompleted complete,
(void)request;
if (provider()->isSuccess(response.http_code_,
response.headers_))
complete(output);
complete(Response(response));
else
complete(Error{response.http_code_, error_stream->str()});
},
......
......@@ -39,6 +39,16 @@ class CloudProvider;
class HttpCallback;
class IItem;
class Response {
public:
Response(IHttpRequest::Response);
std::stringstream& output();
private:
IHttpRequest::Response http_;
};
template <class ReturnValue>
class Request : public IRequest<ReturnValue>,
public std::enable_shared_from_this<Request<ReturnValue>> {
......@@ -50,7 +60,7 @@ class Request : public IRequest<ReturnValue>,
using Callback = std::function<void(ReturnValue)>;
using Resolver = std::function<void(std::shared_ptr<Request>)>;
using AuthorizeCompleted = std::function<void(EitherError<void>)>;
using RequestCompleted = std::function<void(EitherError<util::Output>)>;
using RequestCompleted = std::function<void(EitherError<Response>)>;
class Wrapper : public IRequest<ReturnValue> {
public:
......@@ -97,7 +107,7 @@ class Request : public IRequest<ReturnValue>,
* @return http code or curl error code
*/
void sendRequest(RequestFactory factory, RequestCompleted,
std::shared_ptr<std::ostream> output,
std::shared_ptr<std::ostream> output = nullptr,
ProgressFunction download = nullptr,
ProgressFunction upload = nullptr);
......
......@@ -39,7 +39,6 @@ UploadFileRequest::UploadFileRequest(std::shared_ptr<CloudProvider> p,
auto callback = cb.get();
set(
[=](Request::Pointer request) {
auto output = std::make_shared<std::stringstream>();
sendRequest(
[=](util::Output input) {
callback->reset();
......@@ -49,17 +48,19 @@ UploadFileRequest::UploadFileRequest(std::shared_ptr<CloudProvider> p,