sdk/src/PdsClientImpl.cc (779 lines of code) (raw):
/*
* Copyright 2009-2021 Alibaba Cloud All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <ctime>
#include <algorithm>
#include <sstream>
#include <set>
#include <tinyxml2/tinyxml2.h>
#include <alibabacloud/pds/http/HttpType.h>
#include <alibabacloud/pds/Const.h>
#include <fstream>
#include "utils/Utils.h"
#include "utils/ThreadExecutor.h"
#include "PdsClientImpl.h"
#include "utils/LogUtils.h"
#include "utils/FileSystemUtils.h"
#include "resumable/ResumableUploader.h"
#include "resumable/ResumableDownloader.h"
using namespace AlibabaCloud::PDS;
using namespace tinyxml2;
namespace
{
const std::string SERVICE_NAME = "PDS";
const char *TAG = "PdsClientImpl";
}
PdsClientImpl::PdsClientImpl(const std::string &endpoint, const std::shared_ptr<CredentialsProvider>& credentialsProvider, const ClientConfiguration & configuration) :
Client(SERVICE_NAME, configuration),
endpoint_(endpoint),
credentialsProvider_(credentialsProvider),
executor_(configuration.executor ? configuration.executor :std::make_shared<ThreadExecutor>()),
isValidEndpoint_(IsValidEndpoint(endpoint))
{
}
PdsClientImpl::~PdsClientImpl()
{
}
int PdsClientImpl::asyncExecute(Runnable * r) const
{
if (executor_ == nullptr)
return 1;
executor_->execute(r);
return 0;
}
std::shared_ptr<HttpRequest> PdsClientImpl::buildHttpRequest(const std::string & endpoint, const ServiceRequest & msg, Http::Method method) const
{
auto httpRequest = std::make_shared<HttpRequest>(method);
auto calcContentMD5 = !!(msg.Flags()&REQUEST_FLAG_CONTENTMD5);
auto paramInPath = !!(msg.Flags()&REQUEST_FLAG_PARAM_IN_PATH);
auto pdsDataRequest = !!(msg.Flags()&REQUEST_FLAG_PDS_DATA_REQUEST);
httpRequest->setResponseStreamFactory(msg.ResponseStreamFactory());
addHeaders(httpRequest, msg.Headers());
if (!pdsDataRequest) {
addPdsHeaders(httpRequest);
}
addBody(httpRequest, msg.Body(), calcContentMD5);
if (paramInPath) {
httpRequest->setUrl(Url(msg.Path()));
}
else {
addUrl(httpRequest, endpoint, msg);
}
addOther(httpRequest, msg);
return httpRequest;
}
bool PdsClientImpl::hasResponseError(const std::shared_ptr<HttpResponse>&response) const
{
if (BASE::hasResponseError(response)) {
return true;
}
//check crc64
if (response->request().hasCheckCrc64() &&
!response->request().hasHeader(Http::RANGE) &&
response->hasHeader("x-oss-hash-crc64ecma")) {
uint64_t clientCrc64 = response->request().Crc64Result();
uint64_t serverCrc64 = std::strtoull(response->Header("x-oss-hash-crc64ecma").c_str(), nullptr, 10);
if (clientCrc64 != serverCrc64) {
response->setStatusCode(ERROR_CRC_INCONSISTENT);
std::stringstream ss;
ss << "Crc64 validation failed. Expected hash:" << serverCrc64
<< " not equal to calculated hash:" << clientCrc64
<< ". Transferd bytes:" << response->request().TransferedBytes()
<< ". RequestId:" << response->Header("x-oss-request-id").c_str();
response->setStatusMsg(ss.str().c_str());
return true;
}
}
//check Calback
if (response->statusCode() == 203 &&
(response->request().hasHeader("x-oss-callback") ||
(response->request().url().query().find("callback=") != std::string::npos))) {
return true;
}
return false;
}
void PdsClientImpl::addHeaders(const std::shared_ptr<HttpRequest> &httpRequest, const HeaderCollection &headers) const
{
for (auto const& header : headers) {
httpRequest->addHeader(header.first, header.second);
}
//common headers
httpRequest->addHeader(Http::USER_AGENT, configuration().userAgent);
//Date
if (!httpRequest->hasHeader(Http::DATE)) {
std::time_t t = std::time(nullptr);
httpRequest->addHeader(Http::DATE, ToGmtTime(t));
}
}
void PdsClientImpl::addPdsHeaders(const std::shared_ptr<HttpRequest> &httpRequest) const
{
const Credentials credentials = credentialsProvider_->getCredentials();
if (!credentials.AccessToken().empty()) {
httpRequest->addHeader(Http::AUTHORIZATION, credentials.AccessToken());
}
if (!httpRequest->hasHeader(Http::CONTENT_TYPE)) {
httpRequest->addHeader(Http::CONTENT_TYPE, "application/json");
}
PDS_LOG(LogLevel::LogDebug, TAG, "client(%p) request(%p) Authorization:%s", this, httpRequest.get(), credentials.AccessToken().c_str());
}
void PdsClientImpl::addBody(const std::shared_ptr<HttpRequest> &httpRequest, const std::shared_ptr<std::iostream>& body, bool contentMd5) const
{
if (body == nullptr) {
Http::Method methold = httpRequest->method();
if (methold == Http::Method::Get || methold == Http::Method::Post) {
httpRequest->setHeader(Http::CONTENT_LENGTH, "0");
} else {
httpRequest->removeHeader(Http::CONTENT_LENGTH);
}
}
if ((body != nullptr) && !httpRequest->hasHeader(Http::CONTENT_LENGTH)) {
auto streamSize = GetIOStreamLength(*body);
httpRequest->setHeader(Http::CONTENT_LENGTH, std::to_string(streamSize));
}
if (contentMd5 && body && !httpRequest->hasHeader(Http::CONTENT_MD5)) {
auto md5 = ComputeContentMD5(*body);
httpRequest->setHeader(Http::CONTENT_MD5, md5);
}
httpRequest->addBody(body);
}
void PdsClientImpl::addUrl(const std::shared_ptr<HttpRequest> &httpRequest, const std::string &endpoint, const ServiceRequest &request) const
{
auto host = GetHostString(endpoint);
auto path = request.Path();
Url url(host);
url.setPath(path);
PDS_LOG(LogLevel::LogDebug, TAG, "client(%p) request(%p) host:%s, path:%s", this, httpRequest.get(), host.c_str(), path.c_str());
auto parameters = request.Parameters();
if (!parameters.empty()) {
std::stringstream queryString;
for (const auto &p : parameters)
{
if (p.second.empty())
queryString << "&" << UrlEncode(p.first);
else
queryString << "&" << UrlEncode(p.first) << "=" << UrlEncode(p.second);
}
url.setQuery(queryString.str().substr(1));
}
httpRequest->setUrl(url);
}
void PdsClientImpl::addOther(const std::shared_ptr<HttpRequest> &httpRequest, const ServiceRequest &request) const
{
//progress
httpRequest->setTransferProgress(request.TransferProgress());
// progress control
httpRequest->setProgressControl(request.ProgressControl());
//crc64 check
auto checkCRC64 = !!(request.Flags()&REQUEST_FLAG_CHECK_CRC64);
if (configuration().enableCrc64 && checkCRC64 ) {
httpRequest->setCheckCrc64(true);
#ifdef ENABLE_PDS_TEST
if (!!(request.Flags()&0x80000000)) {
httpRequest->addHeader("oss-test-crc64", "1");
}
#endif
}
}
PdsError PdsClientImpl::buildError(const Error &error) const
{
PdsError err;
if (((error.Status() == 203) || (error.Status() > 299 && error.Status() < 600)) &&
!error.Message().empty()) {
std::string contentType;
auto it = error.Headers().find("Content-Type");
if (it != error.Headers().end()) {
contentType = it->second;
}
if (contentType.find("application/json") != contentType.npos) {
Json::Value root;
Json::CharReaderBuilder rbuilder;
std::string errMsg;
std::stringstream msg;
msg << error.Message();
if (Json::parseFromStream(rbuilder, msg, &root, &errMsg))
{
err.setCode(root["code"].asString());
err.setMessage(root["message"].asString());
} else {
std::stringstream ss;
ss << "ParseJSONError:" << errMsg;
err.setCode("ParseJSONError");
err.setMessage(ss.str());
}
} else if (contentType.find("application/xml") != contentType.npos) {
XMLDocument doc;
XMLError xml_err;
if ((xml_err = doc.Parse(error.Message().c_str(), error.Message().size())) == XML_SUCCESS) {
XMLElement* root =doc.RootElement();
if (root && !std::strncmp("Error", root->Name(), 5)) {
XMLElement *node;
node = root->FirstChildElement("Code");
err.setCode(node ? node->GetText(): "");
node = root->FirstChildElement("Message");
err.setMessage(node ? node->GetText(): "");
node = root->FirstChildElement("RequestId");
err.setRequestId(node ? node->GetText(): "");
node = root->FirstChildElement("HostId");
err.setHost(node ? node->GetText(): "");
} else {
err.setCode("ParseXMLError");
err.setMessage("Xml format invalid, root node name is not Error. the content is:\n" + error.Message());
}
} else {
std::stringstream ss;
ss << "ParseXMLError:" << xml_err;
err.setCode(ss.str());
err.setMessage(XMLDocument::ErrorIDToName(xml_err));
}
} else {
err.setCode(error.Code());
err.setMessage(error.Message());
}
}
else {
err.setCode(error.Code());
err.setMessage(error.Message());
}
//get from header if body has nothing
if (err.RequestId().empty()) {
auto it = error.Headers().find("x-ca-request-id");
if (it != error.Headers().end()) {
err.setRequestId(it->second);
}
}
return err;
}
ServiceResult PdsClientImpl::buildResult(const PdsRequest &request, const std::shared_ptr<HttpResponse> &httpResponse) const
{
ServiceResult result;
auto flag = request.Flags();
if ((flag & REQUEST_FLAG_CHECK_CRC64) &&
(flag & REQUEST_FLAG_SAVE_CLIENT_CRC64)) {
httpResponse->addHeader("x-oss-hash-crc64ecma-by-client", std::to_string(httpResponse->request().Crc64Result()));
}
result.setRequestId(httpResponse->Header("x-ca-request-id"));
result.setPayload(httpResponse->Body());
result.setResponseCode(httpResponse->statusCode());
result.setHeaderCollection(httpResponse->Headers());
return result;
}
PdsOutcome PdsClientImpl::MakeRequest(const PdsRequest &request, Http::Method method) const
{
int ret = request.validate();
if (ret != 0) {
return PdsOutcome(PdsError("ValidateError", request.validateMessage(ret)));
}
if (!isValidEndpoint_) {
return PdsOutcome(PdsError("ValidateError", "The endpoint is invalid."));
}
auto outcome = BASE::AttemptRequest(endpoint_, request, method);
if (outcome.isSuccess()) {
return PdsOutcome(buildResult(request, outcome.result()));
} else {
return PdsOutcome(buildError(outcome.error()));
}
}
////////////////////
// AsyncTask
////////////////////
AsyncTaskGetOutcome PdsClientImpl::AsyncTaskGet(const AsyncTaskGetRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return AsyncTaskGetOutcome(AsyncTaskGetResult(outcome.result().payload()));
}
else {
return AsyncTaskGetOutcome(outcome.error());
}
}
////////////////////
// Dir
////////////////////
DirCreateOutcome PdsClientImpl::DirCreate(const DirCreateRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return DirCreateOutcome(DirCreateResult(outcome.result().payload()));
}
else {
return DirCreateOutcome(outcome.error());
}
}
DirListOutcome PdsClientImpl::DirList(const DirListRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return DirListOutcome(DirListResult(outcome.result().payload()));
}
else {
return DirListOutcome(outcome.error());
}
}
DirSearchOutcome PdsClientImpl::DirSearch(const DirSearchRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return DirSearchOutcome(DirSearchResult(outcome.result().payload()));
}
else {
return DirSearchOutcome(outcome.error());
}
}
DirRenameOutcome PdsClientImpl::DirRename(const DirRenameRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return DirRenameOutcome(DirRenameResult(outcome.result().payload()));
}
else {
return DirRenameOutcome(outcome.error());
}
}
DirTrashOutcome PdsClientImpl::DirTrash(const DirTrashRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return DirTrashOutcome(DirTrashResult(outcome.result().payload()));
}
else {
return DirTrashOutcome(outcome.error());
}
}
DirDeleteOutcome PdsClientImpl::DirDelete(const DirDeleteRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return DirDeleteOutcome(DirDeleteResult(outcome.result().payload()));
}
else {
return DirDeleteOutcome(outcome.error());
}
}
DirCopyOutcome PdsClientImpl::DirCopy(const DirCopyRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return DirCopyOutcome(DirCopyResult(outcome.result().payload()));
}
else {
return DirCopyOutcome(outcome.error());
}
}
DirMoveOutcome PdsClientImpl::DirMove(const DirMoveRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return DirMoveOutcome(DirMoveResult(outcome.result().payload()));
}
else {
return DirMoveOutcome(outcome.error());
}
}
DirHiddenOutcome PdsClientImpl::DirHidden(const DirHiddenRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return DirHiddenOutcome(DirHiddenResult(outcome.result().payload()));
}
else {
return DirHiddenOutcome(outcome.error());
}
}
////////////////////
// File
////////////////////
FileCreateOutcome PdsClientImpl::FileCreate(const FileCreateRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return FileCreateOutcome(FileCreateResult(outcome.result().payload()));
}
else {
return FileCreateOutcome(outcome.error());
}
}
FileGetUploadUrlOutcome PdsClientImpl::FileGetUploadUrl(const FileGetUploadUrlRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return FileGetUploadUrlOutcome(FileGetUploadUrlResult(outcome.result().payload()));
}
else {
return FileGetUploadUrlOutcome(outcome.error());
}
}
FileListUploadedPartsOutcome PdsClientImpl::FileListUploadedParts(const FileListUploadedPartsRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return FileListUploadedPartsOutcome(FileListUploadedPartsResult(outcome.result().payload()));
}
else {
return FileListUploadedPartsOutcome(outcome.error());
}
}
FileCompleteOutcome PdsClientImpl::FileComplete(const FileCompleteRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return FileCompleteOutcome(FileCompleteResult(outcome.result().payload()));
}
else {
return FileCompleteOutcome(outcome.error());
}
}
FileGetDownloadUrlOutcome PdsClientImpl::FileGetDownloadUrl(const FileGetDownloadUrlRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return FileGetDownloadUrlOutcome(FileGetDownloadUrlResult(outcome.result().payload()));
}
else {
return FileGetDownloadUrlOutcome(outcome.error());
}
}
FileGetOutcome PdsClientImpl::FileGet(const FileGetRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return FileGetOutcome(FileGetResult(outcome.result().payload()));
}
else {
return FileGetOutcome(outcome.error());
}
}
FileRenameOutcome PdsClientImpl::FileRename(const FileRenameRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return FileRenameOutcome(FileRenameResult(outcome.result().payload()));
}
else {
return FileRenameOutcome(outcome.error());
}
}
FileTrashOutcome PdsClientImpl::FileTrash(const FileTrashRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return FileTrashOutcome(FileTrashResult());
}
else {
return FileTrashOutcome(outcome.error());
}
}
FileDeleteOutcome PdsClientImpl::FileDelete(const FileDeleteRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return FileDeleteOutcome(FileDeleteResult());
}
else {
return FileDeleteOutcome(outcome.error());
}
}
FileGetVideoPreviewPlayInfoOutcome PdsClientImpl::FileGetVideoPreviewPlayInfo(const FileGetVideoPreviewPlayInfoRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return FileGetVideoPreviewPlayInfoOutcome(outcome.result().payload());
}
else {
return FileGetVideoPreviewPlayInfoOutcome(outcome.error());
}
}
FileCopyOutcome PdsClientImpl::FileCopy(const FileCopyRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return FileCopyOutcome(FileCopyResult(outcome.result().payload()));
}
else {
return FileCopyOutcome(outcome.error());
}
}
FileMoveOutcome PdsClientImpl::FileMove(const FileMoveRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return FileMoveOutcome(FileMoveResult(outcome.result().payload()));
}
else {
return FileMoveOutcome(outcome.error());
}
}
FileHiddenOutcome PdsClientImpl::FileHidden(const FileHiddenRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return FileHiddenOutcome(FileHiddenResult(outcome.result().payload()));
}
else {
return FileHiddenOutcome(outcome.error());
}
}
////////////////////
// Data
////////////////////
DataGetOutcome PdsClientImpl::DataGetByUrl(const DataGetByUrlRequest &request) const
{
auto outcome = MakeRequest(request, Http::Method::Get);
if (outcome.isSuccess()) {
return DataGetOutcome(DataGetResult(outcome.result().payload(), outcome.result().headerCollection()));
}
else {
return DataGetOutcome(outcome.error());
}
}
DataPutOutcome PdsClientImpl::DataPutByUrl(const DataPutByUrlRequest &request) const
{
auto outcome = AttemptRequest(endpoint_, request, Http::Method::Put);
if (outcome.isSuccess()) {
return DataPutOutcome(DataPutResult(outcome.result()->Headers(),
outcome.result()->Body()));
}
else {
return DataPutOutcome(buildError(outcome.error()));
}
}
////////////////////
// Meta
////////////////////
MetaUserTagsPutOutcome PdsClientImpl::MetaUserTagsPut(const MetaUserTagsPutRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
if (outcome.isSuccess()) {
return MetaUserTagsPutOutcome(MetaUserTagsPutResult(outcome.result().payload()));
}
else {
return MetaUserTagsPutOutcome(outcome.error());
}
}
PdsOutcome PdsClientImpl::MetaUserTagsDelete(const MetaUserTagsDeleteRequest& request) const
{
auto outcome = MakeRequest(request, Http::Method::Post);
return outcome;
}
///////////////////////////
// Resumable Operation
///////////////////////////
FileCompleteOutcome PdsClientImpl::ResumableFileUpload(const FileUploadRequest &request) const
{
const auto& reqeustBase = static_cast<const PdsResumableBaseRequest &>(request);
int code = reqeustBase.validate();
if (code != 0) {
return FileCompleteOutcome(PdsError("ValidateError", reqeustBase.validateMessage(code)));
}
if (request.FileSize() <= request.PartSize())
{
PartInfoReqList partInfoReqList;
PartInfoReq info(1, request.FileSize(), 0, request.FileSize()-1);
partInfoReqList.push_back(info);
FileCreateResult fileCreateResult;
// rapid upload
if (configuration().enableRapidUpload) {
// caculate pre hash
uint64_t preHashBlockSize = 1024;
if (preHashBlockSize > request.FileSize()) {
preHashBlockSize = request.FileSize();
}
auto content = GetFstreamByPath(request.FilePath(), request.FilePathW(),
std::ios::in | std::ios::binary);
char streamBuffer[1024];
uint64_t readSize = 0;
if (!content->good()) {
return FileCompleteOutcome(PdsError("ReadFileError", "Failed to read input file"));
}
content->read(streamBuffer, preHashBlockSize);
readSize = static_cast<uint64_t>(content->gcount());
content->close();
if (readSize != preHashBlockSize) {
return FileCompleteOutcome(PdsError("ReadFileError", "Failed to read enough size for caculate pre hash"));
}
std::string preHashSHA1 = ComputeContentSha1(streamBuffer, preHashBlockSize);
// check pre hash request
auto fileCreatePreCheckReq = FileCreateRequest(request.DriveID(), request.ParentFileID(), request.Name(),
request.FileID(), request.CheckNameMode(), request.FileSize());
fileCreatePreCheckReq.setPreHash(preHashSHA1);
fileCreatePreCheckReq.setPartInfoList(partInfoReqList);
fileCreatePreCheckReq.setUserTags(request.UserTags());
fileCreatePreCheckReq.setHidden(request.Hidden());
auto fileCreatePreCheckOutcome = FileCreate(fileCreatePreCheckReq);
if (!fileCreatePreCheckOutcome.isSuccess()) {
// check pre hash error
if (fileCreatePreCheckOutcome.error().Code() != "PreHashMatched") {
return FileCompleteOutcome(fileCreatePreCheckOutcome.error());
}
// check pre hash matched, rapid upload
auto content = GetFstreamByPath(request.FilePath(), request.FilePathW(),
std::ios::in | std::ios::binary);
std::string hashSHA1 = ComputeContentSha1(*content);
content->close();
// rapid upload request
auto fileCreateRapidUploadReq = FileCreateRequest(request.DriveID(), request.ParentFileID(), request.Name(),
request.FileID(), request.CheckNameMode(), request.FileSize());
fileCreateRapidUploadReq.setContentHash(hashSHA1);
fileCreateRapidUploadReq.setPartInfoList(partInfoReqList);
fileCreateRapidUploadReq.setUserTags(request.UserTags());
fileCreateRapidUploadReq.setHidden(request.Hidden());
auto fileCreateRapidUploadOutcome = FileCreate(fileCreateRapidUploadReq);
if (!fileCreateRapidUploadOutcome.isSuccess()) {
return FileCompleteOutcome(fileCreateRapidUploadOutcome.error());
}
// rapid upload success
if (fileCreateRapidUploadOutcome.result().RapidUpload()) {
FileCompleteResult result(fileCreateRapidUploadOutcome.result());
return FileCompleteOutcome(result);
}
// failed to rapid upload, upload data
fileCreateResult = fileCreateRapidUploadOutcome.result();
} else {
// pre hash check not matched, upload data
fileCreateResult = fileCreatePreCheckOutcome.result();
}
} else {
// create by upload data
auto fileCreateReq = FileCreateRequest(request.DriveID(), request.ParentFileID(), request.Name(),
request.FileID(), request.CheckNameMode(), request.FileSize());
fileCreateReq.setPartInfoList(partInfoReqList);
fileCreateReq.setUserTags(request.UserTags());
fileCreateReq.setHidden(request.Hidden());
auto fileCreateOutcome = FileCreate(fileCreateReq);
if (!fileCreateOutcome.isSuccess()) {
return FileCompleteOutcome(fileCreateOutcome.error());
}
fileCreateResult = fileCreateOutcome.result();
}
if (fileCreateResult.Exist()) {
return FileCompleteOutcome(PdsError("SameNameFileExist", "Same name file exist."));
}
std::string fileID = fileCreateResult.FileID();
std::string uploadID = fileCreateResult.UploadID();
PartInfoRespList partInfoRespList = fileCreateResult.PartInfoRespList();
if (partInfoRespList.size() == 0) {
return FileCompleteOutcome(PdsError("GetUploadUrlError", "Get Upload url empty."));
}
std::string uploadURl = partInfoRespList[0].UploadUrl();
// upload data
auto content = GetFstreamByPath(request.FilePath(), request.FilePathW(),
std::ios::in | std::ios::binary);
DataPutByUrlRequest putPartRequest(uploadURl, content);
if (request.TransferProgress().Handler) {
putPartRequest.setTransferProgress(request.TransferProgress());
}
if (request.ProgressControl().Handler) {
putPartRequest.setProgressControl(request.ProgressControl());
}
auto putOutCome = DataPutByUrl(putPartRequest);
auto controller = request.ProgressControl();
if (controller.Handler) {
int32_t controlFlag = controller.Handler(controller.UserData);
if (controlFlag == ProgressControlStop) {
return FileCompleteOutcome(PdsError("ClientError:100003", "Upload stop by upper."));
}
if (controlFlag == ProgressControlCancel) {
return FileCompleteOutcome(PdsError("ClientError:100004", "Upload cancel by upper."));
}
}
if (!putOutCome.isSuccess()) {
return FileCompleteOutcome(putOutCome.error());
}
// pds complete file
FileCompleteRequest completeRequest(request.DriveID(), fileID, uploadID);
auto completeOutcome = FileComplete(completeRequest);
if (!completeOutcome.isSuccess()) {
return FileCompleteOutcome(completeOutcome.error());
}
// check size
uint64_t uploadedfileSize = completeOutcome.result().Size();
if (request.FileSize() != uploadedfileSize) {
return FileCompleteOutcome(PdsError("FileSizeCheckError", "Upload data check size fail."));
}
return completeOutcome;
}
else {
ResumableUploader uploader(request, this);
return uploader.Upload();
}
}
DataGetOutcome PdsClientImpl::ResumableFileDownload(const FileDownloadRequest &request) const
{
const auto& reqeustBase = static_cast<const PdsResumableBaseRequest &>(request);
int code = reqeustBase.validate();
if (code != 0) {
return DataGetOutcome(PdsError("ValidateError", reqeustBase.validateMessage(code)));
}
auto getDownloadUrlReq = FileGetDownloadUrlRequest(request.DriveID(), request.ShareID(), request.FileID());
getDownloadUrlReq.setShareToken(request.ShareToken());
auto getDownloadUrlOutcome = FileGetDownloadUrl(getDownloadUrlReq);
if (!getDownloadUrlOutcome.isSuccess()) {
return DataGetOutcome(getDownloadUrlOutcome.error());
}
auto url = getDownloadUrlOutcome.result().Url();
if (url.empty()) {
return DataGetOutcome(PdsError("DownloadUrlEmptyError", "Download data url is empty."));
}
auto punishFlag = getDownloadUrlOutcome.result().PunishFlag();
if (!configuration().enableDownloadPunishedFile && punishFlag == PunishFlagFileFreeze) {
return DataGetOutcome(PdsError("FileIsPunished", "Punished File cannot be downloaded."));
}
auto fileSize = getDownloadUrlOutcome.result().Size();
auto contentHash = getDownloadUrlOutcome.result().ContentHash();
auto crc64Hash = getDownloadUrlOutcome.result().Crc64Hash();
// small file and punished file are downloaded by single OSS GET request
if (fileSize < (int64_t)request.PartSize() || punishFlag == PunishFlagFileFreeze) {
auto getDataReq = DataGetByUrlRequest(url);
if (request.TransferProgress().Handler) {
getDataReq.setTransferProgress(request.TransferProgress());
}
if (request.ProgressControl().Handler) {
getDataReq.setProgressControl(request.ProgressControl());
}
if (request.TrafficLimit() != 0) {
getDataReq.setTrafficLimit(request.TrafficLimit());
}
getDataReq.setResponseStreamFactory([=]() {
return GetFstreamByPath(request.TempFilePath(), request.TempFilePathW(),
std::ios_base::out | std::ios_base::in | std::ios_base::trunc | std::ios_base::binary);
});
if (configuration().enableCrc64 && !crc64Hash.empty()) {
getDataReq.setFlags(getDataReq.Flags() | REQUEST_FLAG_CHECK_CRC64 | REQUEST_FLAG_SAVE_CLIENT_CRC64);
}
auto outcome = DataGetByUrl(getDataReq);
auto controller = request.ProgressControl();
if (controller.Handler) {
int32_t controlFlag = controller.Handler(controller.UserData);
if (controlFlag == ProgressControlStop || controlFlag == ProgressControlCancel) {
if (IsFileExist(request.TempFilePath())) {
RemoveFile(request.TempFilePath());
}
#ifdef _WIN32
else if (IsFileExist(request.TempFilePathW())) {
RemoveFile(request.TempFilePathW());
}
#endif
}
if (controlFlag == ProgressControlStop) {
return DataGetOutcome(PdsError("ClientError:100003", "Download stop by upper."));
}
if (controlFlag == ProgressControlCancel) {
return DataGetOutcome(PdsError("ClientError:100004", "Download cancel by upper."));
}
}
std::shared_ptr<std::iostream> content = nullptr;
outcome.result().setContent(content);
// check size
if (configuration().enableCheckDownloadFileSize) {
uint64_t localFileSize = GetFileSize(request.TempFilePath(), request.TempFilePathW());
// for punished file, downloaded file size may not be equal to origin file size
if (uint64_t(fileSize) != localFileSize && punishFlag != PunishFlagFileFreeze) {
return DataGetOutcome(PdsError("FileSizeCheckError", "Download data check size fail."));
}
}
if (configuration().enableCrc64 && !crc64Hash.empty()) {
auto localCRC64 = std::strtoull(outcome.result().Metadata().HttpMetaData().at("x-oss-hash-crc64ecma-by-client").c_str(), nullptr, 10);
uint64_t ossServerCrc64 = std::strtoull(outcome.result().Metadata().HttpMetaData().at("x-oss-hash-crc64ecma").c_str(), nullptr, 10);
if (localCRC64 != ossServerCrc64) {
if (IsFileExist(request.TempFilePath())) {
RemoveFile(request.TempFilePath());
}
#ifdef _WIN32
else if (IsFileExist(request.TempFilePathW())) {
RemoveFile(request.TempFilePathW());
}
#endif
return DataGetOutcome(PdsError("CrcCheckError", "Download data CRC checksum fail."));
}
}
bool renameSuccess = false;
if (!request.TempFilePath().empty()) {
renameSuccess = RenameFile(request.TempFilePath(), request.FilePath());
}
#ifdef _WIN32
else if (!request.TempFilePathW().empty()) {
renameSuccess = RenameFile(request.TempFilePathW(), request.FilePathW());
}
#endif
if (!renameSuccess) {
std::stringstream ss;
ss << "rename temp file failed";
return DataGetOutcome(PdsError("RenameError", ss.str()));
}
if (IsFileExist(request.TempFilePath())) {
RemoveFile(request.TempFilePath());
}
#ifdef _WIN32
else if (IsFileExist(request.TempFilePathW())) {
RemoveFile(request.TempFilePathW());
}
#endif
return outcome;
}
ResumableDownloader downloader(request, this, fileSize, contentHash, crc64Hash, url);
return downloader.Download();
}
/*Requests control*/
void PdsClientImpl::DisableRequest()
{
BASE::disableRequest();
PDS_LOG(LogLevel::LogDebug, TAG, "client(%p) DisableRequest", this);
}
void PdsClientImpl::EnableRequest()
{
BASE::enableRequest();
PDS_LOG(LogLevel::LogDebug, TAG, "client(%p) EnableRequest", this);
}