-
Notifications
You must be signed in to change notification settings - Fork 43
/
Copy pathdeliveryoptimization_content_downloader.cpp
171 lines (147 loc) · 5.76 KB
/
deliveryoptimization_content_downloader.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/**
* @file deliveryoptimization_content_downloader.cpp
* @brief Content Downloader Extension using Microsoft Delivery Optimization Agent.
*
* @copyright Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/
#include "aduc/connection_string_utils.h"
#include "aduc/content_downloader_extension.hpp"
#include "aduc/contract_utils.h"
#include "aduc/hash_utils.h"
#include "aduc/logging.h"
#include "aduc/process_utils.hpp"
#include <stdio.h> // for FILE
#include <stdlib.h> // for calloc
#include <sys/stat.h> // for stat
#include <vector>
#include <do_config.h>
#include <do_download.h>
#if defined(WIN32)
// DO currently doesn't call CoInitialize, so need to do that here.
# include <algorithm> // std::replace
#endif
// keep this last to minimize chance to interfere with system header includes.
#include "aduc/aduc_banned.h"
namespace MSDO = microsoft::deliveryoptimization;
ADUC_Result do_download(
const ADUC_FileEntity* entity,
const char* workflowId,
const char* workFolder,
unsigned int timeoutInSeconds,
ADUC_DownloadProgressCallback downloadProgressCallback)
{
ADUC_Result_t resultCode = ADUC_Result_Failure;
ADUC_Result_t extendedResultCode = ADUC_ERC_NOTRECOVERABLE;
if (entity->HashCount == 0)
{
Log_Error("File entity does not contain a file hash! Cannot validate cancelling download.");
resultCode = ADUC_Result_Failure;
extendedResultCode = ADUC_ERC_VALIDATION_FILE_HASH_IS_EMPTY;
if (downloadProgressCallback != nullptr)
{
downloadProgressCallback(workflowId, entity->FileId, ADUC_DownloadProgressState_Error, 0, 0);
}
return ADUC_Result{ resultCode, extendedResultCode };
}
std::string fullFilePath;
#if defined(WIN32)
// DO requires a full path. Once paths are fixed up, this can be removed.
if (workFolder[0] == '/')
{
fullFilePath = "c:";
}
#endif
fullFilePath += workFolder;
fullFilePath += "/";
fullFilePath += entity->TargetFilename;
#if defined(WIN32)
// DO requires backslashes, so convert.
std::replace(fullFilePath.begin(), fullFilePath.end(), '/', '\\');
#endif
Log_Info(
"Downloading File '%s' from '%s' to '%s'", entity->TargetFilename, entity->DownloadUri, fullFilePath.c_str());
const std::error_code doErrorCode = MSDO::download::download_url_to_path(
entity->DownloadUri, fullFilePath.c_str(), false, std::chrono::seconds(timeoutInSeconds));
if (!doErrorCode)
{
resultCode = ADUC_Result_Download_Success;
extendedResultCode = 0;
}
else
{
// Note: The call to download_url_to_path() does not make use of a cancellation token,
// so the download can only timeout or hit a fatal error.
Log_Error(
"DO error, msg: %s, code: %#08x, timeout? %d",
doErrorCode.message().c_str(),
doErrorCode.value(),
(doErrorCode == std::errc::timed_out));
resultCode = ADUC_Result_Failure;
extendedResultCode = MAKE_ADUC_DELIVERY_OPTIMIZATION_EXTENDEDRESULTCODE(doErrorCode.value());
}
// If we downloaded successfully, validate the file hash.
if (resultCode == ADUC_Result_Download_Success)
{
Log_Info("Validating file hash");
SHAversion algVersion;
if (!ADUC_HashUtils_GetShaVersionForTypeString(
ADUC_HashUtils_GetHashType(entity->Hash, entity->HashCount, 0), &algVersion))
{
Log_Error(
"FileEntity for %s has unsupported hash type %s",
fullFilePath.c_str(),
ADUC_HashUtils_GetHashType(entity->Hash, entity->HashCount, 0));
resultCode = ADUC_Result_Failure;
extendedResultCode = ADUC_ERC_VALIDATION_FILE_HASH_TYPE_NOT_SUPPORTED;
if (downloadProgressCallback != nullptr)
{
downloadProgressCallback(
workflowId, entity->FileId, ADUC_DownloadProgressState_Error, resultCode, extendedResultCode);
}
return ADUC_Result{ resultCode, extendedResultCode };
}
const bool isValid = ADUC_HashUtils_IsValidFileHash(
fullFilePath.c_str(),
ADUC_HashUtils_GetHashValue(entity->Hash, entity->HashCount, 0),
algVersion,
false /* suppressErrorLog */);
if (!isValid)
{
Log_Error("Hash for %s is not valid", entity->TargetFilename);
resultCode = ADUC_Result_Failure;
extendedResultCode = ADUC_ERC_VALIDATION_FILE_HASH_INVALID_HASH;
if (downloadProgressCallback != nullptr)
{
downloadProgressCallback(
workflowId, entity->FileId, ADUC_DownloadProgressState_Error, resultCode, extendedResultCode);
}
return ADUC_Result{ resultCode, extendedResultCode };
}
}
// Report progress.
struct stat st
{
};
const off_t fileSize{ (stat(fullFilePath.c_str(), &st) == 0) ? st.st_size : 0 };
if (downloadProgressCallback != nullptr)
{
if (resultCode == ADUC_Result_Download_Success)
{
downloadProgressCallback(
workflowId, entity->FileId, ADUC_DownloadProgressState_Completed, fileSize, fileSize);
}
else
{
downloadProgressCallback(
workflowId,
entity->FileId,
(resultCode == ADUC_Result_Failure_Cancelled) ? ADUC_DownloadProgressState_Cancelled
: ADUC_DownloadProgressState_Error,
fileSize,
fileSize);
}
}
Log_Info("Download resultCode: %d, extendedCode: %d", resultCode, extendedResultCode);
return ADUC_Result{ resultCode, extendedResultCode };
}