Directory: | cvmfs/ |
---|---|
File: | cvmfs/network/download.cc |
Date: | 2025-06-29 02:35:41 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 891 | 1747 | 51.0% |
Branches: | 698 | 2244 | 31.1% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * This file is part of the CernVM File System. | ||
3 | * | ||
4 | * The download module provides an interface for fetching files via HTTP | ||
5 | * and file. It is internally using libcurl and the asynchronous DNS resolver | ||
6 | * c-ares. The JobInfo struct describes a single file/url to download and | ||
7 | * keeps the state during the several phases of downloading. | ||
8 | * | ||
9 | * The module starts in single-threaded mode and can be switched to multi- | ||
10 | * threaded mode by Spawn(). In multi-threaded mode, the Fetch() function still | ||
11 | * blocks but there is a separate I/O thread using asynchronous I/O, which | ||
12 | * maintains all concurrent connections simultaneously. As there might be more | ||
13 | * than 1024 file descriptors for the CernVM-FS process, the I/O thread uses | ||
14 | * poll and the libcurl multi socket interface. | ||
15 | * | ||
16 | * While downloading, files can be decompressed and the secure hash can be | ||
17 | * calculated on the fly. | ||
18 | * | ||
19 | * The module also implements failure handling. If corrupted data has been | ||
20 | * downloaded, the transfer is restarted using HTTP "no-cache" pragma. | ||
21 | * A "host chain" can be configured. When a host fails, there is automatic | ||
22 | * fail-over to the next host in the chain until all hosts are probed. | ||
23 | * Similarly a chain of proxy sets can be configured. Inside a proxy set, | ||
24 | * proxies are selected randomly (load-balancing set). | ||
25 | */ | ||
26 | |||
27 | // TODO(jblomer): MS for time summing | ||
28 | // NOLINTNEXTLINE | ||
29 | #define __STDC_FORMAT_MACROS | ||
30 | |||
31 | |||
32 | #include "download.h" | ||
33 | |||
34 | #include <alloca.h> | ||
35 | #include <errno.h> | ||
36 | #include <inttypes.h> | ||
37 | #include <poll.h> | ||
38 | #include <pthread.h> | ||
39 | #include <signal.h> | ||
40 | #include <stdint.h> | ||
41 | #include <sys/time.h> | ||
42 | #include <unistd.h> | ||
43 | |||
44 | #include <algorithm> | ||
45 | #include <cassert> | ||
46 | #include <cstdio> | ||
47 | #include <cstdlib> | ||
48 | #include <cstring> | ||
49 | #include <map> | ||
50 | #include <set> | ||
51 | #include <utility> | ||
52 | |||
53 | #include "compression/compression.h" | ||
54 | #include "crypto/hash.h" | ||
55 | #include "duplex_curl.h" | ||
56 | #include "interrupt.h" | ||
57 | #include "sanitizer.h" | ||
58 | #include "ssl.h" | ||
59 | #include "util/algorithm.h" | ||
60 | #include "util/atomic.h" | ||
61 | #include "util/concurrency.h" | ||
62 | #include "util/exception.h" | ||
63 | #include "util/logging.h" | ||
64 | #include "util/posix.h" | ||
65 | #include "util/prng.h" | ||
66 | #include "util/smalloc.h" | ||
67 | #include "util/string.h" | ||
68 | |||
69 | using namespace std; // NOLINT | ||
70 | |||
71 | namespace download { | ||
72 | |||
73 | /** | ||
74 | * Returns the status if an interrupt happened for a given repository. | ||
75 | * | ||
76 | * Used only in case CVMFS_FAILOVER_INDEFINITELY (failover_indefinitely_) is set | ||
77 | * where failed downloads are retried indefinitely, unless an interrupt occurred | ||
78 | * | ||
79 | * @note If you use this functionality you need to change the source code of | ||
80 | * e.g. cvmfs_config reload to create a sentinel file. See comment below. | ||
81 | * | ||
82 | * @return true if an interrupt occurred | ||
83 | * false otherwise | ||
84 | */ | ||
85 | ✗ | bool Interrupted(const std::string &fqrn, JobInfo *info) { | |
86 | ✗ | if (info->allow_failure()) { | |
87 | ✗ | return true; | |
88 | } | ||
89 | |||
90 | ✗ | if (!fqrn.empty()) { | |
91 | // it is up to the user the create this sentinel file ("pause_file") if | ||
92 | // CVMFS_FAILOVER_INDEFINITELY is used. It must be created during | ||
93 | // "cvmfs_config reload" and "cvmfs_config reload $fqrn" | ||
94 | ✗ | const std::string pause_file = std::string("/var/run/cvmfs/interrupt.") | |
95 | ✗ | + fqrn; | |
96 | |||
97 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
98 | "(id %" PRId64 ") Interrupted(): checking for existence of %s", | ||
99 | info->id(), pause_file.c_str()); | ||
100 | ✗ | if (FileExists(pause_file)) { | |
101 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
102 | "(id %" PRId64 ") Interrupt marker found - " | ||
103 | "Interrupting current download, this will EIO outstanding IO.", | ||
104 | info->id()); | ||
105 | ✗ | if (0 != unlink(pause_file.c_str())) { | |
106 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
107 | "(id %" PRId64 ") Couldn't delete interrupt marker: errno=%d", | ||
108 | ✗ | info->id(), errno); | |
109 | } | ||
110 | ✗ | return true; | |
111 | } | ||
112 | } | ||
113 | ✗ | return false; | |
114 | } | ||
115 | |||
116 | 3181 | static Failures PrepareDownloadDestination(JobInfo *info) { | |
117 |
3/6✓ Branch 1 taken 3181 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 3181 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3181 times.
|
3181 | if (info->sink() != NULL && !info->sink()->IsValid()) { |
118 | ✗ | cvmfs::PathSink *psink = dynamic_cast<cvmfs::PathSink *>(info->sink()); | |
119 | ✗ | if (psink != NULL) { | |
120 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
121 | "(id %" PRId64 ") Failed to open path %s: %s (errno=%d).", | ||
122 | ✗ | info->id(), psink->path().c_str(), strerror(errno), errno); | |
123 | ✗ | return kFailLocalIO; | |
124 | } else { | ||
125 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
126 | "(id %" PRId64 ") " | ||
127 | "Failed to create a valid sink: \n %s", | ||
128 | ✗ | info->id(), info->sink()->Describe().c_str()); | |
129 | ✗ | return kFailOther; | |
130 | } | ||
131 | } | ||
132 | |||
133 | 3181 | return kFailOk; | |
134 | } | ||
135 | |||
136 | |||
137 | /** | ||
138 | * Called by curl for every HTTP header. Not called for file:// transfers. | ||
139 | */ | ||
140 | 9485 | static size_t CallbackCurlHeader(void *ptr, size_t size, size_t nmemb, | |
141 | void *info_link) { | ||
142 | 9485 | const size_t num_bytes = size * nmemb; | |
143 |
1/2✓ Branch 2 taken 9485 times.
✗ Branch 3 not taken.
|
9485 | const string header_line(static_cast<const char *>(ptr), num_bytes); |
144 | 9485 | JobInfo *info = static_cast<JobInfo *>(info_link); | |
145 | |||
146 | // LogCvmfs(kLogDownload, kLogDebug, "REMOVE-ME: Header callback with %s", | ||
147 | // header_line.c_str()); | ||
148 | |||
149 | // Check http status codes | ||
150 |
4/6✓ Branch 2 taken 9485 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9485 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 192 times.
✓ Branch 10 taken 9293 times.
|
9485 | if (HasPrefix(header_line, "HTTP/1.", false)) { |
151 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 192 times.
|
192 | if (header_line.length() < 10) { |
152 | ✗ | return 0; | |
153 | } | ||
154 | |||
155 | unsigned i; | ||
156 |
5/6✓ Branch 1 taken 384 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 192 times.
✓ Branch 5 taken 192 times.
✓ Branch 6 taken 192 times.
✓ Branch 7 taken 192 times.
|
384 | for (i = 8; (i < header_line.length()) && (header_line[i] == ' '); ++i) { |
157 | } | ||
158 | |||
159 | // Code is initialized to -1 | ||
160 |
1/2✓ Branch 1 taken 192 times.
✗ Branch 2 not taken.
|
192 | if (header_line.length() > i + 2) { |
161 | 192 | info->SetHttpCode(DownloadManager::ParseHttpCode(&header_line[i])); | |
162 | } | ||
163 | |||
164 |
2/2✓ Branch 1 taken 144 times.
✓ Branch 2 taken 48 times.
|
192 | if ((info->http_code() / 100) == 2) { |
165 | 144 | return num_bytes; | |
166 |
0/2✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
48 | } else if ((info->http_code() == 301) || (info->http_code() == 302) |
167 |
2/8✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 48 times.
✗ Branch 9 not taken.
|
48 | || (info->http_code() == 303) || (info->http_code() == 307)) { |
168 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 48 times.
|
48 | if (!info->follow_redirects()) { |
169 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
170 | "(id %" PRId64 ") redirect support not enabled: %s", | ||
171 | info->id(), header_line.c_str()); | ||
172 | ✗ | info->SetErrorCode(kFailHostHttp); | |
173 | ✗ | return 0; | |
174 | } | ||
175 |
1/2✓ Branch 3 taken 48 times.
✗ Branch 4 not taken.
|
48 | LogCvmfs(kLogDownload, kLogDebug, "(id %" PRId64 ") http redirect: %s", |
176 | info->id(), header_line.c_str()); | ||
177 | // libcurl will handle this because of CURLOPT_FOLLOWLOCATION | ||
178 | 48 | return num_bytes; | |
179 | } else { | ||
180 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
181 | "(id %" PRId64 ") http status error code: %s [%d]", info->id(), | ||
182 | header_line.c_str(), info->http_code()); | ||
183 | ✗ | if (((info->http_code() / 100) == 5) || (info->http_code() == 400) | |
184 | ✗ | || (info->http_code() == 404)) { | |
185 | // 5XX returned by host | ||
186 | // 400: error from the GeoAPI module | ||
187 | // 404: the stratum 1 does not have the newest files | ||
188 | ✗ | info->SetErrorCode(kFailHostHttp); | |
189 | ✗ | } else if (info->http_code() == 429) { | |
190 | // 429: rate throttling (we ignore the backoff hint for the time being) | ||
191 | ✗ | info->SetErrorCode(kFailHostConnection); | |
192 | } else { | ||
193 | ✗ | info->SetErrorCode((info->proxy() == "DIRECT") ? kFailHostHttp | |
194 | : kFailProxyHttp); | ||
195 | } | ||
196 | ✗ | return 0; | |
197 | } | ||
198 | } | ||
199 | |||
200 | // If needed: allocate space in sink | ||
201 |
3/4✓ Branch 2 taken 9293 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3689 times.
✓ Branch 5 taken 5604 times.
|
9293 | if (info->sink() != NULL && info->sink()->RequiresReserve() |
202 |
11/20✓ Branch 1 taken 9293 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3689 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 3689 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1171 times.
✓ Branch 11 taken 2518 times.
✓ Branch 12 taken 3689 times.
✓ Branch 13 taken 5604 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 3689 times.
✓ Branch 16 taken 5604 times.
✓ Branch 18 taken 1171 times.
✓ Branch 19 taken 8122 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
|
18586 | && HasPrefix(header_line, "CONTENT-LENGTH:", true)) { |
203 | 1171 | char *tmp = reinterpret_cast<char *>(alloca(num_bytes + 1)); | |
204 | 1171 | uint64_t length = 0; | |
205 | 1171 | sscanf(header_line.c_str(), "%s %" PRIu64, tmp, &length); | |
206 |
1/2✓ Branch 0 taken 1171 times.
✗ Branch 1 not taken.
|
1171 | if (length > 0) { |
207 |
2/4✓ Branch 2 taken 1171 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1171 times.
|
1171 | if (!info->sink()->Reserve(length)) { |
208 | ✗ | LogCvmfs(kLogDownload, kLogDebug | kLogSyslogErr, | |
209 | "(id %" PRId64 ") " | ||
210 | "resource %s too large to store in memory (%" PRIu64 ")", | ||
211 | info->id(), info->url()->c_str(), length); | ||
212 | ✗ | info->SetErrorCode(kFailTooBig); | |
213 | ✗ | return 0; | |
214 | } | ||
215 | } else { | ||
216 | // Empty resource | ||
217 | ✗ | info->sink()->Reserve(0); | |
218 | } | ||
219 |
4/6✓ Branch 2 taken 8122 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 8122 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 48 times.
✓ Branch 10 taken 8074 times.
|
8122 | } else if (HasPrefix(header_line, "LOCATION:", true)) { |
220 | // This comes along with redirects | ||
221 |
1/2✓ Branch 3 taken 48 times.
✗ Branch 4 not taken.
|
48 | LogCvmfs(kLogDownload, kLogDebug, "(id %" PRId64 ") %s", info->id(), |
222 | header_line.c_str()); | ||
223 |
3/6✓ Branch 2 taken 8074 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 8074 times.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 8074 times.
|
8074 | } else if (HasPrefix(header_line, "LINK:", true)) { |
224 | // This is metalink info | ||
225 | ✗ | LogCvmfs(kLogDownload, kLogDebug, "(id %" PRId64 ") %s", info->id(), | |
226 | header_line.c_str()); | ||
227 | ✗ | std::string link = info->link(); | |
228 | ✗ | if (link.size() != 0) { | |
229 | // multiple LINK headers are allowed | ||
230 | ✗ | link = link + ", " + header_line.substr(5); | |
231 | } else { | ||
232 | ✗ | link = header_line.substr(5); | |
233 | } | ||
234 | ✗ | info->SetLink(link); | |
235 |
3/6✓ Branch 3 taken 8074 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 8074 times.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 8074 times.
|
8074 | } else if (HasPrefix(header_line, "X-SQUID-ERROR:", true)) { |
236 | // Reinterpret host error as proxy error | ||
237 | ✗ | if (info->error_code() == kFailHostHttp) { | |
238 | ✗ | info->SetErrorCode(kFailProxyHttp); | |
239 | } | ||
240 |
3/6✓ Branch 2 taken 8074 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 8074 times.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 8074 times.
|
8074 | } else if (HasPrefix(header_line, "PROXY-STATUS:", true)) { |
241 | // Reinterpret host error as proxy error if applicable | ||
242 | ✗ | if ((info->error_code() == kFailHostHttp) | |
243 | ✗ | && (header_line.find("error=") != string::npos)) { | |
244 | ✗ | info->SetErrorCode(kFailProxyHttp); | |
245 | } | ||
246 | } | ||
247 | |||
248 | 9293 | return num_bytes; | |
249 | 9485 | } | |
250 | |||
251 | |||
252 | /** | ||
253 | * Called by curl for every received data chunk. | ||
254 | */ | ||
255 | 3615 | static size_t CallbackCurlData(void *ptr, size_t size, size_t nmemb, | |
256 | void *info_link) { | ||
257 | 3615 | const size_t num_bytes = size * nmemb; | |
258 | 3615 | JobInfo *info = static_cast<JobInfo *>(info_link); | |
259 | |||
260 | // TODO(heretherebedragons) remove if no error comes up | ||
261 | // as this means only jobinfo data request (and not header only) | ||
262 | // come here | ||
263 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3615 times.
|
3615 | assert(info->sink() != NULL); |
264 | |||
265 | // LogCvmfs(kLogDownload, kLogDebug, "Data callback, %d bytes", num_bytes); | ||
266 | |||
267 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3615 times.
|
3615 | if (num_bytes == 0) |
268 | ✗ | return 0; | |
269 | |||
270 |
2/2✓ Branch 1 taken 2066 times.
✓ Branch 2 taken 1549 times.
|
3615 | if (info->expected_hash()) { |
271 | 2066 | shash::Update(reinterpret_cast<unsigned char *>(ptr), num_bytes, | |
272 | info->hash_context()); | ||
273 | } | ||
274 | |||
275 |
2/2✓ Branch 1 taken 2065 times.
✓ Branch 2 taken 1550 times.
|
3615 | if (info->compressed()) { |
276 | 2065 | const zlib::StreamStates retval = zlib::DecompressZStream2Sink( | |
277 | ptr, static_cast<int64_t>(num_bytes), info->GetZstreamPtr(), | ||
278 | info->sink()); | ||
279 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2065 times.
|
2065 | if (retval == zlib::kStreamDataError) { |
280 | ✗ | LogCvmfs(kLogDownload, kLogSyslogErr, | |
281 | "(id %" PRId64 ") failed to decompress %s", info->id(), | ||
282 | info->url()->c_str()); | ||
283 | ✗ | info->SetErrorCode(kFailBadData); | |
284 | ✗ | return 0; | |
285 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2065 times.
|
2065 | } else if (retval == zlib::kStreamIOError) { |
286 | ✗ | LogCvmfs(kLogDownload, kLogSyslogErr, | |
287 | "(id %" PRId64 ") decompressing %s, local IO error", info->id(), | ||
288 | info->url()->c_str()); | ||
289 | ✗ | info->SetErrorCode(kFailLocalIO); | |
290 | ✗ | return 0; | |
291 | } | ||
292 | } else { | ||
293 | 1550 | const int64_t written = info->sink()->Write(ptr, num_bytes); | |
294 |
2/4✓ Branch 0 taken 1550 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1550 times.
|
1550 | if (written < 0 || static_cast<uint64_t>(written) != num_bytes) { |
295 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
296 | "(id %" PRId64 ") " | ||
297 | "Failed to perform write of %zu bytes to sink %s with errno %ld", | ||
298 | ✗ | info->id(), num_bytes, info->sink()->Describe().c_str(), | |
299 | written); | ||
300 | } | ||
301 | } | ||
302 | |||
303 | 3615 | return num_bytes; | |
304 | } | ||
305 | |||
306 | #ifdef DEBUGMSG | ||
307 | 1834 | static int CallbackCurlDebug(CURL *handle, | |
308 | curl_infotype type, | ||
309 | char *data, | ||
310 | size_t size, | ||
311 | void * /* clientp */) { | ||
312 | JobInfo *info; | ||
313 |
1/2✓ Branch 1 taken 1834 times.
✗ Branch 2 not taken.
|
1834 | curl_easy_getinfo(handle, CURLINFO_PRIVATE, &info); |
314 | |||
315 |
3/6✓ Branch 2 taken 1834 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1834 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1834 times.
✗ Branch 9 not taken.
|
3668 | std::string prefix = "(id " + StringifyInt(info->id()) + ") "; |
316 |
4/8✓ Branch 0 taken 314 times.
✓ Branch 1 taken 752 times.
✓ Branch 2 taken 192 times.
✓ Branch 3 taken 576 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
1834 | switch (type) { |
317 | 314 | case CURLINFO_TEXT: | |
318 |
1/2✓ Branch 1 taken 314 times.
✗ Branch 2 not taken.
|
314 | prefix += "{info} "; |
319 | 314 | break; | |
320 | 752 | case CURLINFO_HEADER_IN: | |
321 |
1/2✓ Branch 1 taken 752 times.
✗ Branch 2 not taken.
|
752 | prefix += "{header/recv} "; |
322 | 752 | break; | |
323 | 192 | case CURLINFO_HEADER_OUT: | |
324 |
1/2✓ Branch 1 taken 192 times.
✗ Branch 2 not taken.
|
192 | prefix += "{header/sent} "; |
325 | 192 | break; | |
326 | 576 | case CURLINFO_DATA_IN: | |
327 |
2/2✓ Branch 0 taken 64 times.
✓ Branch 1 taken 512 times.
|
576 | if (size < 50) { |
328 |
1/2✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
|
64 | prefix += "{data/recv} "; |
329 | 64 | break; | |
330 | } else { | ||
331 |
1/2✓ Branch 2 taken 512 times.
✗ Branch 3 not taken.
|
512 | LogCvmfs(kLogCurl, kLogDebug, "%s{data/recv} <snip>", prefix.c_str()); |
332 | 512 | return 0; | |
333 | } | ||
334 | ✗ | case CURLINFO_DATA_OUT: | |
335 | ✗ | if (size < 50) { | |
336 | ✗ | prefix += "{data/sent} "; | |
337 | ✗ | break; | |
338 | } else { | ||
339 | ✗ | LogCvmfs(kLogCurl, kLogDebug, "%s{data/sent} <snip>", prefix.c_str()); | |
340 | ✗ | return 0; | |
341 | } | ||
342 | ✗ | case CURLINFO_SSL_DATA_IN: | |
343 | ✗ | if (size < 50) { | |
344 | ✗ | prefix += "{ssldata/recv} "; | |
345 | ✗ | break; | |
346 | } else { | ||
347 | ✗ | LogCvmfs(kLogCurl, kLogDebug, "%s{ssldata/recv} <snip>", | |
348 | prefix.c_str()); | ||
349 | ✗ | return 0; | |
350 | } | ||
351 | ✗ | case CURLINFO_SSL_DATA_OUT: | |
352 | ✗ | if (size < 50) { | |
353 | ✗ | prefix += "{ssldata/sent} "; | |
354 | ✗ | break; | |
355 | } else { | ||
356 | ✗ | LogCvmfs(kLogCurl, kLogDebug, "%s{ssldata/sent} <snip>", | |
357 | prefix.c_str()); | ||
358 | ✗ | return 0; | |
359 | } | ||
360 | ✗ | default: | |
361 | // just log the message | ||
362 | ✗ | break; | |
363 | } | ||
364 | |||
365 | 1322 | bool valid_char = true; | |
366 |
1/2✓ Branch 2 taken 1322 times.
✗ Branch 3 not taken.
|
1322 | std::string msg(data, size); |
367 |
2/2✓ Branch 1 taken 64021 times.
✓ Branch 2 taken 1322 times.
|
65343 | for (size_t i = 0; i < msg.length(); ++i) { |
368 |
2/4✓ Branch 1 taken 64021 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 64021 times.
|
64021 | if (msg[i] == '\0') { |
369 | ✗ | msg[i] = '~'; | |
370 | } | ||
371 | |||
372 | // verify that char is a valid printable char | ||
373 |
3/6✓ Branch 1 taken 64021 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 59771 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 59771 times.
|
123792 | if ((msg[i] < ' ' || msg[i] > '~') |
374 |
6/8✓ Branch 0 taken 59771 times.
✓ Branch 1 taken 4250 times.
✓ Branch 3 taken 4250 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1968 times.
✓ Branch 6 taken 2282 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 64021 times.
|
125760 | && (msg[i] != 10 /*line feed*/ |
375 |
2/4✓ Branch 1 taken 1968 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1968 times.
|
1968 | && msg[i] != 13 /*carriage return*/)) { |
376 | ✗ | valid_char = false; | |
377 | } | ||
378 | } | ||
379 | |||
380 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1322 times.
|
1322 | if (!valid_char) { |
381 | ✗ | msg = "<Non-plaintext sequence>"; | |
382 | } | ||
383 | |||
384 |
1/2✓ Branch 3 taken 1322 times.
✗ Branch 4 not taken.
|
1322 | LogCvmfs(kLogCurl, kLogDebug, "%s%s", prefix.c_str(), |
385 |
1/2✓ Branch 1 taken 1322 times.
✗ Branch 2 not taken.
|
2644 | Trim(msg, true /* trim_newline */).c_str()); |
386 | 1322 | return 0; | |
387 | 1834 | } | |
388 | #endif | ||
389 | |||
390 | //------------------------------------------------------------------------------ | ||
391 | |||
392 | |||
393 | const int DownloadManager::kProbeUnprobed = -1; | ||
394 | const int DownloadManager::kProbeDown = -2; | ||
395 | const int DownloadManager::kProbeGeo = -3; | ||
396 | |||
397 | 265390 | bool DownloadManager::EscapeUrlChar(unsigned char input, char output[3]) { | |
398 |
8/8✓ Branch 0 taken 237109 times.
✓ Branch 1 taken 28281 times.
✓ Branch 2 taken 183078 times.
✓ Branch 3 taken 54031 times.
✓ Branch 4 taken 179666 times.
✓ Branch 5 taken 31693 times.
✓ Branch 6 taken 168114 times.
✓ Branch 7 taken 11552 times.
|
265390 | if (((input >= '0') && (input <= '9')) || ((input >= 'A') && (input <= 'Z')) |
399 |
8/8✓ Branch 0 taken 158966 times.
✓ Branch 1 taken 40841 times.
✓ Branch 2 taken 144 times.
✓ Branch 3 taken 158822 times.
✓ Branch 4 taken 17732 times.
✓ Branch 5 taken 23253 times.
✓ Branch 6 taken 14320 times.
✓ Branch 7 taken 3412 times.
|
199807 | || ((input >= 'a') && (input <= 'z')) || (input == '/') || (input == ':') |
400 |
6/8✓ Branch 0 taken 9431 times.
✓ Branch 1 taken 4889 times.
✓ Branch 2 taken 9431 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9431 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 9292 times.
✓ Branch 7 taken 139 times.
|
14320 | || (input == '.') || (input == '@') || (input == '+') || (input == '-') |
401 |
5/8✓ Branch 0 taken 144 times.
✓ Branch 1 taken 9148 times.
✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 144 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 144 times.
✗ Branch 7 not taken.
|
9292 | || (input == '_') || (input == '~') || (input == '[') || (input == ']') |
402 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
|
144 | || (input == ',')) { |
403 | 265246 | output[0] = static_cast<char>(input); | |
404 | 265246 | return false; | |
405 | } | ||
406 | |||
407 | 144 | output[0] = '%'; | |
408 | 288 | output[1] = static_cast<char>((input / 16) | |
409 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 112 times.
|
144 | + ((input / 16 <= 9) ? '0' : 'A' - 10)); |
410 | 288 | output[2] = static_cast<char>((input % 16) | |
411 |
2/2✓ Branch 0 taken 64 times.
✓ Branch 1 taken 80 times.
|
144 | + ((input % 16 <= 9) ? '0' : 'A' - 10)); |
412 | 144 | return true; | |
413 | } | ||
414 | |||
415 | |||
416 | /** | ||
417 | * Escape special chars from the URL, except for ':' and '/', | ||
418 | * which should keep their meaning. | ||
419 | */ | ||
420 | 3245 | string DownloadManager::EscapeUrl(const int64_t jobinfo_id, const string &url) { | |
421 | 3245 | string escaped; | |
422 |
1/2✓ Branch 2 taken 3245 times.
✗ Branch 3 not taken.
|
3245 | escaped.reserve(url.length()); |
423 | |||
424 | char escaped_char[3]; | ||
425 |
2/2✓ Branch 1 taken 265390 times.
✓ Branch 2 taken 3245 times.
|
268635 | for (unsigned i = 0, s = url.length(); i < s; ++i) { |
426 |
2/2✓ Branch 2 taken 144 times.
✓ Branch 3 taken 265246 times.
|
265390 | if (EscapeUrlChar(url[i], escaped_char)) { |
427 |
1/2✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
|
144 | escaped.append(escaped_char, 3); |
428 | } else { | ||
429 |
1/2✓ Branch 1 taken 265246 times.
✗ Branch 2 not taken.
|
265246 | escaped.push_back(escaped_char[0]); |
430 | } | ||
431 | } | ||
432 |
1/2✓ Branch 3 taken 3245 times.
✗ Branch 4 not taken.
|
3245 | LogCvmfs(kLogDownload, kLogDebug, "(id %" PRId64 ") escaped %s to %s", |
433 | jobinfo_id, url.c_str(), escaped.c_str()); | ||
434 | |||
435 | 6490 | return escaped; | |
436 | } | ||
437 | |||
438 | /** | ||
439 | * escaped array needs to be sufficiently large. Its size is calculated by | ||
440 | * passing NULL to EscapeHeader. | ||
441 | */ | ||
442 | ✗ | unsigned DownloadManager::EscapeHeader(const string &header, | |
443 | char *escaped_buf, | ||
444 | size_t buf_size) { | ||
445 | ✗ | unsigned esc_pos = 0; | |
446 | char escaped_char[3]; | ||
447 | ✗ | for (unsigned i = 0, s = header.size(); i < s; ++i) { | |
448 | ✗ | if (EscapeUrlChar(header[i], escaped_char)) { | |
449 | ✗ | for (unsigned j = 0; j < 3; ++j) { | |
450 | ✗ | if (escaped_buf) { | |
451 | ✗ | if (esc_pos >= buf_size) | |
452 | ✗ | return esc_pos; | |
453 | ✗ | escaped_buf[esc_pos] = escaped_char[j]; | |
454 | } | ||
455 | ✗ | esc_pos++; | |
456 | } | ||
457 | } else { | ||
458 | ✗ | if (escaped_buf) { | |
459 | ✗ | if (esc_pos >= buf_size) | |
460 | ✗ | return esc_pos; | |
461 | ✗ | escaped_buf[esc_pos] = escaped_char[0]; | |
462 | } | ||
463 | ✗ | esc_pos++; | |
464 | } | ||
465 | } | ||
466 | |||
467 | ✗ | return esc_pos; | |
468 | } | ||
469 | |||
470 | /** | ||
471 | * -1 of digits is not a valid Http return code | ||
472 | */ | ||
473 | 272 | int DownloadManager::ParseHttpCode(const char digits[3]) { | |
474 | 272 | int result = 0; | |
475 | 272 | int factor = 100; | |
476 |
2/2✓ Branch 0 taken 816 times.
✓ Branch 1 taken 256 times.
|
1072 | for (int i = 0; i < 3; ++i) { |
477 |
3/4✓ Branch 0 taken 816 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 800 times.
|
816 | if ((digits[i] < '0') || (digits[i] > '9')) |
478 | 16 | return -1; | |
479 | 800 | result += (digits[i] - '0') * factor; | |
480 | 800 | factor /= 10; | |
481 | } | ||
482 | 256 | return result; | |
483 | } | ||
484 | |||
485 | |||
486 | /** | ||
487 | * Called when new curl sockets arrive or existing curl sockets depart. | ||
488 | */ | ||
489 | ✗ | int DownloadManager::CallbackCurlSocket(CURL * /* easy */, | |
490 | curl_socket_t s, | ||
491 | int action, | ||
492 | void *userp, | ||
493 | void * /* socketp */) { | ||
494 | // LogCvmfs(kLogDownload, kLogDebug, "CallbackCurlSocket called with easy " | ||
495 | // "handle %p, socket %d, action %d", easy, s, action); | ||
496 | ✗ | DownloadManager *download_mgr = static_cast<DownloadManager *>(userp); | |
497 | ✗ | if (action == CURL_POLL_NONE) | |
498 | ✗ | return 0; | |
499 | |||
500 | // Find s in watch_fds_ | ||
501 | unsigned index; | ||
502 | |||
503 | // TODO(heretherebedragons) why start at index = 0 and not 2? | ||
504 | // fd[0] and fd[1] are fixed? | ||
505 | ✗ | for (index = 0; index < download_mgr->watch_fds_inuse_; ++index) { | |
506 | ✗ | if (download_mgr->watch_fds_[index].fd == s) | |
507 | ✗ | break; | |
508 | } | ||
509 | // Or create newly | ||
510 | ✗ | if (index == download_mgr->watch_fds_inuse_) { | |
511 | // Extend array if necessary | ||
512 | ✗ | if (download_mgr->watch_fds_inuse_ == download_mgr->watch_fds_size_) { | |
513 | ✗ | assert(download_mgr->watch_fds_size_ > 0); | |
514 | ✗ | download_mgr->watch_fds_size_ *= 2; | |
515 | ✗ | download_mgr->watch_fds_ = static_cast<struct pollfd *>( | |
516 | ✗ | srealloc(download_mgr->watch_fds_, | |
517 | ✗ | download_mgr->watch_fds_size_ * sizeof(struct pollfd))); | |
518 | } | ||
519 | ✗ | download_mgr->watch_fds_[download_mgr->watch_fds_inuse_].fd = s; | |
520 | ✗ | download_mgr->watch_fds_[download_mgr->watch_fds_inuse_].events = 0; | |
521 | ✗ | download_mgr->watch_fds_[download_mgr->watch_fds_inuse_].revents = 0; | |
522 | ✗ | download_mgr->watch_fds_inuse_++; | |
523 | } | ||
524 | |||
525 | ✗ | switch (action) { | |
526 | ✗ | case CURL_POLL_IN: | |
527 | ✗ | download_mgr->watch_fds_[index].events = POLLIN | POLLPRI; | |
528 | ✗ | break; | |
529 | ✗ | case CURL_POLL_OUT: | |
530 | ✗ | download_mgr->watch_fds_[index].events = POLLOUT | POLLWRBAND; | |
531 | ✗ | break; | |
532 | ✗ | case CURL_POLL_INOUT: | |
533 | ✗ | download_mgr->watch_fds_[index].events = POLLIN | POLLPRI | POLLOUT | |
534 | | POLLWRBAND; | ||
535 | ✗ | break; | |
536 | ✗ | case CURL_POLL_REMOVE: | |
537 | ✗ | if (index < download_mgr->watch_fds_inuse_ - 1) { | |
538 | download_mgr | ||
539 | ✗ | ->watch_fds_[index] = download_mgr->watch_fds_ | |
540 | ✗ | [download_mgr->watch_fds_inuse_ - 1]; | |
541 | } | ||
542 | ✗ | download_mgr->watch_fds_inuse_--; | |
543 | // Shrink array if necessary | ||
544 | ✗ | if ((download_mgr->watch_fds_inuse_ > download_mgr->watch_fds_max_) | |
545 | ✗ | && (download_mgr->watch_fds_inuse_ | |
546 | ✗ | < download_mgr->watch_fds_size_ / 2)) { | |
547 | ✗ | download_mgr->watch_fds_size_ /= 2; | |
548 | // LogCvmfs(kLogDownload, kLogDebug, "shrinking watch_fds_ (%d)", | ||
549 | // watch_fds_size_); | ||
550 | ✗ | download_mgr->watch_fds_ = static_cast<struct pollfd *>( | |
551 | ✗ | srealloc(download_mgr->watch_fds_, | |
552 | ✗ | download_mgr->watch_fds_size_ * sizeof(struct pollfd))); | |
553 | // LogCvmfs(kLogDownload, kLogDebug, "shrinking watch_fds_ done", | ||
554 | // watch_fds_size_); | ||
555 | } | ||
556 | ✗ | break; | |
557 | ✗ | default: | |
558 | ✗ | break; | |
559 | } | ||
560 | |||
561 | ✗ | return 0; | |
562 | } | ||
563 | |||
564 | |||
565 | /** | ||
566 | * Worker thread event loop. Waits on new JobInfo structs on a pipe. | ||
567 | */ | ||
568 | ✗ | void *DownloadManager::MainDownload(void *data) { | |
569 | ✗ | DownloadManager *download_mgr = static_cast<DownloadManager *>(data); | |
570 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
571 | "download I/O thread of DownloadManager '%s' started", | ||
572 | download_mgr->name_.c_str()); | ||
573 | |||
574 | ✗ | const int kIdxPipeTerminate = 0; | |
575 | ✗ | const int kIdxPipeJobs = 1; | |
576 | |||
577 | ✗ | download_mgr->watch_fds_ = static_cast<struct pollfd *>( | |
578 | ✗ | smalloc(2 * sizeof(struct pollfd))); | |
579 | ✗ | download_mgr->watch_fds_size_ = 2; | |
580 | ✗ | download_mgr->watch_fds_[kIdxPipeTerminate].fd = download_mgr->pipe_terminate_ | |
581 | ✗ | ->GetReadFd(); | |
582 | ✗ | download_mgr->watch_fds_[kIdxPipeTerminate].events = POLLIN | POLLPRI; | |
583 | ✗ | download_mgr->watch_fds_[kIdxPipeTerminate].revents = 0; | |
584 | ✗ | download_mgr->watch_fds_[kIdxPipeJobs].fd = download_mgr->pipe_jobs_ | |
585 | ✗ | ->GetReadFd(); | |
586 | ✗ | download_mgr->watch_fds_[kIdxPipeJobs].events = POLLIN | POLLPRI; | |
587 | ✗ | download_mgr->watch_fds_[kIdxPipeJobs].revents = 0; | |
588 | ✗ | download_mgr->watch_fds_inuse_ = 2; | |
589 | |||
590 | ✗ | int still_running = 0; | |
591 | struct timeval timeval_start, timeval_stop; | ||
592 | ✗ | gettimeofday(&timeval_start, NULL); | |
593 | while (true) { | ||
594 | int timeout; | ||
595 | ✗ | if (still_running) { | |
596 | /* NOTE: The following might degrade the performance for many small files | ||
597 | * use case. TODO(jblomer): look into it. | ||
598 | // Specify a timeout for polling in ms; this allows us to return | ||
599 | // to libcurl once a second so it can look for internal operations | ||
600 | // which timed out. libcurl has a more elaborate mechanism | ||
601 | // (CURLMOPT_TIMERFUNCTION) that would inform us of the next potential | ||
602 | // timeout. TODO(bbockelm) we should switch to that in the future. | ||
603 | timeout = 100; | ||
604 | */ | ||
605 | ✗ | timeout = 1; | |
606 | } else { | ||
607 | ✗ | timeout = -1; | |
608 | ✗ | gettimeofday(&timeval_stop, NULL); | |
609 | const int64_t delta = static_cast<int64_t>( | ||
610 | ✗ | 1000 * DiffTimeSeconds(timeval_start, timeval_stop)); | |
611 | ✗ | perf::Xadd(download_mgr->counters_->sz_transfer_time, delta); | |
612 | } | ||
613 | ✗ | const int retval = poll(download_mgr->watch_fds_, | |
614 | ✗ | download_mgr->watch_fds_inuse_, timeout); | |
615 | ✗ | if (retval < 0) { | |
616 | ✗ | continue; | |
617 | } | ||
618 | |||
619 | // Handle timeout | ||
620 | ✗ | if (retval == 0) { | |
621 | ✗ | curl_multi_socket_action(download_mgr->curl_multi_, CURL_SOCKET_TIMEOUT, | |
622 | 0, &still_running); | ||
623 | } | ||
624 | |||
625 | // Terminate I/O thread | ||
626 | ✗ | if (download_mgr->watch_fds_[kIdxPipeTerminate].revents) | |
627 | ✗ | break; | |
628 | |||
629 | // New job arrives | ||
630 | ✗ | if (download_mgr->watch_fds_[kIdxPipeJobs].revents) { | |
631 | ✗ | download_mgr->watch_fds_[kIdxPipeJobs].revents = 0; | |
632 | JobInfo *info; | ||
633 | ✗ | download_mgr->pipe_jobs_->Read<JobInfo *>(&info); | |
634 | ✗ | if (!still_running) { | |
635 | ✗ | gettimeofday(&timeval_start, NULL); | |
636 | } | ||
637 | ✗ | CURL *handle = download_mgr->AcquireCurlHandle(); | |
638 | ✗ | download_mgr->InitializeRequest(info, handle); | |
639 | ✗ | download_mgr->SetUrlOptions(info); | |
640 | ✗ | curl_multi_add_handle(download_mgr->curl_multi_, handle); | |
641 | ✗ | curl_multi_socket_action(download_mgr->curl_multi_, CURL_SOCKET_TIMEOUT, | |
642 | 0, &still_running); | ||
643 | } | ||
644 | |||
645 | // Activity on curl sockets | ||
646 | // Within this loop the curl_multi_socket_action() may cause socket(s) | ||
647 | // to be removed from watch_fds_. If a socket is removed it is replaced | ||
648 | // by the socket at the end of the array and the inuse count is decreased. | ||
649 | // Therefore loop over the array in reverse order. | ||
650 | ✗ | for (int64_t i = download_mgr->watch_fds_inuse_ - 1; i >= 2; --i) { | |
651 | ✗ | if (i >= download_mgr->watch_fds_inuse_) { | |
652 | ✗ | continue; | |
653 | } | ||
654 | ✗ | if (download_mgr->watch_fds_[i].revents) { | |
655 | ✗ | int ev_bitmask = 0; | |
656 | ✗ | if (download_mgr->watch_fds_[i].revents & (POLLIN | POLLPRI)) | |
657 | ✗ | ev_bitmask |= CURL_CSELECT_IN; | |
658 | ✗ | if (download_mgr->watch_fds_[i].revents & (POLLOUT | POLLWRBAND)) | |
659 | ✗ | ev_bitmask |= CURL_CSELECT_OUT; | |
660 | ✗ | if (download_mgr->watch_fds_[i].revents | |
661 | ✗ | & (POLLERR | POLLHUP | POLLNVAL)) { | |
662 | ✗ | ev_bitmask |= CURL_CSELECT_ERR; | |
663 | } | ||
664 | ✗ | download_mgr->watch_fds_[i].revents = 0; | |
665 | |||
666 | ✗ | curl_multi_socket_action(download_mgr->curl_multi_, | |
667 | ✗ | download_mgr->watch_fds_[i].fd, | |
668 | ev_bitmask, | ||
669 | &still_running); | ||
670 | } | ||
671 | } | ||
672 | |||
673 | // Check if transfers are completed | ||
674 | CURLMsg *curl_msg; | ||
675 | int msgs_in_queue; | ||
676 | ✗ | while ((curl_msg = curl_multi_info_read(download_mgr->curl_multi_, | |
677 | &msgs_in_queue))) { | ||
678 | ✗ | if (curl_msg->msg == CURLMSG_DONE) { | |
679 | ✗ | perf::Inc(download_mgr->counters_->n_requests); | |
680 | JobInfo *info; | ||
681 | ✗ | CURL *easy_handle = curl_msg->easy_handle; | |
682 | ✗ | const int curl_error = curl_msg->data.result; | |
683 | ✗ | curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, &info); | |
684 | |||
685 | int64_t redir_count; | ||
686 | ✗ | curl_easy_getinfo(easy_handle, CURLINFO_REDIRECT_COUNT, &redir_count); | |
687 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
688 | "(manager '%s' - id %" PRId64 ") " | ||
689 | "Number of CURL redirects %" PRId64, | ||
690 | download_mgr->name_.c_str(), info->id(), redir_count); | ||
691 | |||
692 | ✗ | curl_multi_remove_handle(download_mgr->curl_multi_, easy_handle); | |
693 | ✗ | if (download_mgr->VerifyAndFinalize(curl_error, info)) { | |
694 | ✗ | curl_multi_add_handle(download_mgr->curl_multi_, easy_handle); | |
695 | ✗ | curl_multi_socket_action(download_mgr->curl_multi_, | |
696 | CURL_SOCKET_TIMEOUT, | ||
697 | 0, | ||
698 | &still_running); | ||
699 | } else { | ||
700 | // Return easy handle into pool and write result back | ||
701 | ✗ | download_mgr->ReleaseCurlHandle(easy_handle); | |
702 | |||
703 | ✗ | DataTubeElement *ele = new DataTubeElement(kActionStop); | |
704 | ✗ | info->GetDataTubePtr()->EnqueueBack(ele); | |
705 | ✗ | info->GetPipeJobResultPtr()->Write<download::Failures>( | |
706 | ✗ | info->error_code()); | |
707 | } | ||
708 | } | ||
709 | } | ||
710 | } | ||
711 | |||
712 | ✗ | for (set<CURL *>::iterator i = download_mgr->pool_handles_inuse_->begin(), | |
713 | ✗ | iEnd = download_mgr->pool_handles_inuse_->end(); | |
714 | ✗ | i != iEnd; | |
715 | ✗ | ++i) { | |
716 | ✗ | curl_multi_remove_handle(download_mgr->curl_multi_, *i); | |
717 | ✗ | curl_easy_cleanup(*i); | |
718 | } | ||
719 | ✗ | download_mgr->pool_handles_inuse_->clear(); | |
720 | ✗ | free(download_mgr->watch_fds_); | |
721 | |||
722 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
723 | "download I/O thread of DownloadManager '%s' terminated", | ||
724 | download_mgr->name_.c_str()); | ||
725 | ✗ | return NULL; | |
726 | } | ||
727 | |||
728 | |||
729 | //------------------------------------------------------------------------------ | ||
730 | |||
731 | |||
732 | 3170 | HeaderLists::~HeaderLists() { | |
733 |
2/2✓ Branch 1 taken 3171 times.
✓ Branch 2 taken 3170 times.
|
6341 | for (unsigned i = 0; i < blocks_.size(); ++i) { |
734 |
1/2✓ Branch 1 taken 3171 times.
✗ Branch 2 not taken.
|
3171 | delete[] blocks_[i]; |
735 | } | ||
736 | 3170 | blocks_.clear(); | |
737 | 3170 | } | |
738 | |||
739 | |||
740 | 6869 | curl_slist *HeaderLists::GetList(const char *header) { return Get(header); } | |
741 | |||
742 | |||
743 | 3181 | curl_slist *HeaderLists::DuplicateList(curl_slist *slist) { | |
744 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3181 times.
|
3181 | assert(slist); |
745 | 3181 | curl_slist *copy = GetList(slist->data); | |
746 | 3181 | copy->next = slist->next; | |
747 | 3181 | curl_slist *prev = copy; | |
748 | 3181 | slist = slist->next; | |
749 |
2/2✓ Branch 0 taken 6362 times.
✓ Branch 1 taken 3181 times.
|
9543 | while (slist) { |
750 | 6362 | curl_slist *new_link = Get(slist->data); | |
751 | 6362 | new_link->next = slist->next; | |
752 | 6362 | prev->next = new_link; | |
753 | 6362 | prev = new_link; | |
754 | 6362 | slist = slist->next; | |
755 | } | ||
756 | 3181 | return copy; | |
757 | } | ||
758 | |||
759 | |||
760 | 6506 | void HeaderLists::AppendHeader(curl_slist *slist, const char *header) { | |
761 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6506 times.
|
6506 | assert(slist); |
762 | 6506 | curl_slist *new_link = Get(header); | |
763 | 6506 | new_link->next = NULL; | |
764 | |||
765 |
2/2✓ Branch 0 taken 3580 times.
✓ Branch 1 taken 6506 times.
|
10086 | while (slist->next) |
766 | 3580 | slist = slist->next; | |
767 | 6506 | slist->next = new_link; | |
768 | 6506 | } | |
769 | |||
770 | |||
771 | /** | ||
772 | * Ensures that a certain header string is _not_ part of slist on return. | ||
773 | * Note that if the first header element matches, the returned slist points | ||
774 | * to a different value. | ||
775 | */ | ||
776 | 5 | void HeaderLists::CutHeader(const char *header, curl_slist **slist) { | |
777 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | assert(slist); |
778 | curl_slist head; | ||
779 | 5 | head.next = *slist; | |
780 | 5 | curl_slist *prev = &head; | |
781 | 5 | curl_slist *rover = *slist; | |
782 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 5 times.
|
14 | while (rover) { |
783 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5 times.
|
9 | if (strcmp(rover->data, header) == 0) { |
784 | 4 | prev->next = rover->next; | |
785 | 4 | Put(rover); | |
786 | 4 | rover = prev; | |
787 | } | ||
788 | 9 | prev = rover; | |
789 | 9 | rover = rover->next; | |
790 | } | ||
791 | 5 | *slist = head.next; | |
792 | 5 | } | |
793 | |||
794 | |||
795 | 3441 | void HeaderLists::PutList(curl_slist *slist) { | |
796 |
2/2✓ Branch 0 taken 9969 times.
✓ Branch 1 taken 3441 times.
|
13410 | while (slist) { |
797 | 9969 | curl_slist *next = slist->next; | |
798 | 9969 | Put(slist); | |
799 | 9969 | slist = next; | |
800 | } | ||
801 | 3441 | } | |
802 | |||
803 | |||
804 | 4 | string HeaderLists::Print(curl_slist *slist) { | |
805 | 4 | string verbose; | |
806 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
|
12 | while (slist) { |
807 |
3/6✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
|
8 | verbose += string(slist->data) + "\n"; |
808 | 8 | slist = slist->next; | |
809 | } | ||
810 | 4 | return verbose; | |
811 | } | ||
812 | |||
813 | |||
814 | 19737 | curl_slist *HeaderLists::Get(const char *header) { | |
815 |
2/2✓ Branch 1 taken 16565 times.
✓ Branch 2 taken 3173 times.
|
19738 | for (unsigned i = 0; i < blocks_.size(); ++i) { |
816 |
2/2✓ Branch 0 taken 131099 times.
✓ Branch 1 taken 1 times.
|
131100 | for (unsigned j = 0; j < kBlockSize; ++j) { |
817 |
2/2✓ Branch 2 taken 16564 times.
✓ Branch 3 taken 114535 times.
|
131099 | if (!IsUsed(&(blocks_[i][j]))) { |
818 | 16564 | blocks_[i][j].data = const_cast<char *>(header); | |
819 | 16564 | return &(blocks_[i][j]); | |
820 | } | ||
821 | } | ||
822 | } | ||
823 | |||
824 | // All used, new block | ||
825 | 3173 | AddBlock(); | |
826 | 3173 | blocks_[blocks_.size() - 1][0].data = const_cast<char *>(header); | |
827 | 3173 | return &(blocks_[blocks_.size() - 1][0]); | |
828 | } | ||
829 | |||
830 | |||
831 | 822261 | void HeaderLists::Put(curl_slist *slist) { | |
832 | 822261 | slist->data = NULL; | |
833 | 822261 | slist->next = NULL; | |
834 | 822261 | } | |
835 | |||
836 | |||
837 | 3173 | void HeaderLists::AddBlock() { | |
838 |
1/2✓ Branch 1 taken 3173 times.
✗ Branch 2 not taken.
|
3173 | curl_slist *new_block = new curl_slist[kBlockSize]; |
839 |
2/2✓ Branch 0 taken 812288 times.
✓ Branch 1 taken 3173 times.
|
815461 | for (unsigned i = 0; i < kBlockSize; ++i) { |
840 | 812288 | Put(&new_block[i]); | |
841 | } | ||
842 |
1/2✓ Branch 1 taken 3173 times.
✗ Branch 2 not taken.
|
3173 | blocks_.push_back(new_block); |
843 | 3173 | } | |
844 | |||
845 | |||
846 | //------------------------------------------------------------------------------ | ||
847 | |||
848 | |||
849 | ✗ | string DownloadManager::ProxyInfo::Print() { | |
850 | ✗ | if (url == "DIRECT") | |
851 | ✗ | return url; | |
852 | |||
853 | ✗ | string result = url; | |
854 | ✗ | const int remaining = static_cast<int>(host.deadline()) | |
855 | ✗ | - static_cast<int>(time(NULL)); | |
856 | ✗ | string expinfo = (remaining >= 0) ? "+" : ""; | |
857 | ✗ | if (abs(remaining) >= 3600) { | |
858 | ✗ | expinfo += StringifyInt(remaining / 3600) + "h"; | |
859 | ✗ | } else if (abs(remaining) >= 60) { | |
860 | ✗ | expinfo += StringifyInt(remaining / 60) + "m"; | |
861 | } else { | ||
862 | ✗ | expinfo += StringifyInt(remaining) + "s"; | |
863 | } | ||
864 | ✗ | if (host.status() == dns::kFailOk) { | |
865 | ✗ | result += " (" + host.name() + ", " + expinfo + ")"; | |
866 | } else { | ||
867 | ✗ | result += " (:unresolved:, " + expinfo + ")"; | |
868 | } | ||
869 | ✗ | return result; | |
870 | } | ||
871 | |||
872 | |||
873 | /** | ||
874 | * Gets an idle CURL handle from the pool. Creates a new one and adds it to | ||
875 | * the pool if necessary. | ||
876 | */ | ||
877 | 3181 | CURL *DownloadManager::AcquireCurlHandle() { | |
878 | CURL *handle; | ||
879 | |||
880 |
2/2✓ Branch 1 taken 1487 times.
✓ Branch 2 taken 1694 times.
|
3181 | if (pool_handles_idle_->empty()) { |
881 | // Create a new handle | ||
882 |
1/2✓ Branch 1 taken 1487 times.
✗ Branch 2 not taken.
|
1487 | handle = curl_easy_init(); |
883 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1487 times.
|
1487 | assert(handle != NULL); |
884 | |||
885 |
1/2✓ Branch 1 taken 1487 times.
✗ Branch 2 not taken.
|
1487 | curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1); |
886 | // curl_easy_setopt(curl_default, CURLOPT_FAILONERROR, 1); | ||
887 |
1/2✓ Branch 1 taken 1487 times.
✗ Branch 2 not taken.
|
1487 | curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, CallbackCurlHeader); |
888 |
1/2✓ Branch 1 taken 1487 times.
✗ Branch 2 not taken.
|
1487 | curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, CallbackCurlData); |
889 | } else { | ||
890 | 1694 | handle = *(pool_handles_idle_->begin()); | |
891 |
1/2✓ Branch 2 taken 1694 times.
✗ Branch 3 not taken.
|
1694 | pool_handles_idle_->erase(pool_handles_idle_->begin()); |
892 | } | ||
893 | |||
894 |
1/2✓ Branch 1 taken 3181 times.
✗ Branch 2 not taken.
|
3181 | pool_handles_inuse_->insert(handle); |
895 | |||
896 | 3181 | return handle; | |
897 | } | ||
898 | |||
899 | |||
900 | 3181 | void DownloadManager::ReleaseCurlHandle(CURL *handle) { | |
901 |
1/2✓ Branch 1 taken 3181 times.
✗ Branch 2 not taken.
|
3181 | const set<CURL *>::iterator elem = pool_handles_inuse_->find(handle); |
902 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 3181 times.
|
3181 | assert(elem != pool_handles_inuse_->end()); |
903 | |||
904 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3181 times.
|
3181 | if (pool_handles_idle_->size() > pool_max_handles_) { |
905 | ✗ | curl_easy_cleanup(*elem); | |
906 | } else { | ||
907 |
1/2✓ Branch 2 taken 3181 times.
✗ Branch 3 not taken.
|
3181 | pool_handles_idle_->insert(*elem); |
908 | } | ||
909 | |||
910 |
1/2✓ Branch 1 taken 3181 times.
✗ Branch 2 not taken.
|
3181 | pool_handles_inuse_->erase(elem); |
911 | 3181 | } | |
912 | |||
913 | |||
914 | /** | ||
915 | * HTTP request options: set the URL and other options such as timeout and | ||
916 | * proxy. | ||
917 | */ | ||
918 | 3181 | void DownloadManager::InitializeRequest(JobInfo *info, CURL *handle) { | |
919 | // Initialize internal download state | ||
920 | 3181 | info->SetCurlHandle(handle); | |
921 | 3181 | info->SetErrorCode(kFailOk); | |
922 | 3181 | info->SetHttpCode(-1); | |
923 | 3181 | info->SetFollowRedirects(follow_redirects_); | |
924 | 3181 | info->SetNumUsedProxies(1); | |
925 | 3181 | info->SetNumUsedMetalinks(1); | |
926 | 3181 | info->SetNumUsedHosts(1); | |
927 | 3181 | info->SetNumRetries(0); | |
928 | 3181 | info->SetBackoffMs(0); | |
929 | 3181 | info->SetHeaders(header_lists_->DuplicateList(default_headers_)); | |
930 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3181 times.
|
3181 | if (info->info_header()) { |
931 | ✗ | header_lists_->AppendHeader(info->headers(), info->info_header()); | |
932 | } | ||
933 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3181 times.
|
3181 | if (enable_http_tracing_) { |
934 | ✗ | for (unsigned int i = 0; i < http_tracing_headers_.size(); i++) { | |
935 | ✗ | header_lists_->AppendHeader(info->headers(), | |
936 | ✗ | (http_tracing_headers_)[i].c_str()); | |
937 | } | ||
938 | |||
939 | ✗ | header_lists_->AppendHeader(info->headers(), info->tracing_header_pid()); | |
940 | ✗ | header_lists_->AppendHeader(info->headers(), info->tracing_header_gid()); | |
941 | ✗ | header_lists_->AppendHeader(info->headers(), info->tracing_header_uid()); | |
942 | |||
943 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
944 | "(manager '%s' - id %" PRId64 ") " | ||
945 | "CURL Header for URL: %s is:\n %s", | ||
946 | name_.c_str(), info->id(), info->url()->c_str(), | ||
947 | ✗ | header_lists_->Print(info->headers()).c_str()); | |
948 | } | ||
949 | |||
950 |
2/2✓ Branch 1 taken 48 times.
✓ Branch 2 taken 3133 times.
|
3181 | if (info->force_nocache()) { |
951 | 48 | SetNocache(info); | |
952 | } else { | ||
953 | 3133 | info->SetNocache(false); | |
954 | } | ||
955 |
2/2✓ Branch 1 taken 2093 times.
✓ Branch 2 taken 1088 times.
|
3181 | if (info->compressed()) { |
956 | 2093 | zlib::DecompressInit(info->GetZstreamPtr()); | |
957 | } | ||
958 |
2/2✓ Branch 1 taken 2094 times.
✓ Branch 2 taken 1087 times.
|
3181 | if (info->expected_hash()) { |
959 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2094 times.
|
2094 | assert(info->hash_context().buffer != NULL); |
960 | 2094 | shash::Init(info->hash_context()); | |
961 | } | ||
962 | |||
963 |
2/6✗ Branch 1 not taken.
✓ Branch 2 taken 3181 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 3181 times.
|
3181 | if ((info->range_offset() != -1) && (info->range_size())) { |
964 | char byte_range_array[100]; | ||
965 | ✗ | const int64_t range_lower = static_cast<int64_t>(info->range_offset()); | |
966 | ✗ | const int64_t range_upper = static_cast<int64_t>(info->range_offset() | |
967 | ✗ | + info->range_size() - 1); | |
968 | ✗ | if (snprintf(byte_range_array, sizeof(byte_range_array), | |
969 | "%" PRId64 "-%" PRId64, range_lower, range_upper) | ||
970 | ✗ | == 100) { | |
971 | ✗ | PANIC(NULL); // Should be impossible given limits on offset size. | |
972 | } | ||
973 | ✗ | curl_easy_setopt(handle, CURLOPT_RANGE, byte_range_array); | |
974 | } else { | ||
975 | 3181 | curl_easy_setopt(handle, CURLOPT_RANGE, NULL); | |
976 | } | ||
977 | |||
978 | // Set curl parameters | ||
979 | 3181 | curl_easy_setopt(handle, CURLOPT_PRIVATE, static_cast<void *>(info)); | |
980 | 3181 | curl_easy_setopt(handle, CURLOPT_WRITEHEADER, static_cast<void *>(info)); | |
981 | 3181 | curl_easy_setopt(handle, CURLOPT_WRITEDATA, static_cast<void *>(info)); | |
982 | 3181 | curl_easy_setopt(handle, CURLOPT_HTTPHEADER, info->headers()); | |
983 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3181 times.
|
3181 | if (info->head_request()) { |
984 | ✗ | curl_easy_setopt(handle, CURLOPT_NOBODY, 1); | |
985 | } else { | ||
986 | 3181 | curl_easy_setopt(handle, CURLOPT_HTTPGET, 1); | |
987 | } | ||
988 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3181 times.
|
3181 | if (opt_ipv4_only_) { |
989 | ✗ | curl_easy_setopt(handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); | |
990 | } | ||
991 |
2/2✓ Branch 0 taken 842 times.
✓ Branch 1 taken 2339 times.
|
3181 | if (follow_redirects_) { |
992 | 842 | curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1); | |
993 | 842 | curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 4); | |
994 | } | ||
995 | #ifdef DEBUGMSG | ||
996 | 3181 | curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); | |
997 | 3181 | curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, CallbackCurlDebug); | |
998 | #endif | ||
999 | 3181 | } | |
1000 | |||
1001 | 6458 | void DownloadManager::CheckHostInfoReset(const std::string &typ, | |
1002 | HostInfo &info, | ||
1003 | JobInfo *jobinfo, | ||
1004 | time_t &now) { | ||
1005 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6458 times.
|
6458 | if (info.timestamp_backup > 0) { |
1006 | ✗ | if (now == 0) | |
1007 | ✗ | now = time(NULL); | |
1008 | ✗ | if (static_cast<int64_t>(now) | |
1009 | ✗ | > static_cast<int64_t>(info.timestamp_backup + info.reset_after)) { | |
1010 | ✗ | LogCvmfs(kLogDownload, kLogDebug | kLogSyslogWarn, | |
1011 | "(manager %s - id %" PRId64 ") " | ||
1012 | "switching %s from %s to %s (reset %s)", | ||
1013 | name_.c_str(), jobinfo->id(), typ.c_str(), | ||
1014 | ✗ | (*info.chain)[info.current].c_str(), (*info.chain)[0].c_str(), | |
1015 | typ.c_str()); | ||
1016 | ✗ | info.current = 0; | |
1017 | ✗ | info.timestamp_backup = 0; | |
1018 | } | ||
1019 | } | ||
1020 | 6458 | } | |
1021 | |||
1022 | |||
1023 | /** | ||
1024 | * Sets the URL specific options such as host to use and timeout. It might also | ||
1025 | * set an error code, in which case the further processing should react on. | ||
1026 | */ | ||
1027 | 3229 | void DownloadManager::SetUrlOptions(JobInfo *info) { | |
1028 | 3229 | CURL *curl_handle = info->curl_handle(); | |
1029 | 3229 | string url_prefix; | |
1030 | 3229 | time_t now = 0; | |
1031 | |||
1032 | 3229 | const MutexLockGuard m(lock_options_); | |
1033 | |||
1034 | // sharding policy | ||
1035 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3229 times.
|
3229 | if (sharding_policy_.UseCount() > 0) { |
1036 | ✗ | if (info->proxy() != "") { | |
1037 | // proxy already set, so this is a failover event | ||
1038 | ✗ | perf::Inc(counters_->n_proxy_failover); | |
1039 | } | ||
1040 | ✗ | info->SetProxy(sharding_policy_->GetNextProxy( | |
1041 | ✗ | info->url(), info->proxy(), | |
1042 | ✗ | info->range_offset() == -1 ? 0 : info->range_offset())); | |
1043 | |||
1044 | ✗ | curl_easy_setopt(info->curl_handle(), CURLOPT_PROXY, info->proxy().c_str()); | |
1045 | } else { // no sharding policy | ||
1046 | // Check if proxy group needs to be reset from backup to primary | ||
1047 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3229 times.
|
3229 | if (opt_timestamp_backup_proxies_ > 0) { |
1048 | ✗ | now = time(NULL); | |
1049 | ✗ | if (static_cast<int64_t>(now) > static_cast<int64_t>( | |
1050 | ✗ | opt_timestamp_backup_proxies_ + opt_proxy_groups_reset_after_)) { | |
1051 | ✗ | opt_proxy_groups_current_ = 0; | |
1052 | ✗ | opt_timestamp_backup_proxies_ = 0; | |
1053 | ✗ | RebalanceProxiesUnlocked("Reset proxy group from backup to primary"); | |
1054 | } | ||
1055 | } | ||
1056 | // Check if load-balanced proxies within the group need to be reset | ||
1057 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3229 times.
|
3229 | if (opt_timestamp_failover_proxies_ > 0) { |
1058 | ✗ | if (now == 0) | |
1059 | ✗ | now = time(NULL); | |
1060 | ✗ | if (static_cast<int64_t>(now) | |
1061 | ✗ | > static_cast<int64_t>(opt_timestamp_failover_proxies_ | |
1062 | ✗ | + opt_proxy_groups_reset_after_)) { | |
1063 | ✗ | RebalanceProxiesUnlocked( | |
1064 | "Reset load-balanced proxies within the active group"); | ||
1065 | } | ||
1066 | } | ||
1067 | |||
1068 |
1/2✓ Branch 2 taken 3229 times.
✗ Branch 3 not taken.
|
3229 | ProxyInfo *proxy = ChooseProxyUnlocked(info->expected_hash()); |
1069 |
6/6✓ Branch 0 taken 1435 times.
✓ Branch 1 taken 1794 times.
✓ Branch 3 taken 1371 times.
✓ Branch 4 taken 64 times.
✓ Branch 5 taken 3165 times.
✓ Branch 6 taken 64 times.
|
3229 | if (!proxy || (proxy->url == "DIRECT")) { |
1070 |
2/4✓ Branch 2 taken 3165 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3165 times.
✗ Branch 6 not taken.
|
3165 | info->SetProxy("DIRECT"); |
1071 |
1/2✓ Branch 2 taken 3165 times.
✗ Branch 3 not taken.
|
3165 | curl_easy_setopt(info->curl_handle(), CURLOPT_PROXY, ""); |
1072 | } else { | ||
1073 | // Note: inside ValidateProxyIpsUnlocked() we may change the proxy data | ||
1074 | // structure, so we must not pass proxy->... (== current_proxy()) | ||
1075 | // parameters directly | ||
1076 |
1/2✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
|
64 | const std::string purl = proxy->url; |
1077 |
1/2✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
|
64 | const dns::Host phost = proxy->host; |
1078 |
1/2✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
|
64 | const bool changed = ValidateProxyIpsUnlocked(purl, phost); |
1079 | // Current proxy may have changed | ||
1080 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
|
64 | if (changed) { |
1081 | ✗ | proxy = ChooseProxyUnlocked(info->expected_hash()); | |
1082 | } | ||
1083 |
1/2✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
|
64 | info->SetProxy(proxy->url); |
1084 |
1/2✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
|
64 | if (proxy->host.status() == dns::kFailOk) { |
1085 |
2/4✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 64 times.
✗ Branch 7 not taken.
|
64 | curl_easy_setopt(info->curl_handle(), CURLOPT_PROXY, |
1086 | info->proxy().c_str()); | ||
1087 | } else { | ||
1088 | // We know it can't work, don't even try to download | ||
1089 | ✗ | curl_easy_setopt(info->curl_handle(), CURLOPT_PROXY, "0.0.0.0"); | |
1090 | } | ||
1091 | 64 | } | |
1092 | } // end !sharding | ||
1093 | |||
1094 | // Check if metalink and host chains need to be reset | ||
1095 |
2/4✓ Branch 2 taken 3229 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3229 times.
✗ Branch 6 not taken.
|
3229 | CheckHostInfoReset("metalink", opt_metalink_, info, now); |
1096 |
2/4✓ Branch 2 taken 3229 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3229 times.
✗ Branch 6 not taken.
|
3229 | CheckHostInfoReset("host", opt_host_, info, now); |
1097 | |||
1098 |
1/2✓ Branch 1 taken 3229 times.
✗ Branch 2 not taken.
|
3229 | curl_easy_setopt(curl_handle, CURLOPT_LOW_SPEED_LIMIT, opt_low_speed_limit_); |
1099 |
3/6✓ Branch 1 taken 3229 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 64 times.
✓ Branch 6 taken 3165 times.
|
3229 | if (info->proxy() != "DIRECT") { |
1100 |
1/2✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
|
64 | curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, opt_timeout_proxy_); |
1101 |
1/2✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
|
64 | curl_easy_setopt(curl_handle, CURLOPT_LOW_SPEED_TIME, opt_timeout_proxy_); |
1102 | } else { | ||
1103 |
1/2✓ Branch 1 taken 3165 times.
✗ Branch 2 not taken.
|
3165 | curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, opt_timeout_direct_); |
1104 |
1/2✓ Branch 1 taken 3165 times.
✗ Branch 2 not taken.
|
3165 | curl_easy_setopt(curl_handle, CURLOPT_LOW_SPEED_TIME, opt_timeout_direct_); |
1105 | } | ||
1106 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3229 times.
|
3229 | if (!opt_dns_server_.empty()) |
1107 | ✗ | curl_easy_setopt(curl_handle, CURLOPT_DNS_SERVERS, opt_dns_server_.c_str()); | |
1108 | |||
1109 |
2/2✓ Branch 1 taken 1603 times.
✓ Branch 2 taken 1626 times.
|
3229 | if (info->probe_hosts()) { |
1110 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1603 times.
|
1603 | if (CheckMetalinkChain(now)) { |
1111 | ✗ | url_prefix = (*opt_metalink_.chain)[opt_metalink_.current]; | |
1112 | ✗ | info->SetCurrentMetalinkChainIndex(opt_metalink_.current); | |
1113 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
1114 | "(manager %s - id %" PRId64 ") " | ||
1115 | "reading from metalink %d", | ||
1116 | name_.c_str(), info->id(), opt_metalink_.current); | ||
1117 |
1/2✓ Branch 0 taken 1603 times.
✗ Branch 1 not taken.
|
1603 | } else if (opt_host_.chain) { |
1118 |
1/2✓ Branch 2 taken 1603 times.
✗ Branch 3 not taken.
|
1603 | url_prefix = (*opt_host_.chain)[opt_host_.current]; |
1119 | 1603 | info->SetCurrentHostChainIndex(opt_host_.current); | |
1120 |
1/2✓ Branch 3 taken 1603 times.
✗ Branch 4 not taken.
|
1603 | LogCvmfs(kLogDownload, kLogDebug, |
1121 | "(manager %s - id %" PRId64 ") " | ||
1122 | "reading from host %d", | ||
1123 | name_.c_str(), info->id(), opt_host_.current); | ||
1124 | } | ||
1125 | } | ||
1126 | |||
1127 |
1/2✓ Branch 2 taken 3229 times.
✗ Branch 3 not taken.
|
3229 | string url = url_prefix + *(info->url()); |
1128 | |||
1129 |
1/2✓ Branch 1 taken 3229 times.
✗ Branch 2 not taken.
|
3229 | curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 1L); |
1130 |
2/6✓ Branch 1 taken 3229 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 3229 times.
|
3229 | if (url.substr(0, 5) == "https") { |
1131 | ✗ | const bool rvb = ssl_certificate_store_.ApplySslCertificatePath( | |
1132 | curl_handle); | ||
1133 | ✗ | if (!rvb) { | |
1134 | ✗ | LogCvmfs(kLogDownload, kLogDebug | kLogSyslogWarn, | |
1135 | "(manager %s - id %" PRId64 ") " | ||
1136 | "Failed to set SSL certificate path %s", | ||
1137 | name_.c_str(), info->id(), | ||
1138 | ✗ | ssl_certificate_store_.GetCaPath().c_str()); | |
1139 | } | ||
1140 | ✗ | if (info->pid() != -1) { | |
1141 | ✗ | if (credentials_attachment_ == NULL) { | |
1142 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
1143 | "(manager %s - id %" PRId64 ") " | ||
1144 | "uses secure downloads but no credentials attachment set", | ||
1145 | name_.c_str(), info->id()); | ||
1146 | } else { | ||
1147 | ✗ | const bool retval = credentials_attachment_->ConfigureCurlHandle( | |
1148 | curl_handle, info->pid(), info->GetCredDataPtr()); | ||
1149 | ✗ | if (!retval) { | |
1150 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
1151 | "(manager %s - id %" PRId64 ") " | ||
1152 | "failed attaching credentials", | ||
1153 | name_.c_str(), info->id()); | ||
1154 | } | ||
1155 | } | ||
1156 | } | ||
1157 | // The download manager disables signal handling in the curl library; | ||
1158 | // as OpenSSL's implementation of TLS will generate a sigpipe in some | ||
1159 | // error paths, we must explicitly disable SIGPIPE here. | ||
1160 | // TODO(jblomer): it should be enough to do this once | ||
1161 | ✗ | signal(SIGPIPE, SIG_IGN); | |
1162 | } | ||
1163 | |||
1164 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3229 times.
|
3229 | if (url.find("@proxy@") != string::npos) { |
1165 | // This is used in Geo-API requests (only), to replace a portion of the | ||
1166 | // URL with the current proxy name for the sake of caching the result. | ||
1167 | // Replace the @proxy@ either with a passed in "forced" template (which | ||
1168 | // is set from $CVMFS_PROXY_TEMPLATE) if there is one, or a "direct" | ||
1169 | // template (which is the uuid) if there's no proxy, or the name of the | ||
1170 | // proxy. | ||
1171 | ✗ | string replacement; | |
1172 | ✗ | if (proxy_template_forced_ != "") { | |
1173 | ✗ | replacement = proxy_template_forced_; | |
1174 | ✗ | } else if (info->proxy() == "DIRECT") { | |
1175 | ✗ | replacement = proxy_template_direct_; | |
1176 | } else { | ||
1177 | ✗ | if (opt_proxy_groups_current_ >= opt_proxy_groups_fallback_) { | |
1178 | // It doesn't make sense to use the fallback proxies in Geo-API requests | ||
1179 | // since the fallback proxies are supposed to get sorted, too. | ||
1180 | ✗ | info->SetProxy("DIRECT"); | |
1181 | ✗ | curl_easy_setopt(info->curl_handle(), CURLOPT_PROXY, ""); | |
1182 | ✗ | replacement = proxy_template_direct_; | |
1183 | } else { | ||
1184 | ✗ | replacement = ChooseProxyUnlocked(info->expected_hash())->host.name(); | |
1185 | } | ||
1186 | } | ||
1187 | ✗ | replacement = (replacement == "") ? proxy_template_direct_ : replacement; | |
1188 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
1189 | "(manager %s - id %" PRId64 ") " | ||
1190 | "replacing @proxy@ by %s", | ||
1191 | name_.c_str(), info->id(), replacement.c_str()); | ||
1192 | ✗ | url = ReplaceAll(url, "@proxy@", replacement); | |
1193 | } | ||
1194 | |||
1195 | // TODO(heretherebedragons) before removing | ||
1196 | // static_cast<cvmfs::MemSink*>(info->sink)->size() == 0 | ||
1197 | // and just always call info->sink->Reserve() | ||
1198 | // we should do a speed check | ||
1199 |
3/4✓ Branch 2 taken 3229 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1333 times.
✓ Branch 5 taken 1896 times.
|
3229 | if ((info->sink() != NULL) && info->sink()->RequiresReserve() |
1200 |
1/2✓ Branch 2 taken 1333 times.
✗ Branch 3 not taken.
|
1333 | && (static_cast<cvmfs::MemSink *>(info->sink())->size() == 0) |
1201 |
11/20✓ Branch 1 taken 3229 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1333 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 1333 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1100 times.
✓ Branch 11 taken 233 times.
✓ Branch 12 taken 1333 times.
✓ Branch 13 taken 1896 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 1333 times.
✓ Branch 16 taken 1896 times.
✓ Branch 18 taken 1100 times.
✓ Branch 19 taken 2129 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
|
6458 | && HasPrefix(url, "file://", false)) { |
1202 | platform_stat64 stat_buf; | ||
1203 | 1100 | const int retval = platform_stat(url.c_str(), &stat_buf); | |
1204 |
1/2✓ Branch 0 taken 1100 times.
✗ Branch 1 not taken.
|
1100 | if (retval != 0) { |
1205 | // this is an error: file does not exist or out of memory | ||
1206 | // error is caught in other code section. | ||
1207 |
1/2✓ Branch 2 taken 1100 times.
✗ Branch 3 not taken.
|
1100 | info->sink()->Reserve(64ul * 1024ul); |
1208 | } else { | ||
1209 | ✗ | info->sink()->Reserve(stat_buf.st_size); | |
1210 | } | ||
1211 | } | ||
1212 | |||
1213 |
2/4✓ Branch 2 taken 3229 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 3229 times.
✗ Branch 7 not taken.
|
3229 | curl_easy_setopt(curl_handle, CURLOPT_URL, |
1214 | EscapeUrl(info->id(), url).c_str()); | ||
1215 | 3229 | } | |
1216 | |||
1217 | |||
1218 | /** | ||
1219 | * Checks if the name resolving information is still up to date. The host | ||
1220 | * object should be one from the current load-balance group. If the information | ||
1221 | * changed, gather new set of resolved IPs and, if different, exchange them in | ||
1222 | * the load-balance group on the fly. In the latter case, also rebalance the | ||
1223 | * proxies. The options mutex needs to be open. | ||
1224 | * | ||
1225 | * Returns true if proxies may have changed. | ||
1226 | */ | ||
1227 | 64 | bool DownloadManager::ValidateProxyIpsUnlocked(const string &url, | |
1228 | const dns::Host &host) { | ||
1229 |
2/4✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 64 times.
✗ Branch 4 not taken.
|
64 | if (!host.IsExpired()) |
1230 | 64 | return false; | |
1231 | ✗ | LogCvmfs(kLogDownload, kLogDebug, "(manager '%s') validate DNS entry for %s", | |
1232 | ✗ | name_.c_str(), host.name().c_str()); | |
1233 | |||
1234 | ✗ | const unsigned group_idx = opt_proxy_groups_current_; | |
1235 | ✗ | dns::Host new_host = resolver_->Resolve(host.name()); | |
1236 | |||
1237 | ✗ | bool update_only = true; // No changes to the list of IP addresses. | |
1238 | ✗ | if (new_host.status() != dns::kFailOk) { | |
1239 | // Try again later in case resolving fails. | ||
1240 | ✗ | LogCvmfs(kLogDownload, kLogDebug | kLogSyslogWarn, | |
1241 | "(manager '%s') failed to resolve IP addresses for %s (%d - %s)", | ||
1242 | ✗ | name_.c_str(), host.name().c_str(), new_host.status(), | |
1243 | dns::Code2Ascii(new_host.status())); | ||
1244 | ✗ | new_host = dns::Host::ExtendDeadline(host, resolver_->min_ttl()); | |
1245 | ✗ | } else if (!host.IsEquivalent(new_host)) { | |
1246 | ✗ | update_only = false; | |
1247 | } | ||
1248 | |||
1249 | ✗ | if (update_only) { | |
1250 | ✗ | for (unsigned i = 0; i < (*opt_proxy_groups_)[group_idx].size(); ++i) { | |
1251 | ✗ | if ((*opt_proxy_groups_)[group_idx][i].host.id() == host.id()) | |
1252 | ✗ | (*opt_proxy_groups_)[group_idx][i].host = new_host; | |
1253 | } | ||
1254 | ✗ | return false; | |
1255 | } | ||
1256 | |||
1257 | ✗ | assert(new_host.status() == dns::kFailOk); | |
1258 | |||
1259 | // Remove old host objects, insert new objects, and rebalance. | ||
1260 | ✗ | LogCvmfs(kLogDownload, kLogDebug | kLogSyslog, | |
1261 | "(manager '%s') DNS entries for proxy %s changed, adjusting", | ||
1262 | ✗ | name_.c_str(), host.name().c_str()); | |
1263 | ✗ | vector<ProxyInfo> *group = current_proxy_group(); | |
1264 | ✗ | opt_num_proxies_ -= group->size(); | |
1265 | ✗ | for (unsigned i = 0; i < group->size();) { | |
1266 | ✗ | if ((*group)[i].host.id() == host.id()) { | |
1267 | ✗ | group->erase(group->begin() + i); | |
1268 | } else { | ||
1269 | ✗ | i++; | |
1270 | } | ||
1271 | } | ||
1272 | ✗ | vector<ProxyInfo> new_infos; | |
1273 | ✗ | set<string> best_addresses = new_host.ViewBestAddresses(opt_ip_preference_); | |
1274 | ✗ | set<string>::const_iterator iter_ips = best_addresses.begin(); | |
1275 | ✗ | for (; iter_ips != best_addresses.end(); ++iter_ips) { | |
1276 | ✗ | const string url_ip = dns::RewriteUrl(url, *iter_ips); | |
1277 | ✗ | new_infos.push_back(ProxyInfo(new_host, url_ip)); | |
1278 | } | ||
1279 | ✗ | group->insert(group->end(), new_infos.begin(), new_infos.end()); | |
1280 | ✗ | opt_num_proxies_ += new_infos.size(); | |
1281 | |||
1282 | ✗ | const std::string msg = "DNS entries for proxy " + host.name() + " changed"; | |
1283 | |||
1284 | ✗ | RebalanceProxiesUnlocked(msg); | |
1285 | ✗ | return true; | |
1286 | } | ||
1287 | |||
1288 | |||
1289 | /** | ||
1290 | * Adds transfer time and downloaded bytes to the global counters. | ||
1291 | */ | ||
1292 | 3371 | void DownloadManager::UpdateStatistics(CURL *handle) { | |
1293 | double val; | ||
1294 | int retval; | ||
1295 | 3371 | int64_t sum = 0; | |
1296 | |||
1297 |
1/2✓ Branch 1 taken 3371 times.
✗ Branch 2 not taken.
|
3371 | retval = curl_easy_getinfo(handle, CURLINFO_SIZE_DOWNLOAD, &val); |
1298 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3371 times.
|
3371 | assert(retval == CURLE_OK); |
1299 | 3371 | sum += static_cast<int64_t>(val); | |
1300 | /*retval = curl_easy_getinfo(handle, CURLINFO_HEADER_SIZE, &val); | ||
1301 | assert(retval == CURLE_OK); | ||
1302 | sum += static_cast<int64_t>(val);*/ | ||
1303 | 3371 | perf::Xadd(counters_->sz_transferred_bytes, sum); | |
1304 | 3371 | } | |
1305 | |||
1306 | |||
1307 | /** | ||
1308 | * Retry if possible if not on no-cache and if not already done too often. | ||
1309 | */ | ||
1310 | 3371 | bool DownloadManager::CanRetry(const JobInfo *info) { | |
1311 | 3371 | const MutexLockGuard m(lock_options_); | |
1312 | 3371 | const unsigned max_retries = opt_max_retries_; | |
1313 | |||
1314 |
2/2✓ Branch 2 taken 1306 times.
✓ Branch 3 taken 1984 times.
|
6661 | return !(info->nocache()) && (info->num_retries() < max_retries) |
1315 |
3/4✓ Branch 0 taken 3290 times.
✓ Branch 1 taken 81 times.
✓ Branch 4 taken 1306 times.
✗ Branch 5 not taken.
|
7967 | && (IsProxyTransferError(info->error_code()) |
1316 |
2/2✓ Branch 2 taken 109 times.
✓ Branch 3 taken 1197 times.
|
1306 | || IsHostTransferError(info->error_code())); |
1317 | 3371 | } | |
1318 | |||
1319 | /** | ||
1320 | * Backoff for retry to introduce a jitter into a cluster of requesting | ||
1321 | * cvmfs nodes. | ||
1322 | * Retry only when HTTP caching is on. | ||
1323 | * | ||
1324 | * \return true if backoff has been performed, false otherwise | ||
1325 | */ | ||
1326 | 109 | void DownloadManager::Backoff(JobInfo *info) { | |
1327 | 109 | unsigned backoff_init_ms = 0; | |
1328 | 109 | unsigned backoff_max_ms = 0; | |
1329 | { | ||
1330 | 109 | const MutexLockGuard m(lock_options_); | |
1331 | 109 | backoff_init_ms = opt_backoff_init_ms_; | |
1332 | 109 | backoff_max_ms = opt_backoff_max_ms_; | |
1333 | 109 | } | |
1334 | |||
1335 | 109 | info->SetNumRetries(info->num_retries() + 1); | |
1336 | 109 | perf::Inc(counters_->n_retries); | |
1337 |
2/2✓ Branch 1 taken 50 times.
✓ Branch 2 taken 59 times.
|
109 | if (info->backoff_ms() == 0) { |
1338 | 50 | info->SetBackoffMs(prng_.Next(backoff_init_ms + 1)); // Must be != 0 | |
1339 | } else { | ||
1340 | 59 | info->SetBackoffMs(info->backoff_ms() * 2); | |
1341 | } | ||
1342 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 100 times.
|
109 | if (info->backoff_ms() > backoff_max_ms) { |
1343 | 9 | info->SetBackoffMs(backoff_max_ms); | |
1344 | } | ||
1345 | |||
1346 | 109 | LogCvmfs(kLogDownload, kLogDebug, | |
1347 | "(manager '%s' - id %" PRId64 ") backing off for %d ms", | ||
1348 | name_.c_str(), info->id(), info->backoff_ms()); | ||
1349 | 109 | SafeSleepMs(info->backoff_ms()); | |
1350 | 109 | } | |
1351 | |||
1352 | 81 | void DownloadManager::SetNocache(JobInfo *info) { | |
1353 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 81 times.
|
81 | if (info->nocache()) |
1354 | ✗ | return; | |
1355 | 81 | header_lists_->AppendHeader(info->headers(), "Pragma: no-cache"); | |
1356 | 81 | header_lists_->AppendHeader(info->headers(), "Cache-Control: no-cache"); | |
1357 | 81 | curl_easy_setopt(info->curl_handle(), CURLOPT_HTTPHEADER, info->headers()); | |
1358 | 81 | info->SetNocache(true); | |
1359 | } | ||
1360 | |||
1361 | |||
1362 | /** | ||
1363 | * Reverse operation of SetNocache. Makes sure that "no-cache" header | ||
1364 | * disappears from the list of headers to let proxies work normally. | ||
1365 | */ | ||
1366 | 190 | void DownloadManager::SetRegularCache(JobInfo *info) { | |
1367 |
1/2✓ Branch 1 taken 190 times.
✗ Branch 2 not taken.
|
190 | if (info->nocache() == false) |
1368 | 190 | return; | |
1369 | ✗ | header_lists_->CutHeader("Pragma: no-cache", info->GetHeadersPtr()); | |
1370 | ✗ | header_lists_->CutHeader("Cache-Control: no-cache", info->GetHeadersPtr()); | |
1371 | ✗ | curl_easy_setopt(info->curl_handle(), CURLOPT_HTTPHEADER, info->headers()); | |
1372 | ✗ | info->SetNocache(false); | |
1373 | } | ||
1374 | |||
1375 | |||
1376 | /** | ||
1377 | * Frees the storage associated with the authz attachment from the job | ||
1378 | */ | ||
1379 | 3229 | void DownloadManager::ReleaseCredential(JobInfo *info) { | |
1380 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3229 times.
|
3229 | if (info->cred_data()) { |
1381 | ✗ | assert(credentials_attachment_ != NULL); // Someone must have set it | |
1382 | ✗ | credentials_attachment_->ReleaseCurlHandle(info->curl_handle(), | |
1383 | info->cred_data()); | ||
1384 | ✗ | info->SetCredData(NULL); | |
1385 | } | ||
1386 | 3229 | } | |
1387 | |||
1388 | |||
1389 | /* Sort links based on the "pri=" parameter */ | ||
1390 | ✗ | static bool sortlinks(const std::string &s1, const std::string &s2) { | |
1391 | ✗ | const size_t pos1 = s1.find("; pri="); | |
1392 | ✗ | const size_t pos2 = s2.find("; pri="); | |
1393 | int pri1, pri2; | ||
1394 | ✗ | if ((pos1 != std::string::npos) && (pos2 != std::string::npos) | |
1395 | ✗ | && (sscanf(s1.substr(pos1 + 6).c_str(), "%d", &pri1) == 1) | |
1396 | ✗ | && (sscanf(s2.substr(pos2 + 6).c_str(), "%d", &pri2) == 1)) { | |
1397 | ✗ | return pri1 < pri2; | |
1398 | } | ||
1399 | ✗ | return false; | |
1400 | } | ||
1401 | |||
1402 | /** | ||
1403 | * Parses Link header and uses it to set a new host chain. | ||
1404 | * See rfc6249. | ||
1405 | */ | ||
1406 | ✗ | void DownloadManager::ProcessLink(JobInfo *info) { | |
1407 | ✗ | std::vector<std::string> links = SplitString(info->link(), ','); | |
1408 | ✗ | if (info->link().find("; pri=") != std::string::npos) | |
1409 | ✗ | std::sort(links.begin(), links.end(), sortlinks); | |
1410 | |||
1411 | ✗ | std::vector<std::string> host_list; | |
1412 | |||
1413 | ✗ | std::vector<std::string>::const_iterator il = links.begin(); | |
1414 | ✗ | for (; il != links.end(); ++il) { | |
1415 | ✗ | const std::string &link = *il; | |
1416 | ✗ | if ((link.find("; rel=duplicate") == std::string::npos) | |
1417 | ✗ | && (link.find("; rel=\"duplicate\"") == std::string::npos)) { | |
1418 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
1419 | "skipping link '%s' because it does not contain rel=duplicate", | ||
1420 | link.c_str()); | ||
1421 | ✗ | continue; | |
1422 | } | ||
1423 | // ignore depth= field since there's nothing useful we can do with it | ||
1424 | |||
1425 | ✗ | size_t start = link.find('<'); | |
1426 | ✗ | if (start == std::string::npos) { | |
1427 | ✗ | LogCvmfs( | |
1428 | kLogDownload, kLogDebug, | ||
1429 | "skipping link '%s' because it does not have a left angle bracket", | ||
1430 | link.c_str()); | ||
1431 | ✗ | continue; | |
1432 | } | ||
1433 | |||
1434 | ✗ | start++; | |
1435 | ✗ | if ((link.substr(start, 7) != "http://") | |
1436 | ✗ | && (link.substr(start, 8) != "https://")) { | |
1437 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
1438 | "skipping link '%s' of unrecognized url protocol", link.c_str()); | ||
1439 | ✗ | continue; | |
1440 | } | ||
1441 | |||
1442 | ✗ | size_t end = link.find('/', start + 8); | |
1443 | ✗ | if (end == std::string::npos) | |
1444 | ✗ | end = link.find('>'); | |
1445 | ✗ | if (end == std::string::npos) { | |
1446 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
1447 | "skipping link '%s' because no slash in url and no right angle " | ||
1448 | "bracket", | ||
1449 | link.c_str()); | ||
1450 | ✗ | continue; | |
1451 | } | ||
1452 | ✗ | const std::string host = link.substr(start, end - start); | |
1453 | ✗ | LogCvmfs(kLogDownload, kLogDebug, "adding linked host '%s'", host.c_str()); | |
1454 | ✗ | host_list.push_back(host); | |
1455 | } | ||
1456 | |||
1457 | ✗ | if (host_list.size() > 0) { | |
1458 | ✗ | SetHostChain(host_list); | |
1459 | ✗ | opt_metalink_timestamp_link_ = time(NULL); | |
1460 | } | ||
1461 | } | ||
1462 | |||
1463 | |||
1464 | /** | ||
1465 | * Checks the result of a curl download and implements the failure logic, such | ||
1466 | * as changing the proxy server. Takes care of cleanup. | ||
1467 | * | ||
1468 | * \return true if another download should be performed, false otherwise | ||
1469 | */ | ||
1470 | 3371 | bool DownloadManager::VerifyAndFinalize(const int curl_error, JobInfo *info) { | |
1471 |
1/2✓ Branch 6 taken 3371 times.
✗ Branch 7 not taken.
|
3371 | LogCvmfs(kLogDownload, kLogDebug, |
1472 | "(manager '%s' - id %" PRId64 ") " | ||
1473 | "Verify downloaded url %s, proxy %s (curl error %d)", | ||
1474 | name_.c_str(), info->id(), info->url()->c_str(), | ||
1475 |
1/2✓ Branch 1 taken 3371 times.
✗ Branch 2 not taken.
|
6742 | info->proxy().c_str(), curl_error); |
1476 |
1/2✓ Branch 2 taken 3371 times.
✗ Branch 3 not taken.
|
3371 | UpdateStatistics(info->curl_handle()); |
1477 | |||
1478 | bool was_metalink; | ||
1479 | 3371 | std::string typ; | |
1480 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3371 times.
|
3371 | if (info->current_metalink_chain_index() >= 0) { |
1481 | ✗ | was_metalink = true; | |
1482 | ✗ | typ = "metalink"; | |
1483 | ✗ | if (info->link() != "") { | |
1484 | // process Link header whether or not the redirected URL got an error | ||
1485 | ✗ | ProcessLink(info); | |
1486 | } | ||
1487 | } else { | ||
1488 | 3371 | was_metalink = false; | |
1489 |
1/2✓ Branch 1 taken 3371 times.
✗ Branch 2 not taken.
|
3371 | typ = "host"; |
1490 | } | ||
1491 | |||
1492 | |||
1493 | // Verification and error classification | ||
1494 |
3/14✓ Branch 0 taken 3055 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 314 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
3371 | switch (curl_error) { |
1495 | 3055 | case CURLE_OK: | |
1496 | // Verify content hash | ||
1497 |
2/2✓ Branch 1 taken 2066 times.
✓ Branch 2 taken 989 times.
|
3055 | if (info->expected_hash()) { |
1498 |
1/2✓ Branch 1 taken 2066 times.
✗ Branch 2 not taken.
|
2066 | shash::Any match_hash; |
1499 |
1/2✓ Branch 2 taken 2066 times.
✗ Branch 3 not taken.
|
2066 | shash::Final(info->hash_context(), &match_hash); |
1500 |
3/4✓ Branch 2 taken 2066 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 66 times.
✓ Branch 5 taken 2000 times.
|
2066 | if (match_hash != *(info->expected_hash())) { |
1501 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
|
66 | if (ignore_signature_failures_) { |
1502 | ✗ | LogCvmfs( | |
1503 | kLogDownload, kLogDebug | kLogSyslogErr, | ||
1504 | "(manager '%s' - id %" PRId64 ") " | ||
1505 | "ignoring failed hash verification of %s (expected %s, got %s)", | ||
1506 | name_.c_str(), info->id(), info->url()->c_str(), | ||
1507 | ✗ | info->expected_hash()->ToString().c_str(), | |
1508 | ✗ | match_hash.ToString().c_str()); | |
1509 | } else { | ||
1510 |
1/2✓ Branch 7 taken 66 times.
✗ Branch 8 not taken.
|
132 | LogCvmfs(kLogDownload, kLogDebug, |
1511 | "(manager '%s' - id %" PRId64 ") " | ||
1512 | "hash verification of %s failed (expected %s, got %s)", | ||
1513 | name_.c_str(), info->id(), info->url()->c_str(), | ||
1514 |
1/2✓ Branch 2 taken 66 times.
✗ Branch 3 not taken.
|
132 | info->expected_hash()->ToString().c_str(), |
1515 |
1/2✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
|
132 | match_hash.ToString().c_str()); |
1516 | 66 | info->SetErrorCode(kFailBadData); | |
1517 | 66 | break; | |
1518 | } | ||
1519 | } | ||
1520 | } | ||
1521 | |||
1522 | 2989 | info->SetErrorCode(kFailOk); | |
1523 | 2989 | break; | |
1524 | ✗ | case CURLE_UNSUPPORTED_PROTOCOL: | |
1525 | ✗ | info->SetErrorCode(kFailUnsupportedProtocol); | |
1526 | ✗ | break; | |
1527 | 2 | case CURLE_URL_MALFORMAT: | |
1528 | 2 | info->SetErrorCode(kFailBadUrl); | |
1529 | 2 | break; | |
1530 | ✗ | case CURLE_COULDNT_RESOLVE_PROXY: | |
1531 | ✗ | info->SetErrorCode(kFailProxyResolve); | |
1532 | ✗ | break; | |
1533 | ✗ | case CURLE_COULDNT_RESOLVE_HOST: | |
1534 | ✗ | info->SetErrorCode(kFailHostResolve); | |
1535 | ✗ | break; | |
1536 | ✗ | case CURLE_OPERATION_TIMEDOUT: | |
1537 | ✗ | info->SetErrorCode((info->proxy() == "DIRECT") ? kFailHostTooSlow | |
1538 | : kFailProxyTooSlow); | ||
1539 | ✗ | break; | |
1540 | ✗ | case CURLE_PARTIAL_FILE: | |
1541 | case CURLE_GOT_NOTHING: | ||
1542 | case CURLE_RECV_ERROR: | ||
1543 | ✗ | info->SetErrorCode((info->proxy() == "DIRECT") ? kFailHostShortTransfer | |
1544 | : kFailProxyShortTransfer); | ||
1545 | ✗ | break; | |
1546 | 314 | case CURLE_FILE_COULDNT_READ_FILE: | |
1547 | case CURLE_COULDNT_CONNECT: | ||
1548 |
3/6✓ Branch 1 taken 314 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 16 times.
✓ Branch 6 taken 298 times.
|
314 | if (info->proxy() != "DIRECT") { |
1549 | // This is a guess. Fail-over can still change to switching host | ||
1550 | 16 | info->SetErrorCode(kFailProxyConnection); | |
1551 | } else { | ||
1552 | 298 | info->SetErrorCode(kFailHostConnection); | |
1553 | } | ||
1554 | 314 | break; | |
1555 | ✗ | case CURLE_TOO_MANY_REDIRECTS: | |
1556 | ✗ | info->SetErrorCode(kFailHostConnection); | |
1557 | ✗ | break; | |
1558 | ✗ | case CURLE_SSL_CACERT_BADFILE: | |
1559 | ✗ | LogCvmfs(kLogDownload, kLogDebug | kLogSyslogErr, | |
1560 | "(manager '%s' -id %" PRId64 ") " | ||
1561 | "Failed to load certificate bundle. " | ||
1562 | "X509_CERT_BUNDLE might point to the wrong location.", | ||
1563 | name_.c_str(), info->id()); | ||
1564 | ✗ | info->SetErrorCode(kFailHostConnection); | |
1565 | ✗ | break; | |
1566 | // As of curl 7.62.0, CURLE_SSL_CACERT is the same as | ||
1567 | // CURLE_PEER_FAILED_VERIFICATION | ||
1568 | ✗ | case CURLE_PEER_FAILED_VERIFICATION: | |
1569 | ✗ | LogCvmfs(kLogDownload, kLogDebug | kLogSyslogErr, | |
1570 | "(manager '%s' - id %" PRId64 ") " | ||
1571 | "invalid SSL certificate of remote host. " | ||
1572 | "X509_CERT_DIR and/or X509_CERT_BUNDLE might point to the wrong " | ||
1573 | "location.", | ||
1574 | name_.c_str(), info->id()); | ||
1575 | ✗ | info->SetErrorCode(kFailHostConnection); | |
1576 | ✗ | break; | |
1577 | ✗ | case CURLE_ABORTED_BY_CALLBACK: | |
1578 | case CURLE_WRITE_ERROR: | ||
1579 | // Error set by callback | ||
1580 | ✗ | break; | |
1581 | ✗ | case CURLE_SEND_ERROR: | |
1582 | // The curl error CURLE_SEND_ERROR can be seen when a cache is misbehaving | ||
1583 | // and closing connections before the http request send is completed. | ||
1584 | // Handle this error, treating it as a short transfer error. | ||
1585 | ✗ | info->SetErrorCode((info->proxy() == "DIRECT") ? kFailHostShortTransfer | |
1586 | : kFailProxyShortTransfer); | ||
1587 | ✗ | break; | |
1588 | ✗ | default: | |
1589 | ✗ | LogCvmfs(kLogDownload, kLogSyslogErr, | |
1590 | "(manager '%s' - id %" PRId64 ") " | ||
1591 | "unexpected curl error (%d) while trying to fetch %s", | ||
1592 | name_.c_str(), info->id(), curl_error, info->url()->c_str()); | ||
1593 | ✗ | info->SetErrorCode(kFailOther); | |
1594 | ✗ | break; | |
1595 | } | ||
1596 | |||
1597 | std::vector<std::string> *host_chain; | ||
1598 | unsigned char num_used_hosts; | ||
1599 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3371 times.
|
3371 | if (was_metalink) { |
1600 | ✗ | host_chain = opt_metalink_.chain; | |
1601 | ✗ | num_used_hosts = info->num_used_metalinks(); | |
1602 | } else { | ||
1603 | 3371 | host_chain = opt_host_.chain; | |
1604 | 3371 | num_used_hosts = info->num_used_hosts(); | |
1605 | } | ||
1606 | |||
1607 | // Determination if download should be repeated | ||
1608 | 3371 | bool try_again = false; | |
1609 |
1/2✓ Branch 1 taken 3371 times.
✗ Branch 2 not taken.
|
3371 | bool same_url_retry = CanRetry(info); |
1610 |
2/2✓ Branch 1 taken 382 times.
✓ Branch 2 taken 2989 times.
|
3371 | if (info->error_code() != kFailOk) { |
1611 | 382 | const MutexLockGuard m(lock_options_); | |
1612 |
2/2✓ Branch 1 taken 66 times.
✓ Branch 2 taken 316 times.
|
382 | if (info->error_code() == kFailBadData) { |
1613 |
2/2✓ Branch 1 taken 33 times.
✓ Branch 2 taken 33 times.
|
66 | if (!info->nocache()) { |
1614 | 33 | try_again = true; | |
1615 | } else { | ||
1616 | // Make it a host failure | ||
1617 |
1/2✓ Branch 4 taken 33 times.
✗ Branch 5 not taken.
|
33 | LogCvmfs(kLogDownload, kLogDebug | kLogSyslogWarn, |
1618 | "(manager '%s' - id %" PRId64 ") " | ||
1619 | "data corruption with no-cache header, try another %s", | ||
1620 | name_.c_str(), info->id(), typ.c_str()); | ||
1621 | |||
1622 | 33 | info->SetErrorCode(kFailHostHttp); | |
1623 | } | ||
1624 | } | ||
1625 | 382 | if (same_url_retry | |
1626 |
5/6✓ Branch 0 taken 273 times.
✓ Branch 1 taken 109 times.
✓ Branch 3 taken 273 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 157 times.
✓ Branch 6 taken 225 times.
|
655 | || (((info->error_code() == kFailHostResolve) |
1627 |
2/2✓ Branch 2 taken 84 times.
✓ Branch 3 taken 189 times.
|
273 | || IsHostTransferError(info->error_code()) |
1628 |
2/2✓ Branch 1 taken 33 times.
✓ Branch 2 taken 51 times.
|
84 | || (info->error_code() == kFailHostHttp)) |
1629 |
3/4✓ Branch 1 taken 133 times.
✓ Branch 2 taken 89 times.
✓ Branch 3 taken 133 times.
✗ Branch 4 not taken.
|
222 | && info->probe_hosts() && host_chain |
1630 |
2/2✓ Branch 1 taken 48 times.
✓ Branch 2 taken 85 times.
|
133 | && (num_used_hosts < host_chain->size()))) { |
1631 | 157 | try_again = true; | |
1632 | } | ||
1633 | 382 | if (same_url_retry | |
1634 |
5/6✓ Branch 0 taken 273 times.
✓ Branch 1 taken 109 times.
✓ Branch 3 taken 273 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 125 times.
✓ Branch 6 taken 257 times.
|
655 | || (((info->error_code() == kFailProxyResolve) |
1635 |
2/2✓ Branch 2 taken 257 times.
✓ Branch 3 taken 16 times.
|
273 | || IsProxyTransferError(info->error_code()) |
1636 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 257 times.
|
257 | || (info->error_code() == kFailProxyHttp)))) { |
1637 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 125 times.
|
125 | if (sharding_policy_.UseCount() > 0) { // sharding policy |
1638 | ✗ | try_again = true; | |
1639 | ✗ | same_url_retry = false; | |
1640 | } else { // no sharding | ||
1641 | 125 | try_again = true; | |
1642 | // If all proxies failed, do a next round with the next host | ||
1643 |
4/6✓ Branch 0 taken 16 times.
✓ Branch 1 taken 109 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 125 times.
|
125 | if (!same_url_retry && (info->num_used_proxies() >= opt_num_proxies_)) { |
1644 | // Check if this can be made a host fail-over | ||
1645 | ✗ | if (info->probe_hosts() && host_chain | |
1646 | ✗ | && (num_used_hosts < host_chain->size())) { | |
1647 | // reset proxy group if not already performed by other handle | ||
1648 | ✗ | if (opt_proxy_groups_) { | |
1649 | ✗ | if ((opt_proxy_groups_current_ > 0) | |
1650 | ✗ | || (opt_proxy_groups_current_burned_ > 0)) { | |
1651 | ✗ | opt_proxy_groups_current_ = 0; | |
1652 | ✗ | opt_timestamp_backup_proxies_ = 0; | |
1653 | ✗ | const std::string msg = "reset proxies for " + typ | |
1654 | ✗ | + " failover"; | |
1655 | ✗ | RebalanceProxiesUnlocked(msg); | |
1656 | } | ||
1657 | } | ||
1658 | |||
1659 | // Make it a host failure | ||
1660 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
1661 | "(manager '%s' - id %" PRId64 ") make it a %s failure", | ||
1662 | name_.c_str(), info->id(), typ.c_str()); | ||
1663 | ✗ | info->SetNumUsedProxies(1); | |
1664 | ✗ | info->SetErrorCode(kFailHostAfterProxy); | |
1665 | } else { | ||
1666 | ✗ | if (failover_indefinitely_) { | |
1667 | // Instead of giving up, reset the num_used_proxies counter, | ||
1668 | // switch proxy and try again | ||
1669 | ✗ | LogCvmfs(kLogDownload, kLogDebug | kLogSyslogWarn, | |
1670 | "(manager '%s' - id %" PRId64 ") " | ||
1671 | "VerifyAndFinalize() would fail the download here. " | ||
1672 | "Instead switch proxy and retry download. " | ||
1673 | "typ=%s " | ||
1674 | "info->probe_hosts=%d host_chain=%p num_used_hosts=%d " | ||
1675 | "host_chain->size()=%lu same_url_retry=%d " | ||
1676 | "info->num_used_proxies=%d opt_num_proxies_=%d", | ||
1677 | name_.c_str(), info->id(), typ.c_str(), | ||
1678 | ✗ | static_cast<int>(info->probe_hosts()), host_chain, | |
1679 | ✗ | num_used_hosts, host_chain ? host_chain->size() : -1, | |
1680 | static_cast<int>(same_url_retry), | ||
1681 | ✗ | info->num_used_proxies(), opt_num_proxies_); | |
1682 | ✗ | info->SetNumUsedProxies(1); | |
1683 | ✗ | RebalanceProxiesUnlocked( | |
1684 | "download failed - failover indefinitely"); | ||
1685 | ✗ | try_again = !Interrupted(fqrn_, info); | |
1686 | } else { | ||
1687 | ✗ | try_again = false; | |
1688 | } | ||
1689 | } | ||
1690 | } // Make a proxy failure a host failure | ||
1691 | } // Proxy failure assumed | ||
1692 | } // end !sharding | ||
1693 | 382 | } | |
1694 | |||
1695 |
2/2✓ Branch 0 taken 206 times.
✓ Branch 1 taken 3165 times.
|
3371 | if (try_again) { |
1696 |
1/2✓ Branch 3 taken 206 times.
✗ Branch 4 not taken.
|
206 | LogCvmfs(kLogDownload, kLogDebug, |
1697 | "(manager '%s' - id %" PRId64 ") " | ||
1698 | "Trying again on same curl handle, same url: %d, " | ||
1699 | "error code %d no-cache %d", | ||
1700 | 206 | name_.c_str(), info->id(), same_url_retry, info->error_code(), | |
1701 | 206 | info->nocache()); | |
1702 | // Reset internal state and destination | ||
1703 |
4/8✓ Branch 1 taken 206 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 206 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 206 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 206 times.
|
206 | if (info->sink() != NULL && info->sink()->Reset() != 0) { |
1704 | ✗ | info->SetErrorCode(kFailLocalIO); | |
1705 | ✗ | goto verify_and_finalize_stop; | |
1706 | } | ||
1707 |
6/8✓ Branch 1 taken 16 times.
✓ Branch 2 taken 190 times.
✓ Branch 5 taken 16 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 16 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 16 times.
✓ Branch 10 taken 190 times.
|
206 | if (info->interrupt_cue() && info->interrupt_cue()->IsCanceled()) { |
1708 | 16 | info->SetErrorCode(kFailCanceled); | |
1709 | 16 | goto verify_and_finalize_stop; | |
1710 | } | ||
1711 | |||
1712 |
2/2✓ Branch 1 taken 60 times.
✓ Branch 2 taken 130 times.
|
190 | if (info->expected_hash()) { |
1713 |
1/2✓ Branch 2 taken 60 times.
✗ Branch 3 not taken.
|
60 | shash::Init(info->hash_context()); |
1714 | } | ||
1715 |
2/2✓ Branch 1 taken 60 times.
✓ Branch 2 taken 130 times.
|
190 | if (info->compressed()) { |
1716 |
1/2✓ Branch 2 taken 60 times.
✗ Branch 3 not taken.
|
60 | zlib::DecompressInit(info->GetZstreamPtr()); |
1717 | } | ||
1718 | |||
1719 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 190 times.
|
190 | if (sharding_policy_.UseCount() > 0) { // sharding policy |
1720 | ✗ | ReleaseCredential(info); | |
1721 | ✗ | SetUrlOptions(info); | |
1722 | } else { // no sharding policy | ||
1723 |
1/2✓ Branch 1 taken 190 times.
✗ Branch 2 not taken.
|
190 | SetRegularCache(info); |
1724 | |||
1725 | // Failure handling | ||
1726 | 190 | bool switch_proxy = false; | |
1727 | 190 | bool switch_host = false; | |
1728 |
2/4✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 157 times.
|
190 | switch (info->error_code()) { |
1729 | 33 | case kFailBadData: | |
1730 |
1/2✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
|
33 | SetNocache(info); |
1731 | 33 | break; | |
1732 | ✗ | case kFailProxyResolve: | |
1733 | case kFailProxyHttp: | ||
1734 | ✗ | switch_proxy = true; | |
1735 | ✗ | break; | |
1736 | ✗ | case kFailHostResolve: | |
1737 | case kFailHostHttp: | ||
1738 | case kFailHostAfterProxy: | ||
1739 | ✗ | switch_host = true; | |
1740 | ✗ | break; | |
1741 | 157 | default: | |
1742 |
2/2✓ Branch 2 taken 16 times.
✓ Branch 3 taken 141 times.
|
157 | if (IsProxyTransferError(info->error_code())) { |
1743 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (same_url_retry) { |
1744 | ✗ | Backoff(info); | |
1745 | } else { | ||
1746 | 16 | switch_proxy = true; | |
1747 | } | ||
1748 |
1/2✓ Branch 2 taken 141 times.
✗ Branch 3 not taken.
|
141 | } else if (IsHostTransferError(info->error_code())) { |
1749 |
2/2✓ Branch 0 taken 109 times.
✓ Branch 1 taken 32 times.
|
141 | if (same_url_retry) { |
1750 |
1/2✓ Branch 1 taken 109 times.
✗ Branch 2 not taken.
|
109 | Backoff(info); |
1751 | } else { | ||
1752 | 32 | switch_host = true; | |
1753 | } | ||
1754 | } else { | ||
1755 | // No other errors expected when retrying | ||
1756 | ✗ | PANIC(NULL); | |
1757 | } | ||
1758 | } | ||
1759 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 174 times.
|
190 | if (switch_proxy) { |
1760 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | ReleaseCredential(info); |
1761 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | SwitchProxy(info); |
1762 | 16 | info->SetNumUsedProxies(info->num_used_proxies() + 1); | |
1763 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | SetUrlOptions(info); |
1764 | } | ||
1765 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 158 times.
|
190 | if (switch_host) { |
1766 |
1/2✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
|
32 | ReleaseCredential(info); |
1767 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | if (was_metalink) { |
1768 | ✗ | SwitchMetalink(info); | |
1769 | ✗ | info->SetNumUsedMetalinks(num_used_hosts + 1); | |
1770 | } else { | ||
1771 |
1/2✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
|
32 | SwitchHost(info); |
1772 | 32 | info->SetNumUsedHosts(num_used_hosts + 1); | |
1773 | } | ||
1774 |
1/2✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
|
32 | SetUrlOptions(info); |
1775 | } | ||
1776 | } // end !sharding | ||
1777 | |||
1778 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 190 times.
|
190 | if (failover_indefinitely_) { |
1779 | // try again, breaking if there's a cvmfs reload happening and we are in a | ||
1780 | // proxy failover. This will EIO the call application. | ||
1781 | ✗ | return !Interrupted(fqrn_, info); | |
1782 | } | ||
1783 | 190 | return true; // try again | |
1784 | } | ||
1785 | |||
1786 | 3165 | verify_and_finalize_stop: | |
1787 | // Finalize, flush destination file | ||
1788 |
1/2✓ Branch 1 taken 3181 times.
✗ Branch 2 not taken.
|
3181 | ReleaseCredential(info); |
1789 |
4/8✓ Branch 1 taken 3181 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3181 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 3181 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3181 times.
|
3181 | if (info->sink() != NULL && info->sink()->Flush() != 0) { |
1790 | ✗ | info->SetErrorCode(kFailLocalIO); | |
1791 | } | ||
1792 | |||
1793 |
2/2✓ Branch 1 taken 2093 times.
✓ Branch 2 taken 1088 times.
|
3181 | if (info->compressed()) |
1794 |
1/2✓ Branch 2 taken 2093 times.
✗ Branch 3 not taken.
|
2093 | zlib::DecompressFini(info->GetZstreamPtr()); |
1795 | |||
1796 |
1/2✓ Branch 1 taken 3181 times.
✗ Branch 2 not taken.
|
3181 | if (info->headers()) { |
1797 |
1/2✓ Branch 2 taken 3181 times.
✗ Branch 3 not taken.
|
3181 | header_lists_->PutList(info->headers()); |
1798 | 3181 | info->SetHeaders(NULL); | |
1799 | } | ||
1800 | |||
1801 | 3181 | return false; // stop transfer and return to Fetch() | |
1802 | 3371 | } | |
1803 | |||
1804 | 3168 | DownloadManager::~DownloadManager() { | |
1805 | // cleaned up fini | ||
1806 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3168 times.
|
3168 | if (sharding_policy_.UseCount() > 0) { |
1807 | ✗ | sharding_policy_.Reset(); | |
1808 | } | ||
1809 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3168 times.
|
3168 | if (health_check_.UseCount() > 0) { |
1810 | ✗ | if (health_check_.Unique()) { | |
1811 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
1812 | "(manager '%s') Stopping healthcheck thread", name_.c_str()); | ||
1813 | ✗ | health_check_->StopHealthcheck(); | |
1814 | } | ||
1815 | ✗ | health_check_.Reset(); | |
1816 | } | ||
1817 | |||
1818 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3168 times.
|
3168 | if (atomic_xadd32(&multi_threaded_, 0) == 1) { |
1819 | // Shutdown I/O thread | ||
1820 | ✗ | pipe_terminate_->Write(kPipeTerminateSignal); | |
1821 | ✗ | pthread_join(thread_download_, NULL); | |
1822 | // All handles are removed from the multi stack | ||
1823 | ✗ | pipe_terminate_.Destroy(); | |
1824 | ✗ | pipe_jobs_.Destroy(); | |
1825 | } | ||
1826 | |||
1827 | 6336 | for (set<CURL *>::iterator i = pool_handles_idle_->begin(), | |
1828 | 3168 | iEnd = pool_handles_idle_->end(); | |
1829 |
2/2✓ Branch 1 taken 1486 times.
✓ Branch 2 taken 3168 times.
|
4654 | i != iEnd; |
1830 | 1486 | ++i) { | |
1831 | 1486 | curl_easy_cleanup(*i); | |
1832 | } | ||
1833 | |||
1834 |
1/2✓ Branch 0 taken 3168 times.
✗ Branch 1 not taken.
|
3168 | delete pool_handles_idle_; |
1835 |
1/2✓ Branch 0 taken 3168 times.
✗ Branch 1 not taken.
|
3168 | delete pool_handles_inuse_; |
1836 | 3168 | curl_multi_cleanup(curl_multi_); | |
1837 | |||
1838 |
1/2✓ Branch 0 taken 3168 times.
✗ Branch 1 not taken.
|
3168 | delete header_lists_; |
1839 |
1/2✓ Branch 0 taken 3168 times.
✗ Branch 1 not taken.
|
3168 | if (user_agent_) |
1840 | 3168 | free(user_agent_); | |
1841 | |||
1842 |
1/2✓ Branch 0 taken 3168 times.
✗ Branch 1 not taken.
|
3168 | delete counters_; |
1843 |
2/2✓ Branch 0 taken 1661 times.
✓ Branch 1 taken 1507 times.
|
3168 | delete opt_host_.chain; |
1844 |
2/2✓ Branch 0 taken 1661 times.
✓ Branch 1 taken 1507 times.
|
3168 | delete opt_host_chain_rtt_; |
1845 |
2/2✓ Branch 0 taken 1540 times.
✓ Branch 1 taken 1628 times.
|
3168 | delete opt_proxy_groups_; |
1846 | |||
1847 | 3168 | curl_global_cleanup(); | |
1848 |
1/2✓ Branch 0 taken 3168 times.
✗ Branch 1 not taken.
|
3168 | delete resolver_; |
1849 | |||
1850 | // old destructor | ||
1851 | 3168 | pthread_mutex_destroy(lock_options_); | |
1852 | 3168 | pthread_mutex_destroy(lock_synchronous_mode_); | |
1853 | 3168 | free(lock_options_); | |
1854 | 3168 | free(lock_synchronous_mode_); | |
1855 | 3168 | } | |
1856 | |||
1857 | 3169 | void DownloadManager::InitHeaders() { | |
1858 | // User-Agent | ||
1859 |
1/2✓ Branch 2 taken 3169 times.
✗ Branch 3 not taken.
|
3169 | string cernvm_id = "User-Agent: cvmfs "; |
1860 | #ifdef CVMFS_LIBCVMFS | ||
1861 |
1/2✓ Branch 1 taken 3169 times.
✗ Branch 2 not taken.
|
3169 | cernvm_id += "libcvmfs "; |
1862 | #else | ||
1863 | ✗ | cernvm_id += "Fuse "; | |
1864 | #endif | ||
1865 |
2/4✓ Branch 2 taken 3169 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3169 times.
✗ Branch 6 not taken.
|
3169 | cernvm_id += string(CVMFS_VERSION); |
1866 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3169 times.
|
3169 | if (getenv("CERNVM_UUID") != NULL) { |
1867 | cernvm_id += " " | ||
1868 | ✗ | + sanitizer::InputSanitizer("az AZ 09 -") | |
1869 | ✗ | .Filter(getenv("CERNVM_UUID")); | |
1870 | } | ||
1871 | 3169 | user_agent_ = strdup(cernvm_id.c_str()); | |
1872 | |||
1873 |
1/2✓ Branch 1 taken 3169 times.
✗ Branch 2 not taken.
|
3169 | header_lists_ = new HeaderLists(); |
1874 | |||
1875 |
1/2✓ Branch 1 taken 3169 times.
✗ Branch 2 not taken.
|
3169 | default_headers_ = header_lists_->GetList("Connection: Keep-Alive"); |
1876 |
1/2✓ Branch 1 taken 3169 times.
✗ Branch 2 not taken.
|
3169 | header_lists_->AppendHeader(default_headers_, "Pragma:"); |
1877 |
1/2✓ Branch 1 taken 3169 times.
✗ Branch 2 not taken.
|
3169 | header_lists_->AppendHeader(default_headers_, user_agent_); |
1878 | 3169 | } | |
1879 | |||
1880 | 3169 | DownloadManager::DownloadManager(const unsigned max_pool_handles, | |
1881 | const perf::StatisticsTemplate &statistics, | ||
1882 | 3169 | const std::string &name) | |
1883 | 3169 | : prng_(Prng()) | |
1884 | 3169 | , pool_handles_idle_(new set<CURL *>) | |
1885 | 3169 | , pool_handles_inuse_(new set<CURL *>) | |
1886 | 3169 | , pool_max_handles_(max_pool_handles) | |
1887 | 3169 | , pipe_terminate_(NULL) | |
1888 |
1/2✓ Branch 1 taken 3169 times.
✗ Branch 2 not taken.
|
3169 | , pipe_jobs_(NULL) |
1889 | 3169 | , watch_fds_(NULL) | |
1890 | 3169 | , watch_fds_size_(0) | |
1891 | 3169 | , watch_fds_inuse_(0) | |
1892 | 3169 | , watch_fds_max_(4 * max_pool_handles) | |
1893 | 3169 | , opt_timeout_proxy_(5) | |
1894 | 3169 | , opt_timeout_direct_(10) | |
1895 | 3169 | , opt_low_speed_limit_(1024) | |
1896 | 3169 | , opt_max_retries_(0) | |
1897 | 3169 | , opt_backoff_init_ms_(0) | |
1898 | 3169 | , opt_backoff_max_ms_(0) | |
1899 | 3169 | , enable_info_header_(false) | |
1900 | 3169 | , opt_ipv4_only_(false) | |
1901 | 3169 | , follow_redirects_(false) | |
1902 | 3169 | , ignore_signature_failures_(false) | |
1903 | 3169 | , enable_http_tracing_(false) | |
1904 | 3169 | , opt_metalink_(NULL, 0, 0, 0) | |
1905 | 3169 | , opt_metalink_timestamp_link_(0) | |
1906 | 3169 | , opt_host_(NULL, 0, 0, 0) | |
1907 | 3169 | , opt_host_chain_rtt_(NULL) | |
1908 | 3169 | , opt_proxy_groups_(NULL) | |
1909 | 3169 | , opt_proxy_groups_current_(0) | |
1910 | 3169 | , opt_proxy_groups_current_burned_(0) | |
1911 | 3169 | , opt_proxy_groups_fallback_(0) | |
1912 | 3169 | , opt_num_proxies_(0) | |
1913 | 3169 | , opt_proxy_shard_(false) | |
1914 | 3169 | , failover_indefinitely_(false) | |
1915 |
1/2✓ Branch 1 taken 3169 times.
✗ Branch 2 not taken.
|
3169 | , name_(name) |
1916 | 3169 | , opt_ip_preference_(dns::kIpPreferSystem) | |
1917 | 3169 | , opt_timestamp_backup_proxies_(0) | |
1918 | 3169 | , opt_timestamp_failover_proxies_(0) | |
1919 | 3169 | , opt_proxy_groups_reset_after_(0) | |
1920 | 3169 | , credentials_attachment_(NULL) | |
1921 |
4/8✓ Branch 12 taken 3169 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 3169 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 3169 times.
✗ Branch 19 not taken.
✓ Branch 22 taken 3169 times.
✗ Branch 23 not taken.
|
9507 | , counters_(new Counters(statistics)) { |
1922 | 3169 | atomic_init32(&multi_threaded_); | |
1923 | |||
1924 | 3169 | lock_options_ = reinterpret_cast<pthread_mutex_t *>( | |
1925 | 3169 | smalloc(sizeof(pthread_mutex_t))); | |
1926 | 3169 | int retval = pthread_mutex_init(lock_options_, NULL); | |
1927 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3169 times.
|
3169 | assert(retval == 0); |
1928 | 3169 | lock_synchronous_mode_ = reinterpret_cast<pthread_mutex_t *>( | |
1929 | 3169 | smalloc(sizeof(pthread_mutex_t))); | |
1930 | 3169 | retval = pthread_mutex_init(lock_synchronous_mode_, NULL); | |
1931 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3169 times.
|
3169 | assert(retval == 0); |
1932 | |||
1933 |
1/2✓ Branch 1 taken 3169 times.
✗ Branch 2 not taken.
|
3169 | retval = curl_global_init(CURL_GLOBAL_ALL); |
1934 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3169 times.
|
3169 | assert(retval == CURLE_OK); |
1935 | |||
1936 |
1/2✓ Branch 1 taken 3169 times.
✗ Branch 2 not taken.
|
3169 | InitHeaders(); |
1937 | |||
1938 |
1/2✓ Branch 1 taken 3169 times.
✗ Branch 2 not taken.
|
3169 | curl_multi_ = curl_multi_init(); |
1939 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3169 times.
|
3169 | assert(curl_multi_ != NULL); |
1940 |
1/2✓ Branch 1 taken 3169 times.
✗ Branch 2 not taken.
|
3169 | curl_multi_setopt(curl_multi_, CURLMOPT_SOCKETFUNCTION, CallbackCurlSocket); |
1941 |
1/2✓ Branch 1 taken 3169 times.
✗ Branch 2 not taken.
|
3169 | curl_multi_setopt(curl_multi_, CURLMOPT_SOCKETDATA, |
1942 | static_cast<void *>(this)); | ||
1943 |
1/2✓ Branch 1 taken 3169 times.
✗ Branch 2 not taken.
|
3169 | curl_multi_setopt(curl_multi_, CURLMOPT_MAXCONNECTS, watch_fds_max_); |
1944 |
1/2✓ Branch 1 taken 3169 times.
✗ Branch 2 not taken.
|
3169 | curl_multi_setopt(curl_multi_, CURLMOPT_MAX_TOTAL_CONNECTIONS, |
1945 | pool_max_handles_); | ||
1946 | |||
1947 | 3169 | prng_.InitLocaltime(); | |
1948 | |||
1949 | // Name resolving | ||
1950 | 3169 | if ((getenv("CVMFS_IPV4_ONLY") != NULL) | |
1951 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 3169 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 3169 times.
|
3169 | && (strlen(getenv("CVMFS_IPV4_ONLY")) > 0)) { |
1952 | ✗ | opt_ipv4_only_ = true; | |
1953 | } | ||
1954 |
1/2✓ Branch 1 taken 3169 times.
✗ Branch 2 not taken.
|
3169 | resolver_ = dns::NormalResolver::Create(opt_ipv4_only_, kDnsDefaultRetries, |
1955 | kDnsDefaultTimeoutMs); | ||
1956 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3169 times.
|
3169 | assert(resolver_); |
1957 | 3169 | } | |
1958 | |||
1959 | /** | ||
1960 | * Spawns the I/O worker thread and switches the module in multi-threaded mode. | ||
1961 | * No way back except Fini(); Init(); | ||
1962 | */ | ||
1963 | ✗ | void DownloadManager::Spawn() { | |
1964 | ✗ | pipe_terminate_ = new Pipe<kPipeThreadTerminator>(); | |
1965 | ✗ | pipe_jobs_ = new Pipe<kPipeDownloadJobs>(); | |
1966 | |||
1967 | ✗ | const int retval = pthread_create(&thread_download_, NULL, MainDownload, | |
1968 | static_cast<void *>(this)); | ||
1969 | ✗ | assert(retval == 0); | |
1970 | |||
1971 | ✗ | atomic_inc32(&multi_threaded_); | |
1972 | |||
1973 | ✗ | if (health_check_.UseCount() > 0) { | |
1974 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
1975 | "(manager '%s') Starting healthcheck thread", name_.c_str()); | ||
1976 | ✗ | health_check_->StartHealthcheck(); | |
1977 | } | ||
1978 | } | ||
1979 | |||
1980 | |||
1981 | /** | ||
1982 | * Downloads data from an insecure outside channel (currently HTTP or file). | ||
1983 | */ | ||
1984 | 3181 | Failures DownloadManager::Fetch(JobInfo *info) { | |
1985 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3181 times.
|
3181 | assert(info != NULL); |
1986 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3181 times.
|
3181 | assert(info->url() != NULL); |
1987 | |||
1988 | Failures result; | ||
1989 |
1/2✓ Branch 1 taken 3181 times.
✗ Branch 2 not taken.
|
3181 | result = PrepareDownloadDestination(info); |
1990 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3181 times.
|
3181 | if (result != kFailOk) |
1991 | ✗ | return result; | |
1992 | |||
1993 |
2/2✓ Branch 1 taken 2094 times.
✓ Branch 2 taken 1087 times.
|
3181 | if (info->expected_hash()) { |
1994 | 2094 | const shash::Algorithms algorithm = info->expected_hash()->algorithm; | |
1995 | 2094 | info->GetHashContextPtr()->algorithm = algorithm; | |
1996 |
1/2✓ Branch 1 taken 2094 times.
✗ Branch 2 not taken.
|
2094 | info->GetHashContextPtr()->size = shash::GetContextSize(algorithm); |
1997 | 2094 | info->GetHashContextPtr()->buffer = alloca(info->hash_context().size); | |
1998 | } | ||
1999 | |||
2000 | // In case JobInfo object is being reused | ||
2001 |
2/4✓ Branch 2 taken 3181 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3181 times.
✗ Branch 6 not taken.
|
3181 | info->SetLink(""); |
2002 | |||
2003 | // Prepare cvmfs-info: header, allocate string on the stack | ||
2004 | 3181 | info->SetInfoHeader(NULL); | |
2005 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 3181 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 3181 times.
|
3181 | if (enable_info_header_ && info->extra_info()) { |
2006 | ✗ | const char *header_name = "cvmfs-info: "; | |
2007 | ✗ | const size_t header_name_len = strlen(header_name); | |
2008 | const unsigned header_size = 1 + header_name_len | ||
2009 | ✗ | + EscapeHeader(*(info->extra_info()), NULL, 0); | |
2010 | ✗ | info->SetInfoHeader(static_cast<char *>(alloca(header_size))); | |
2011 | ✗ | memcpy(info->info_header(), header_name, header_name_len); | |
2012 | ✗ | EscapeHeader(*(info->extra_info()), info->info_header() + header_name_len, | |
2013 | ✗ | header_size - header_name_len); | |
2014 | ✗ | info->info_header()[header_size - 1] = '\0'; | |
2015 | } | ||
2016 | |||
2017 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3181 times.
|
3181 | if (enable_http_tracing_) { |
2018 | ✗ | const std::string str_pid = "X-CVMFS-PID: " + StringifyInt(info->pid()); | |
2019 | ✗ | const std::string str_gid = "X-CVMFS-GID: " + StringifyUint(info->gid()); | |
2020 | ✗ | const std::string str_uid = "X-CVMFS-UID: " + StringifyUint(info->uid()); | |
2021 | |||
2022 | // will be auto freed at the end of this function Fetch(JobInfo *info) | ||
2023 | ✗ | info->SetTracingHeaderPid(static_cast<char *>(alloca(str_pid.size() + 1))); | |
2024 | ✗ | info->SetTracingHeaderGid(static_cast<char *>(alloca(str_gid.size() + 1))); | |
2025 | ✗ | info->SetTracingHeaderUid(static_cast<char *>(alloca(str_uid.size() + 1))); | |
2026 | |||
2027 | ✗ | memcpy(info->tracing_header_pid(), str_pid.c_str(), str_pid.size() + 1); | |
2028 | ✗ | memcpy(info->tracing_header_gid(), str_gid.c_str(), str_gid.size() + 1); | |
2029 | ✗ | memcpy(info->tracing_header_uid(), str_uid.c_str(), str_uid.size() + 1); | |
2030 | } | ||
2031 | |||
2032 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3181 times.
|
3181 | if (atomic_xadd32(&multi_threaded_, 0) == 1) { |
2033 | ✗ | if (!info->IsValidPipeJobResults()) { | |
2034 | ✗ | info->CreatePipeJobResults(); | |
2035 | } | ||
2036 | ✗ | if (!info->IsValidDataTube()) { | |
2037 | ✗ | info->CreateDataTube(); | |
2038 | } | ||
2039 | |||
2040 | // LogCvmfs(kLogDownload, kLogDebug, "send job to thread, pipe %d %d", | ||
2041 | // info->wait_at[0], info->wait_at[1]); | ||
2042 | ✗ | pipe_jobs_->Write<JobInfo *>(info); | |
2043 | |||
2044 | do { | ||
2045 | ✗ | DataTubeElement *ele = info->GetDataTubePtr()->PopFront(); | |
2046 | |||
2047 | ✗ | if (ele->action == kActionStop) { | |
2048 | ✗ | delete ele; | |
2049 | ✗ | break; | |
2050 | } | ||
2051 | // TODO(heretherebedragons) add compression | ||
2052 | ✗ | } while (true); | |
2053 | |||
2054 | ✗ | info->GetPipeJobResultPtr()->Read<download::Failures>(&result); | |
2055 | // LogCvmfs(kLogDownload, kLogDebug, "got result %d", result); | ||
2056 | } else { | ||
2057 | 3181 | const MutexLockGuard l(lock_synchronous_mode_); | |
2058 |
1/2✓ Branch 1 taken 3181 times.
✗ Branch 2 not taken.
|
3181 | CURL *handle = AcquireCurlHandle(); |
2059 |
1/2✓ Branch 1 taken 3181 times.
✗ Branch 2 not taken.
|
3181 | InitializeRequest(info, handle); |
2060 |
1/2✓ Branch 1 taken 3181 times.
✗ Branch 2 not taken.
|
3181 | SetUrlOptions(info); |
2061 | // curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); | ||
2062 | int retval; | ||
2063 | do { | ||
2064 |
1/2✓ Branch 1 taken 3371 times.
✗ Branch 2 not taken.
|
3371 | retval = curl_easy_perform(handle); |
2065 | 3371 | perf::Inc(counters_->n_requests); | |
2066 | double elapsed; | ||
2067 |
1/2✓ Branch 1 taken 3371 times.
✗ Branch 2 not taken.
|
3371 | if (curl_easy_getinfo(handle, CURLINFO_TOTAL_TIME, &elapsed) |
2068 |
1/2✓ Branch 0 taken 3371 times.
✗ Branch 1 not taken.
|
3371 | == CURLE_OK) { |
2069 | 3371 | perf::Xadd(counters_->sz_transfer_time, | |
2070 | 3371 | static_cast<int64_t>(elapsed * 1000)); | |
2071 | } | ||
2072 |
3/4✓ Branch 1 taken 3371 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 190 times.
✓ Branch 4 taken 3181 times.
|
3371 | } while (VerifyAndFinalize(retval, info)); |
2073 | 3181 | result = info->error_code(); | |
2074 |
1/2✓ Branch 2 taken 3181 times.
✗ Branch 3 not taken.
|
3181 | ReleaseCurlHandle(info->curl_handle()); |
2075 | 3181 | } | |
2076 | |||
2077 |
2/2✓ Branch 0 taken 192 times.
✓ Branch 1 taken 2989 times.
|
3181 | if (result != kFailOk) { |
2078 |
1/2✓ Branch 4 taken 192 times.
✗ Branch 5 not taken.
|
192 | LogCvmfs(kLogDownload, kLogDebug, |
2079 | "(manager '%s' - id %" PRId64 ") " | ||
2080 | "download failed (error %d - %s)", | ||
2081 | name_.c_str(), info->id(), result, Code2Ascii(result)); | ||
2082 | |||
2083 |
1/2✓ Branch 1 taken 192 times.
✗ Branch 2 not taken.
|
192 | if (info->sink() != NULL) { |
2084 |
1/2✓ Branch 2 taken 192 times.
✗ Branch 3 not taken.
|
192 | info->sink()->Purge(); |
2085 | } | ||
2086 | } | ||
2087 | |||
2088 | 3181 | return result; | |
2089 | } | ||
2090 | |||
2091 | |||
2092 | /** | ||
2093 | * Used by the client to connect the authz session manager to the download | ||
2094 | * manager. | ||
2095 | */ | ||
2096 | 1001 | void DownloadManager::SetCredentialsAttachment(CredentialsAttachment *ca) { | |
2097 | 1001 | const MutexLockGuard m(lock_options_); | |
2098 | 1001 | credentials_attachment_ = ca; | |
2099 | 1001 | } | |
2100 | |||
2101 | /** | ||
2102 | * Gets the DNS sever. | ||
2103 | */ | ||
2104 | ✗ | std::string DownloadManager::GetDnsServer() const { return opt_dns_server_; } | |
2105 | |||
2106 | /** | ||
2107 | * Sets a DNS server. Only for testing as it cannot be reverted to the system | ||
2108 | * default. | ||
2109 | */ | ||
2110 | ✗ | void DownloadManager::SetDnsServer(const string &address) { | |
2111 | ✗ | if (!address.empty()) { | |
2112 | ✗ | const MutexLockGuard m(lock_options_); | |
2113 | ✗ | opt_dns_server_ = address; | |
2114 | ✗ | assert(!opt_dns_server_.empty()); | |
2115 | |||
2116 | ✗ | vector<string> servers; | |
2117 | ✗ | servers.push_back(address); | |
2118 | ✗ | const bool retval = resolver_->SetResolvers(servers); | |
2119 | ✗ | assert(retval); | |
2120 | } | ||
2121 | ✗ | LogCvmfs(kLogDownload, kLogSyslog, "(manager '%s') set nameserver to %s", | |
2122 | name_.c_str(), address.c_str()); | ||
2123 | } | ||
2124 | |||
2125 | |||
2126 | /** | ||
2127 | * Sets the DNS query timeout parameters. | ||
2128 | */ | ||
2129 | 1779 | void DownloadManager::SetDnsParameters(const unsigned retries, | |
2130 | const unsigned timeout_ms) { | ||
2131 | 1779 | const MutexLockGuard m(lock_options_); | |
2132 | 1779 | if ((resolver_->retries() == retries) | |
2133 |
3/6✓ Branch 0 taken 1779 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1779 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1779 times.
✗ Branch 6 not taken.
|
1779 | && (resolver_->timeout_ms() == timeout_ms)) { |
2134 | 1779 | return; | |
2135 | } | ||
2136 | ✗ | delete resolver_; | |
2137 | ✗ | resolver_ = NULL; | |
2138 | ✗ | resolver_ = dns::NormalResolver::Create(opt_ipv4_only_, retries, timeout_ms); | |
2139 | ✗ | assert(resolver_); | |
2140 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1779 times.
|
1779 | } |
2141 | |||
2142 | |||
2143 | 1779 | void DownloadManager::SetDnsTtlLimits(const unsigned min_seconds, | |
2144 | const unsigned max_seconds) { | ||
2145 | 1779 | const MutexLockGuard m(lock_options_); | |
2146 | 1779 | resolver_->set_min_ttl(min_seconds); | |
2147 | 1779 | resolver_->set_max_ttl(max_seconds); | |
2148 | 1779 | } | |
2149 | |||
2150 | |||
2151 | ✗ | void DownloadManager::SetIpPreference(dns::IpPreference preference) { | |
2152 | ✗ | const MutexLockGuard m(lock_options_); | |
2153 | ✗ | opt_ip_preference_ = preference; | |
2154 | } | ||
2155 | |||
2156 | |||
2157 | /** | ||
2158 | * Sets two timeout values for proxied and for direct connections, respectively. | ||
2159 | * The timeout counts for all sorts of connection phases, | ||
2160 | * DNS, HTTP connect, etc. | ||
2161 | */ | ||
2162 | 2221 | void DownloadManager::SetTimeout(const unsigned seconds_proxy, | |
2163 | const unsigned seconds_direct) { | ||
2164 | 2221 | const MutexLockGuard m(lock_options_); | |
2165 | 2221 | opt_timeout_proxy_ = seconds_proxy; | |
2166 | 2221 | opt_timeout_direct_ = seconds_direct; | |
2167 | 2221 | } | |
2168 | |||
2169 | |||
2170 | /** | ||
2171 | * Sets contains the average transfer speed in bytes per second that the | ||
2172 | * transfer should be below during CURLOPT_LOW_SPEED_TIME seconds for libcurl to | ||
2173 | * consider it to be too slow and abort. Only effective for new connections. | ||
2174 | */ | ||
2175 | ✗ | void DownloadManager::SetLowSpeedLimit(const unsigned low_speed_limit) { | |
2176 | ✗ | const MutexLockGuard m(lock_options_); | |
2177 | ✗ | opt_low_speed_limit_ = low_speed_limit; | |
2178 | } | ||
2179 | |||
2180 | |||
2181 | /** | ||
2182 | * Receives the currently active timeout values. | ||
2183 | */ | ||
2184 | 746 | void DownloadManager::GetTimeout(unsigned *seconds_proxy, | |
2185 | unsigned *seconds_direct) { | ||
2186 | 746 | const MutexLockGuard m(lock_options_); | |
2187 | 746 | *seconds_proxy = opt_timeout_proxy_; | |
2188 | 746 | *seconds_direct = opt_timeout_direct_; | |
2189 | 746 | } | |
2190 | |||
2191 | |||
2192 | /** | ||
2193 | * Parses a list of ';'-separated hosts for the metalink chain. The empty | ||
2194 | * string removes the metalink list. | ||
2195 | */ | ||
2196 | ✗ | void DownloadManager::SetMetalinkChain(const string &metalink_list) { | |
2197 | ✗ | SetMetalinkChain(SplitString(metalink_list, ';')); | |
2198 | } | ||
2199 | |||
2200 | |||
2201 | ✗ | void DownloadManager::SetMetalinkChain( | |
2202 | const std::vector<std::string> &metalink_list) { | ||
2203 | ✗ | const MutexLockGuard m(lock_options_); | |
2204 | ✗ | opt_metalink_.timestamp_backup = 0; | |
2205 | ✗ | delete opt_metalink_.chain; | |
2206 | ✗ | opt_metalink_.current = 0; | |
2207 | |||
2208 | ✗ | if (metalink_list.empty()) { | |
2209 | ✗ | opt_metalink_.chain = NULL; | |
2210 | ✗ | return; | |
2211 | } | ||
2212 | |||
2213 | ✗ | opt_metalink_.chain = new vector<string>(metalink_list); | |
2214 | } | ||
2215 | |||
2216 | |||
2217 | /** | ||
2218 | * Retrieves the currently set chain of metalink hosts and the currently | ||
2219 | * used metalink host. | ||
2220 | */ | ||
2221 | ✗ | void DownloadManager::GetMetalinkInfo(vector<string> *metalink_chain, | |
2222 | unsigned *current_metalink) { | ||
2223 | ✗ | const MutexLockGuard m(lock_options_); | |
2224 | ✗ | if (opt_metalink_.chain) { | |
2225 | ✗ | if (current_metalink) { | |
2226 | ✗ | *current_metalink = opt_metalink_.current; | |
2227 | } | ||
2228 | ✗ | if (metalink_chain) { | |
2229 | ✗ | *metalink_chain = *opt_metalink_.chain; | |
2230 | } | ||
2231 | } | ||
2232 | } | ||
2233 | |||
2234 | |||
2235 | /** | ||
2236 | * Parses a list of ';'-separated hosts for the host chain. The empty string | ||
2237 | * removes the host list. | ||
2238 | */ | ||
2239 | 1662 | void DownloadManager::SetHostChain(const string &host_list) { | |
2240 |
1/2✓ Branch 2 taken 1662 times.
✗ Branch 3 not taken.
|
1662 | SetHostChain(SplitString(host_list, ';')); |
2241 | 1662 | } | |
2242 | |||
2243 | |||
2244 | 1704 | void DownloadManager::SetHostChain(const std::vector<std::string> &host_list) { | |
2245 | 1704 | const MutexLockGuard m(lock_options_); | |
2246 | 1704 | opt_host_.timestamp_backup = 0; | |
2247 |
2/2✓ Branch 0 taken 789 times.
✓ Branch 1 taken 915 times.
|
1704 | delete opt_host_.chain; |
2248 |
2/2✓ Branch 0 taken 789 times.
✓ Branch 1 taken 915 times.
|
1704 | delete opt_host_chain_rtt_; |
2249 | 1704 | opt_host_.current = 0; | |
2250 | |||
2251 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1704 times.
|
1704 | if (host_list.empty()) { |
2252 | ✗ | opt_host_.chain = NULL; | |
2253 | ✗ | opt_host_chain_rtt_ = NULL; | |
2254 | ✗ | return; | |
2255 | } | ||
2256 | |||
2257 |
2/4✓ Branch 1 taken 1704 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1704 times.
✗ Branch 5 not taken.
|
1704 | opt_host_.chain = new vector<string>(host_list); |
2258 | 3408 | opt_host_chain_rtt_ = new vector<int>(opt_host_.chain->size(), | |
2259 |
2/4✓ Branch 1 taken 1704 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1704 times.
✗ Branch 5 not taken.
|
1704 | kProbeUnprobed); |
2260 | // LogCvmfs(kLogDownload, kLogSyslog, "using host %s", | ||
2261 | // (*opt_host_.chain)[0].c_str()); | ||
2262 |
1/2✓ Branch 1 taken 1704 times.
✗ Branch 2 not taken.
|
1704 | } |
2263 | |||
2264 | |||
2265 | /** | ||
2266 | * Retrieves the currently set chain of hosts, their round trip times, and the | ||
2267 | * currently used host. | ||
2268 | */ | ||
2269 | 294 | void DownloadManager::GetHostInfo(vector<string> *host_chain, vector<int> *rtt, | |
2270 | unsigned *current_host) { | ||
2271 | 294 | const MutexLockGuard m(lock_options_); | |
2272 |
1/2✓ Branch 0 taken 294 times.
✗ Branch 1 not taken.
|
294 | if (opt_host_.chain) { |
2273 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 294 times.
|
294 | if (current_host) { |
2274 | ✗ | *current_host = opt_host_.current; | |
2275 | } | ||
2276 |
1/2✓ Branch 0 taken 294 times.
✗ Branch 1 not taken.
|
294 | if (host_chain) { |
2277 |
1/2✓ Branch 1 taken 294 times.
✗ Branch 2 not taken.
|
294 | *host_chain = *opt_host_.chain; |
2278 | } | ||
2279 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 294 times.
|
294 | if (rtt) { |
2280 | ✗ | *rtt = *opt_host_chain_rtt_; | |
2281 | } | ||
2282 | } | ||
2283 | 294 | } | |
2284 | |||
2285 | |||
2286 | /** | ||
2287 | * Jumps to the next proxy in the ring of forward proxy servers. | ||
2288 | * Selects one randomly from a load-balancing group. | ||
2289 | * | ||
2290 | * Allow for the fact that the proxy may have already been failed by | ||
2291 | * another transfer, or that the proxy may no longer be part of the | ||
2292 | * current load-balancing group. | ||
2293 | */ | ||
2294 | 16 | void DownloadManager::SwitchProxy(JobInfo *info) { | |
2295 | 16 | const MutexLockGuard m(lock_options_); | |
2296 | |||
2297 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (!opt_proxy_groups_) { |
2298 | ✗ | return; | |
2299 | } | ||
2300 | |||
2301 | // Fail any matching proxies within the current load-balancing group | ||
2302 | 16 | vector<ProxyInfo> *group = current_proxy_group(); | |
2303 | 16 | const unsigned group_size = group->size(); | |
2304 | 16 | unsigned failed = 0; | |
2305 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 16 times.
|
32 | for (unsigned i = 0; i < group_size - opt_proxy_groups_current_burned_; ++i) { |
2306 |
5/14✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 16 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 16 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 16 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
|
16 | if (info && (info->proxy() == (*group)[i].url)) { |
2307 | // Move to list of failed proxies | ||
2308 | 16 | opt_proxy_groups_current_burned_++; | |
2309 |
1/2✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
|
16 | swap((*group)[i], |
2310 | 16 | (*group)[group_size - opt_proxy_groups_current_burned_]); | |
2311 | 16 | perf::Inc(counters_->n_proxy_failover); | |
2312 | 16 | failed++; | |
2313 | } | ||
2314 | } | ||
2315 | |||
2316 | // Do nothing more unless at least one proxy was marked as failed | ||
2317 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (!failed) |
2318 | ✗ | return; | |
2319 | |||
2320 | // If all proxies from the current load-balancing group are burned, switch to | ||
2321 | // another group | ||
2322 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | if (opt_proxy_groups_current_burned_ == group->size()) { |
2323 | 16 | opt_proxy_groups_current_burned_ = 0; | |
2324 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | if (opt_proxy_groups_->size() > 1) { |
2325 | 32 | opt_proxy_groups_current_ = (opt_proxy_groups_current_ + 1) | |
2326 | 16 | % opt_proxy_groups_->size(); | |
2327 | // Remember the timestamp of switching to backup proxies | ||
2328 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (opt_proxy_groups_reset_after_ > 0) { |
2329 | ✗ | if (opt_proxy_groups_current_ > 0) { | |
2330 | ✗ | if (opt_timestamp_backup_proxies_ == 0) | |
2331 | ✗ | opt_timestamp_backup_proxies_ = time(NULL); | |
2332 | // LogCvmfs(kLogDownload, kLogDebug | kLogSyslogWarn, | ||
2333 | // "switched to (another) backup proxy group"); | ||
2334 | } else { | ||
2335 | ✗ | opt_timestamp_backup_proxies_ = 0; | |
2336 | // LogCvmfs(kLogDownload, kLogDebug | kLogSyslogWarn, | ||
2337 | // "switched back to primary proxy group"); | ||
2338 | } | ||
2339 | ✗ | opt_timestamp_failover_proxies_ = 0; | |
2340 | } | ||
2341 | } | ||
2342 | } else { | ||
2343 | // Record failover time | ||
2344 | ✗ | if (opt_proxy_groups_reset_after_ > 0) { | |
2345 | ✗ | if (opt_timestamp_failover_proxies_ == 0) | |
2346 | ✗ | opt_timestamp_failover_proxies_ = time(NULL); | |
2347 | } | ||
2348 | } | ||
2349 | |||
2350 |
2/4✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 16 times.
✗ Branch 6 not taken.
|
16 | UpdateProxiesUnlocked("failed proxy"); |
2351 |
1/2✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
|
16 | LogCvmfs(kLogDownload, kLogDebug, |
2352 | "(manager '%s' - id %" PRId64 ") " | ||
2353 | "%lu proxies remain in group", | ||
2354 | name_.c_str(), info->id(), | ||
2355 | 16 | current_proxy_group()->size() - opt_proxy_groups_current_burned_); | |
2356 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | } |
2357 | |||
2358 | |||
2359 | /** | ||
2360 | * Switches to the next host in the chain. If jobinfo is set, switch only if | ||
2361 | * the current host is identical to the one used by jobinfo, otherwise another | ||
2362 | * transfer has already done the switch. | ||
2363 | */ | ||
2364 | 33 | void DownloadManager::SwitchHostInfo(const std::string &typ, | |
2365 | HostInfo &info, | ||
2366 | JobInfo *jobinfo) { | ||
2367 | 33 | const MutexLockGuard m(lock_options_); | |
2368 | |||
2369 |
5/6✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 32 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 32 times.
|
33 | if (!info.chain || (info.chain->size() == 1)) { |
2370 | 1 | return; | |
2371 | } | ||
2372 | |||
2373 |
1/2✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
|
32 | if (jobinfo) { |
2374 | int lastused; | ||
2375 |
1/2✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
|
32 | if (typ == "host") { |
2376 | 32 | lastused = jobinfo->current_host_chain_index(); | |
2377 | } else { | ||
2378 | ✗ | lastused = jobinfo->current_metalink_chain_index(); | |
2379 | } | ||
2380 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | if (lastused != info.current) { |
2381 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
2382 | "(manager '%s' - id %" PRId64 ")" | ||
2383 | "don't switch %s, " | ||
2384 | "last used %s: %s, current %s: %s", | ||
2385 | name_.c_str(), jobinfo->id(), typ.c_str(), typ.c_str(), | ||
2386 | ✗ | (*info.chain)[lastused].c_str(), typ.c_str(), | |
2387 | ✗ | (*info.chain)[info.current].c_str()); | |
2388 | ✗ | return; | |
2389 | } | ||
2390 | } | ||
2391 | |||
2392 |
1/2✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
|
32 | string reason = "manually triggered"; |
2393 |
1/2✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
|
32 | string info_id = "(manager " + name_; |
2394 |
1/2✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
|
32 | if (jobinfo) { |
2395 |
1/2✓ Branch 3 taken 32 times.
✗ Branch 4 not taken.
|
32 | reason = download::Code2Ascii(jobinfo->error_code()); |
2396 |
2/4✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 32 times.
✗ Branch 6 not taken.
|
32 | info_id = " - id " + StringifyInt(jobinfo->id()); |
2397 | } | ||
2398 |
1/2✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
|
32 | info_id += ")"; |
2399 | |||
2400 |
1/2✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
|
32 | const std::string old_host = (*info.chain)[info.current]; |
2401 | 32 | info.current = (info.current + 1) % static_cast<int>(info.chain->size()); | |
2402 |
1/2✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
|
32 | if (typ == "host") { |
2403 | 32 | perf::Inc(counters_->n_host_failover); | |
2404 | } else { | ||
2405 | ✗ | perf::Inc(counters_->n_metalink_failover); | |
2406 | } | ||
2407 |
1/3✓ Branch 6 taken 32 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
64 | LogCvmfs(kLogDownload, kLogDebug | kLogSyslogWarn, |
2408 | "%s switching %s from %s to %s (%s)", info_id.c_str(), typ.c_str(), | ||
2409 | 32 | old_host.c_str(), (*info.chain)[info.current].c_str(), | |
2410 | reason.c_str()); | ||
2411 | |||
2412 | // Remember the timestamp of switching to backup host | ||
2413 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | if (info.reset_after > 0) { |
2414 | ✗ | if (info.current != 0) { | |
2415 | ✗ | if (info.timestamp_backup == 0) | |
2416 | ✗ | info.timestamp_backup = time(NULL); | |
2417 | } else { | ||
2418 | ✗ | info.timestamp_backup = 0; | |
2419 | } | ||
2420 | } | ||
2421 |
2/2✓ Branch 4 taken 32 times.
✓ Branch 5 taken 1 times.
|
33 | } |
2422 | |||
2423 | 33 | void DownloadManager::SwitchHost(JobInfo *info) { | |
2424 |
2/4✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 33 times.
✗ Branch 6 not taken.
|
33 | SwitchHostInfo("host", opt_host_, info); |
2425 | 33 | } | |
2426 | |||
2427 | 1 | void DownloadManager::SwitchHost() { SwitchHost(NULL); } | |
2428 | |||
2429 | |||
2430 | ✗ | void DownloadManager::SwitchMetalink(JobInfo *info) { | |
2431 | ✗ | SwitchHostInfo("metalink", opt_metalink_, info); | |
2432 | } | ||
2433 | |||
2434 | |||
2435 | ✗ | void DownloadManager::SwitchMetalink() { SwitchMetalink(NULL); } | |
2436 | |||
2437 | 1603 | bool DownloadManager::CheckMetalinkChain(time_t now) { | |
2438 | 1603 | return (opt_metalink_.chain | |
2439 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 1603 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
1603 | && ((opt_metalink_timestamp_link_ == 0) |
2440 | ✗ | || (static_cast<int64_t>((now == 0) ? time(NULL) : now) | |
2441 | ✗ | > static_cast<int64_t>(opt_metalink_timestamp_link_ | |
2442 |
0/2✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
1603 | + opt_metalink_.reset_after)))); |
2443 | } | ||
2444 | |||
2445 | |||
2446 | /** | ||
2447 | * Orders the hostlist according to RTT of downloading .cvmfschecksum. | ||
2448 | * Sets the current host to the best-responsive host. | ||
2449 | * If you change the host list in between by SetHostChain(), it will be | ||
2450 | * overwritten by this function. | ||
2451 | */ | ||
2452 | ✗ | void DownloadManager::ProbeHosts() { | |
2453 | ✗ | vector<string> host_chain; | |
2454 | ✗ | vector<int> host_rtt; | |
2455 | unsigned current_host; | ||
2456 | |||
2457 | ✗ | GetHostInfo(&host_chain, &host_rtt, ¤t_host); | |
2458 | |||
2459 | // Stopwatch, two times to fill caches first | ||
2460 | unsigned i, retries; | ||
2461 | ✗ | string url; | |
2462 | |||
2463 | ✗ | cvmfs::MemSink memsink; | |
2464 | ✗ | JobInfo info(&url, false, false, NULL, &memsink); | |
2465 | ✗ | for (retries = 0; retries < 2; ++retries) { | |
2466 | ✗ | for (i = 0; i < host_chain.size(); ++i) { | |
2467 | ✗ | url = host_chain[i] + "/.cvmfspublished"; | |
2468 | |||
2469 | struct timeval tv_start, tv_end; | ||
2470 | ✗ | gettimeofday(&tv_start, NULL); | |
2471 | ✗ | const Failures result = Fetch(&info); | |
2472 | ✗ | gettimeofday(&tv_end, NULL); | |
2473 | ✗ | memsink.Reset(); | |
2474 | ✗ | if (result == kFailOk) { | |
2475 | ✗ | host_rtt[i] = static_cast<int>(DiffTimeSeconds(tv_start, tv_end) | |
2476 | ✗ | * 1000); | |
2477 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
2478 | "(manager '%s' - id %" PRId64 ") " | ||
2479 | "probing host %s had %dms rtt", | ||
2480 | ✗ | name_.c_str(), info.id(), url.c_str(), host_rtt[i]); | |
2481 | } else { | ||
2482 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
2483 | "(manager '%s' - id %" PRId64 ") " | ||
2484 | "error while probing host %s: %d %s", | ||
2485 | name_.c_str(), info.id(), url.c_str(), result, | ||
2486 | Code2Ascii(result)); | ||
2487 | ✗ | host_rtt[i] = INT_MAX; | |
2488 | } | ||
2489 | } | ||
2490 | } | ||
2491 | |||
2492 | ✗ | SortTeam(&host_rtt, &host_chain); | |
2493 | ✗ | for (i = 0; i < host_chain.size(); ++i) { | |
2494 | ✗ | if (host_rtt[i] == INT_MAX) | |
2495 | ✗ | host_rtt[i] = kProbeDown; | |
2496 | } | ||
2497 | |||
2498 | ✗ | const MutexLockGuard m(lock_options_); | |
2499 | ✗ | delete opt_host_.chain; | |
2500 | ✗ | delete opt_host_chain_rtt_; | |
2501 | ✗ | opt_host_.chain = new vector<string>(host_chain); | |
2502 | ✗ | opt_host_chain_rtt_ = new vector<int>(host_rtt); | |
2503 | ✗ | opt_host_.current = 0; | |
2504 | } | ||
2505 | |||
2506 | ✗ | bool DownloadManager::GeoSortServers(std::vector<std::string> *servers, | |
2507 | std::vector<uint64_t> *output_order) { | ||
2508 | ✗ | if (!servers) { | |
2509 | ✗ | return false; | |
2510 | } | ||
2511 | ✗ | if (servers->size() == 1) { | |
2512 | ✗ | if (output_order) { | |
2513 | ✗ | output_order->clear(); | |
2514 | ✗ | output_order->push_back(0); | |
2515 | } | ||
2516 | ✗ | return true; | |
2517 | } | ||
2518 | |||
2519 | ✗ | std::vector<std::string> host_chain; | |
2520 | ✗ | GetHostInfo(&host_chain, NULL, NULL); | |
2521 | |||
2522 | ✗ | std::vector<std::string> server_dns_names; | |
2523 | ✗ | server_dns_names.reserve(servers->size()); | |
2524 | ✗ | for (unsigned i = 0; i < servers->size(); ++i) { | |
2525 | ✗ | const std::string host = dns::ExtractHost((*servers)[i]); | |
2526 | ✗ | server_dns_names.push_back(host.empty() ? (*servers)[i] : host); | |
2527 | } | ||
2528 | ✗ | const std::string host_list = JoinStrings(server_dns_names, ","); | |
2529 | |||
2530 | ✗ | vector<string> host_chain_shuffled; | |
2531 | { | ||
2532 | // Protect against concurrent access to prng_ | ||
2533 | ✗ | const MutexLockGuard m(lock_options_); | |
2534 | // Determine random hosts for the Geo-API query | ||
2535 | ✗ | host_chain_shuffled = Shuffle(host_chain, &prng_); | |
2536 | } | ||
2537 | // Request ordered list via Geo-API | ||
2538 | ✗ | bool success = false; | |
2539 | ✗ | const unsigned max_attempts = std::min(host_chain_shuffled.size(), size_t(3)); | |
2540 | ✗ | vector<uint64_t> geo_order(servers->size()); | |
2541 | ✗ | for (unsigned i = 0; i < max_attempts; ++i) { | |
2542 | ✗ | const string url = host_chain_shuffled[i] + "/api/v1.0/geo/@proxy@/" | |
2543 | ✗ | + host_list; | |
2544 | ✗ | LogCvmfs(kLogDownload, kLogDebug, | |
2545 | "(manager '%s') requesting ordered server list from %s", | ||
2546 | name_.c_str(), url.c_str()); | ||
2547 | ✗ | cvmfs::MemSink memsink; | |
2548 | ✗ | JobInfo info(&url, false, false, NULL, &memsink); | |
2549 | ✗ | const Failures result = Fetch(&info); | |
2550 | ✗ | if (result == kFailOk) { | |
2551 | ✗ | const string order(reinterpret_cast<char *>(memsink.data()), | |
2552 | ✗ | memsink.pos()); | |
2553 | ✗ | memsink.Reset(); | |
2554 | ✗ | const bool retval = ValidateGeoReply(order, servers->size(), &geo_order); | |
2555 | ✗ | if (!retval) { | |
2556 | ✗ | LogCvmfs(kLogDownload, kLogDebug | kLogSyslogWarn, | |
2557 | "(manager '%s') retrieved invalid GeoAPI reply from %s [%s]", | ||
2558 | name_.c_str(), url.c_str(), order.c_str()); | ||
2559 | } else { | ||
2560 | ✗ | LogCvmfs(kLogDownload, kLogDebug | kLogSyslog, | |
2561 | "(manager '%s') " | ||
2562 | "geographic order of servers retrieved from %s", | ||
2563 | name_.c_str(), | ||
2564 | ✗ | dns::ExtractHost(host_chain_shuffled[i]).c_str()); | |
2565 | // remove new line at end of "order" | ||
2566 | ✗ | LogCvmfs(kLogDownload, kLogDebug, "order is %s", | |
2567 | ✗ | Trim(order, true /* trim_newline */).c_str()); | |
2568 | ✗ | success = true; | |
2569 | ✗ | break; | |
2570 | } | ||
2571 | ✗ | } else { | |
2572 | ✗ | LogCvmfs(kLogDownload, kLogDebug | kLogSyslogWarn, | |
2573 | "(manager '%s') GeoAPI request for %s failed with error %d [%s]", | ||
2574 | name_.c_str(), url.c_str(), result, Code2Ascii(result)); | ||
2575 | } | ||
2576 | } | ||
2577 | ✗ | if (!success) { | |
2578 | ✗ | LogCvmfs(kLogDownload, kLogDebug | kLogSyslogWarn, | |
2579 | "(manager '%s') " | ||
2580 | "failed to retrieve geographic order from stratum 1 servers", | ||
2581 | name_.c_str()); | ||
2582 | ✗ | return false; | |
2583 | } | ||
2584 | |||
2585 | ✗ | if (output_order) { | |
2586 | ✗ | output_order->swap(geo_order); | |
2587 | } else { | ||
2588 | ✗ | std::vector<std::string> sorted_servers; | |
2589 | ✗ | sorted_servers.reserve(geo_order.size()); | |
2590 | ✗ | for (unsigned i = 0; i < geo_order.size(); ++i) { | |
2591 | ✗ | const uint64_t orderval = geo_order[i]; | |
2592 | ✗ | sorted_servers.push_back((*servers)[orderval]); | |
2593 | } | ||
2594 | ✗ | servers->swap(sorted_servers); | |
2595 | } | ||
2596 | ✗ | return true; | |
2597 | } | ||
2598 | |||
2599 | |||
2600 | /** | ||
2601 | * Uses the Geo-API of Stratum 1s to let any of them order the list of servers | ||
2602 | * and fallback proxies (if any). | ||
2603 | * Tries at most three random Stratum 1s before giving up. | ||
2604 | * If you change the host list in between by SetHostChain() or the fallback | ||
2605 | * proxy list by SetProxyChain(), they will be overwritten by this function. | ||
2606 | */ | ||
2607 | ✗ | bool DownloadManager::ProbeGeo() { | |
2608 | ✗ | vector<string> host_chain; | |
2609 | ✗ | vector<int> host_rtt; | |
2610 | unsigned current_host; | ||
2611 | ✗ | vector<vector<ProxyInfo> > proxy_chain; | |
2612 | unsigned fallback_group; | ||
2613 | |||
2614 | ✗ | GetHostInfo(&host_chain, &host_rtt, ¤t_host); | |
2615 | ✗ | GetProxyInfo(&proxy_chain, NULL, &fallback_group); | |
2616 | ✗ | if ((host_chain.size() < 2) && ((proxy_chain.size() - fallback_group) < 2)) | |
2617 | ✗ | return true; | |
2618 | |||
2619 | ✗ | vector<string> host_names; | |
2620 | ✗ | for (unsigned i = 0; i < host_chain.size(); ++i) | |
2621 | ✗ | host_names.push_back(dns::ExtractHost(host_chain[i])); | |
2622 | ✗ | SortTeam(&host_names, &host_chain); | |
2623 | ✗ | const unsigned last_geo_host = host_names.size(); | |
2624 | |||
2625 | ✗ | if ((fallback_group == 0) && (last_geo_host > 1)) { | |
2626 | // There are no non-fallback proxies, which means that the client | ||
2627 | // will always use the fallback proxies. Add a keyword separator | ||
2628 | // between the hosts and fallback proxies so the geosorting service | ||
2629 | // will know to sort the hosts based on the distance from the | ||
2630 | // closest fallback proxy rather than the distance from the client. | ||
2631 | ✗ | host_names.push_back("+PXYSEP+"); | |
2632 | } | ||
2633 | |||
2634 | // Add fallback proxy names to the end of the host list | ||
2635 | ✗ | const unsigned first_geo_fallback = host_names.size(); | |
2636 | ✗ | for (unsigned i = fallback_group; i < proxy_chain.size(); ++i) { | |
2637 | // We only take the first fallback proxy name from every group under the | ||
2638 | // assumption that load-balanced servers are at the same location | ||
2639 | ✗ | host_names.push_back(proxy_chain[i][0].host.name()); | |
2640 | } | ||
2641 | |||
2642 | ✗ | std::vector<uint64_t> geo_order; | |
2643 | ✗ | const bool success = GeoSortServers(&host_names, &geo_order); | |
2644 | ✗ | if (!success) { | |
2645 | // GeoSortServers already logged a failure message. | ||
2646 | ✗ | return false; | |
2647 | } | ||
2648 | |||
2649 | // Re-install host chain and proxy chain | ||
2650 | ✗ | const MutexLockGuard m(lock_options_); | |
2651 | ✗ | delete opt_host_.chain; | |
2652 | ✗ | opt_num_proxies_ = 0; | |
2653 | ✗ | opt_host_.chain = new vector<string>(host_chain.size()); | |
2654 | |||
2655 | // It's possible that opt_proxy_groups_fallback_ might have changed while | ||
2656 | // the lock wasn't held | ||
2657 | vector<vector<ProxyInfo> > *proxy_groups = new vector<vector<ProxyInfo> >( | ||
2658 | ✗ | opt_proxy_groups_fallback_ + proxy_chain.size() - fallback_group); | |
2659 | // First copy the non-fallback part of the current proxy chain | ||
2660 | ✗ | for (unsigned i = 0; i < opt_proxy_groups_fallback_; ++i) { | |
2661 | ✗ | (*proxy_groups)[i] = (*opt_proxy_groups_)[i]; | |
2662 | ✗ | opt_num_proxies_ += (*opt_proxy_groups_)[i].size(); | |
2663 | } | ||
2664 | |||
2665 | // Copy the host chain and fallback proxies by geo order. Array indices | ||
2666 | // in geo_order that are smaller than last_geo_host refer to a stratum 1, | ||
2667 | // and those indices greater than or equal to first_geo_fallback refer to | ||
2668 | // a fallback proxy. | ||
2669 | ✗ | unsigned hosti = 0; | |
2670 | ✗ | unsigned proxyi = opt_proxy_groups_fallback_; | |
2671 | ✗ | for (unsigned i = 0; i < geo_order.size(); ++i) { | |
2672 | ✗ | const uint64_t orderval = geo_order[i]; | |
2673 | ✗ | if (orderval < static_cast<uint64_t>(last_geo_host)) { | |
2674 | // LogCvmfs(kLogCvmfs, kLogSyslog, "this is orderval %u at host index | ||
2675 | // %u", orderval, hosti); | ||
2676 | ✗ | (*opt_host_.chain)[hosti++] = host_chain[orderval]; | |
2677 | ✗ | } else if (orderval >= static_cast<uint64_t>(first_geo_fallback)) { | |
2678 | // LogCvmfs(kLogCvmfs, kLogSyslog, | ||
2679 | // "this is orderval %u at proxy index %u, using proxy_chain index %u", | ||
2680 | // orderval, proxyi, fallback_group + orderval - first_geo_fallback); | ||
2681 | ✗ | (*proxy_groups)[proxyi] = proxy_chain[fallback_group + orderval | |
2682 | ✗ | - first_geo_fallback]; | |
2683 | ✗ | opt_num_proxies_ += (*proxy_groups)[proxyi].size(); | |
2684 | ✗ | proxyi++; | |
2685 | } | ||
2686 | } | ||
2687 | |||
2688 | ✗ | opt_proxy_map_.clear(); | |
2689 | ✗ | delete opt_proxy_groups_; | |
2690 | ✗ | opt_proxy_groups_ = proxy_groups; | |
2691 | // In pathological cases, opt_proxy_groups_current_ can be larger now when | ||
2692 | // proxies changed in-between. | ||
2693 | ✗ | if (opt_proxy_groups_current_ > opt_proxy_groups_->size()) { | |
2694 | ✗ | if (opt_proxy_groups_->size() == 0) { | |
2695 | ✗ | opt_proxy_groups_current_ = 0; | |
2696 | } else { | ||
2697 | ✗ | opt_proxy_groups_current_ = opt_proxy_groups_->size() - 1; | |
2698 | } | ||
2699 | ✗ | opt_proxy_groups_current_burned_ = 0; | |
2700 | } | ||
2701 | |||
2702 | ✗ | UpdateProxiesUnlocked("geosort"); | |
2703 | |||
2704 | ✗ | delete opt_host_chain_rtt_; | |
2705 | ✗ | opt_host_chain_rtt_ = new vector<int>(host_chain.size(), kProbeGeo); | |
2706 | ✗ | opt_host_.current = 0; | |
2707 | |||
2708 | ✗ | return true; | |
2709 | } | ||
2710 | |||
2711 | |||
2712 | /** | ||
2713 | * Validates a string of the form "1,4,2,3" representing in which order the | ||
2714 | * the expected_size number of hosts should be put for optimal geographic | ||
2715 | * proximity. Returns false if the reply_order string is invalid, otherwise | ||
2716 | * fills in the reply_vals array with zero-based order indexes (e.g. | ||
2717 | * [0,3,1,2]) and returns true. | ||
2718 | */ | ||
2719 | 224 | bool DownloadManager::ValidateGeoReply(const string &reply_order, | |
2720 | const unsigned expected_size, | ||
2721 | vector<uint64_t> *reply_vals) { | ||
2722 |
2/2✓ Branch 1 taken 16 times.
✓ Branch 2 taken 208 times.
|
224 | if (reply_order.empty()) |
2723 | 16 | return false; | |
2724 |
2/4✓ Branch 2 taken 208 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 208 times.
✗ Branch 6 not taken.
|
416 | const sanitizer::InputSanitizer sanitizer("09 , \n"); |
2725 |
3/4✓ Branch 1 taken 208 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
✓ Branch 4 taken 192 times.
|
208 | if (!sanitizer.IsValid(reply_order)) |
2726 | 16 | return false; | |
2727 |
2/4✓ Branch 2 taken 192 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 192 times.
✗ Branch 6 not taken.
|
384 | const sanitizer::InputSanitizer strip_newline("09 ,"); |
2728 |
1/2✓ Branch 1 taken 192 times.
✗ Branch 2 not taken.
|
192 | vector<string> reply_strings = SplitString(strip_newline.Filter(reply_order), |
2729 |
1/2✓ Branch 1 taken 192 times.
✗ Branch 2 not taken.
|
192 | ','); |
2730 | 192 | vector<uint64_t> tmp_vals; | |
2731 |
2/2✓ Branch 1 taken 368 times.
✓ Branch 2 taken 160 times.
|
528 | for (unsigned i = 0; i < reply_strings.size(); ++i) { |
2732 |
2/2✓ Branch 2 taken 32 times.
✓ Branch 3 taken 336 times.
|
368 | if (reply_strings[i].empty()) |
2733 | 32 | return false; | |
2734 |
2/4✓ Branch 2 taken 336 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 336 times.
✗ Branch 6 not taken.
|
336 | tmp_vals.push_back(String2Uint64(reply_strings[i])); |
2735 | } | ||
2736 |
2/2✓ Branch 1 taken 80 times.
✓ Branch 2 taken 80 times.
|
160 | if (tmp_vals.size() != expected_size) |
2737 | 80 | return false; | |
2738 | |||
2739 | // Check if tmp_vals contains the number 1..n | ||
2740 |
1/2✓ Branch 3 taken 80 times.
✗ Branch 4 not taken.
|
80 | set<uint64_t> coverage(tmp_vals.begin(), tmp_vals.end()); |
2741 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 80 times.
|
80 | if (coverage.size() != tmp_vals.size()) |
2742 | ✗ | return false; | |
2743 |
5/6✓ Branch 2 taken 64 times.
✓ Branch 3 taken 16 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 64 times.
✓ Branch 9 taken 16 times.
✓ Branch 10 taken 64 times.
|
80 | if ((*coverage.begin() != 1) || (*coverage.rbegin() != coverage.size())) |
2744 | 16 | return false; | |
2745 | |||
2746 |
2/2✓ Branch 0 taken 144 times.
✓ Branch 1 taken 64 times.
|
208 | for (unsigned i = 0; i < expected_size; ++i) { |
2747 | 144 | (*reply_vals)[i] = tmp_vals[i] - 1; | |
2748 | } | ||
2749 | 64 | return true; | |
2750 | 208 | } | |
2751 | |||
2752 | |||
2753 | /** | ||
2754 | * Removes DIRECT from a list of ';' and '|' separated proxies. | ||
2755 | * \return true if DIRECT was present, false otherwise | ||
2756 | */ | ||
2757 | 1700 | bool DownloadManager::StripDirect(const string &proxy_list, | |
2758 | string *cleaned_list) { | ||
2759 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1700 times.
|
1700 | assert(cleaned_list); |
2760 |
2/2✓ Branch 1 taken 1556 times.
✓ Branch 2 taken 144 times.
|
1700 | if (proxy_list == "") { |
2761 |
1/2✓ Branch 1 taken 1556 times.
✗ Branch 2 not taken.
|
1556 | *cleaned_list = ""; |
2762 | 1556 | return false; | |
2763 | } | ||
2764 | 144 | bool result = false; | |
2765 | |||
2766 |
1/2✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
|
144 | vector<string> proxy_groups = SplitString(proxy_list, ';'); |
2767 | 144 | vector<string> cleaned_groups; | |
2768 |
2/2✓ Branch 1 taken 352 times.
✓ Branch 2 taken 144 times.
|
496 | for (unsigned i = 0; i < proxy_groups.size(); ++i) { |
2769 |
1/2✓ Branch 2 taken 352 times.
✗ Branch 3 not taken.
|
352 | vector<string> group = SplitString(proxy_groups[i], '|'); |
2770 | 352 | vector<string> cleaned; | |
2771 |
2/2✓ Branch 1 taken 592 times.
✓ Branch 2 taken 352 times.
|
944 | for (unsigned j = 0; j < group.size(); ++j) { |
2772 |
6/6✓ Branch 2 taken 432 times.
✓ Branch 3 taken 160 times.
✓ Branch 6 taken 208 times.
✓ Branch 7 taken 224 times.
✓ Branch 8 taken 368 times.
✓ Branch 9 taken 224 times.
|
592 | if ((group[j] == "DIRECT") || (group[j] == "")) { |
2773 | 368 | result = true; | |
2774 | } else { | ||
2775 |
1/2✓ Branch 2 taken 224 times.
✗ Branch 3 not taken.
|
224 | cleaned.push_back(group[j]); |
2776 | } | ||
2777 | } | ||
2778 |
2/2✓ Branch 1 taken 112 times.
✓ Branch 2 taken 240 times.
|
352 | if (!cleaned.empty()) |
2779 |
3/6✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 112 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 112 times.
✗ Branch 9 not taken.
|
112 | cleaned_groups.push_back(JoinStrings(cleaned, "|")); |
2780 | 352 | } | |
2781 | |||
2782 |
2/4✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 144 times.
✗ Branch 6 not taken.
|
144 | *cleaned_list = JoinStrings(cleaned_groups, ";"); |
2783 | 144 | return result; | |
2784 | 144 | } | |
2785 | |||
2786 | |||
2787 | /** | ||
2788 | * Parses a list of ';'- and '|'-separated proxy servers and fallback proxy | ||
2789 | * servers for the proxy groups. | ||
2790 | * The empty string for both removes the proxy chain. | ||
2791 | * The set_mode parameter can be used to set either proxies (leaving fallback | ||
2792 | * proxies unchanged) or fallback proxies (leaving regular proxies unchanged) | ||
2793 | * or both. | ||
2794 | */ | ||
2795 | 1540 | void DownloadManager::SetProxyChain(const string &proxy_list, | |
2796 | const string &fallback_proxy_list, | ||
2797 | const ProxySetModes set_mode) { | ||
2798 | 1540 | const MutexLockGuard m(lock_options_); | |
2799 | |||
2800 | 1540 | opt_timestamp_backup_proxies_ = 0; | |
2801 | 1540 | opt_timestamp_failover_proxies_ = 0; | |
2802 |
1/2✓ Branch 1 taken 1540 times.
✗ Branch 2 not taken.
|
1540 | string set_proxy_list = opt_proxy_list_; |
2803 |
1/2✓ Branch 1 taken 1540 times.
✗ Branch 2 not taken.
|
1540 | string set_proxy_fallback_list = opt_proxy_fallback_list_; |
2804 | bool contains_direct; | ||
2805 |
3/4✓ Branch 0 taken 1540 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1492 times.
✓ Branch 3 taken 48 times.
|
1540 | if ((set_mode == kSetProxyFallback) || (set_mode == kSetProxyBoth)) { |
2806 |
1/2✓ Branch 1 taken 1492 times.
✗ Branch 2 not taken.
|
1492 | opt_proxy_fallback_list_ = fallback_proxy_list; |
2807 | } | ||
2808 |
3/4✓ Branch 0 taken 1492 times.
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 1492 times.
✗ Branch 3 not taken.
|
1540 | if ((set_mode == kSetProxyRegular) || (set_mode == kSetProxyBoth)) { |
2809 |
1/2✓ Branch 1 taken 1540 times.
✗ Branch 2 not taken.
|
1540 | opt_proxy_list_ = proxy_list; |
2810 | } | ||
2811 |
1/2✓ Branch 1 taken 1540 times.
✗ Branch 2 not taken.
|
1540 | contains_direct = StripDirect(opt_proxy_fallback_list_, |
2812 | &set_proxy_fallback_list); | ||
2813 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1540 times.
|
1540 | if (contains_direct) { |
2814 | ✗ | LogCvmfs(kLogDownload, kLogSyslogWarn | kLogDebug, | |
2815 | "(manager '%s') fallback proxies do not support DIRECT, removing", | ||
2816 | name_.c_str()); | ||
2817 | } | ||
2818 |
1/2✓ Branch 1 taken 1540 times.
✗ Branch 2 not taken.
|
1540 | if (set_proxy_fallback_list == "") { |
2819 |
1/2✓ Branch 1 taken 1540 times.
✗ Branch 2 not taken.
|
1540 | set_proxy_list = opt_proxy_list_; |
2820 | } else { | ||
2821 | ✗ | const bool contains_direct = StripDirect(opt_proxy_list_, &set_proxy_list); | |
2822 | ✗ | if (contains_direct) { | |
2823 | ✗ | LogCvmfs(kLogDownload, kLogSyslog | kLogDebug, | |
2824 | "(manager '%s') skipping DIRECT proxy to use fallback proxy", | ||
2825 | name_.c_str()); | ||
2826 | } | ||
2827 | } | ||
2828 | |||
2829 | // From this point on, use set_proxy_list and set_fallback_proxy_list as | ||
2830 | // effective proxy lists! | ||
2831 | |||
2832 | 1540 | opt_proxy_map_.clear(); | |
2833 |
2/2✓ Branch 0 taken 746 times.
✓ Branch 1 taken 794 times.
|
1540 | delete opt_proxy_groups_; |
2834 |
2/6✗ Branch 1 not taken.
✓ Branch 2 taken 1540 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1540 times.
|
1540 | if ((set_proxy_list == "") && (set_proxy_fallback_list == "")) { |
2835 | ✗ | opt_proxy_groups_ = NULL; | |
2836 | ✗ | opt_proxy_groups_current_ = 0; | |
2837 | ✗ | opt_proxy_groups_current_burned_ = 0; | |
2838 | ✗ | opt_proxy_groups_fallback_ = 0; | |
2839 | ✗ | opt_num_proxies_ = 0; | |
2840 | ✗ | return; | |
2841 | } | ||
2842 | |||
2843 | // Determine number of regular proxy groups (== first fallback proxy group) | ||
2844 | 1540 | opt_proxy_groups_fallback_ = 0; | |
2845 |
1/2✓ Branch 1 taken 1540 times.
✗ Branch 2 not taken.
|
1540 | if (set_proxy_list != "") { |
2846 |
1/2✓ Branch 1 taken 1540 times.
✗ Branch 2 not taken.
|
1540 | opt_proxy_groups_fallback_ = SplitString(set_proxy_list, ';').size(); |
2847 | } | ||
2848 |
1/2✓ Branch 2 taken 1540 times.
✗ Branch 3 not taken.
|
1540 | LogCvmfs(kLogDownload, kLogDebug, |
2849 | "(manager '%s') " | ||
2850 | "first fallback proxy group %u", | ||
2851 | name_.c_str(), opt_proxy_groups_fallback_); | ||
2852 | |||
2853 | // Concatenate regular proxies and fallback proxies, both of which can be | ||
2854 | // empty. | ||
2855 |
1/2✓ Branch 1 taken 1540 times.
✗ Branch 2 not taken.
|
1540 | string all_proxy_list = set_proxy_list; |
2856 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1540 times.
|
1540 | if (set_proxy_fallback_list != "") { |
2857 | ✗ | if (all_proxy_list != "") | |
2858 | ✗ | all_proxy_list += ";"; | |
2859 | ✗ | all_proxy_list += set_proxy_fallback_list; | |
2860 | } | ||
2861 |
1/2✓ Branch 3 taken 1540 times.
✗ Branch 4 not taken.
|
1540 | LogCvmfs(kLogDownload, kLogDebug, "(manager '%s') full proxy list %s", |
2862 | name_.c_str(), all_proxy_list.c_str()); | ||
2863 | |||
2864 | // Resolve server names in provided urls | ||
2865 | 1540 | vector<string> hostnames; // All encountered hostnames | |
2866 | 1540 | vector<string> proxy_groups; | |
2867 |
1/2✓ Branch 1 taken 1540 times.
✗ Branch 2 not taken.
|
1540 | if (all_proxy_list != "") |
2868 |
1/2✓ Branch 1 taken 1540 times.
✗ Branch 2 not taken.
|
1540 | proxy_groups = SplitString(all_proxy_list, ';'); |
2869 |
2/2✓ Branch 1 taken 1556 times.
✓ Branch 2 taken 1540 times.
|
3096 | for (unsigned i = 0; i < proxy_groups.size(); ++i) { |
2870 |
1/2✓ Branch 2 taken 1556 times.
✗ Branch 3 not taken.
|
1556 | vector<string> this_group = SplitString(proxy_groups[i], '|'); |
2871 |
2/2✓ Branch 1 taken 1556 times.
✓ Branch 2 taken 1556 times.
|
3112 | for (unsigned j = 0; j < this_group.size(); ++j) { |
2872 |
1/2✓ Branch 2 taken 1556 times.
✗ Branch 3 not taken.
|
1556 | this_group[j] = dns::AddDefaultScheme(this_group[j]); |
2873 | // Note: DIRECT strings will be "extracted" to an empty string. | ||
2874 |
1/2✓ Branch 2 taken 1556 times.
✗ Branch 3 not taken.
|
1556 | const string hostname = dns::ExtractHost(this_group[j]); |
2875 | // Save the hostname. Leave empty (DIRECT) names so indexes will | ||
2876 | // match later. | ||
2877 |
1/2✓ Branch 1 taken 1556 times.
✗ Branch 2 not taken.
|
1556 | hostnames.push_back(hostname); |
2878 | 1556 | } | |
2879 | 1556 | } | |
2880 | 1540 | vector<dns::Host> hosts; | |
2881 |
1/2✓ Branch 3 taken 1540 times.
✗ Branch 4 not taken.
|
1540 | LogCvmfs(kLogDownload, kLogDebug, |
2882 | "(manager '%s') " | ||
2883 | "resolving %lu proxy addresses", | ||
2884 | name_.c_str(), hostnames.size()); | ||
2885 |
1/2✓ Branch 1 taken 1540 times.
✗ Branch 2 not taken.
|
1540 | resolver_->ResolveMany(hostnames, &hosts); |
2886 | |||
2887 | // Construct opt_proxy_groups_: traverse proxy list in same order and expand | ||
2888 | // names to resolved IP addresses. | ||
2889 |
1/2✓ Branch 1 taken 1540 times.
✗ Branch 2 not taken.
|
1540 | opt_proxy_groups_ = new vector<vector<ProxyInfo> >(); |
2890 | 1540 | opt_num_proxies_ = 0; | |
2891 | 1540 | unsigned num_proxy = 0; // Combined i, j counter | |
2892 |
2/2✓ Branch 1 taken 1556 times.
✓ Branch 2 taken 1540 times.
|
3096 | for (unsigned i = 0; i < proxy_groups.size(); ++i) { |
2893 |
1/2✓ Branch 2 taken 1556 times.
✗ Branch 3 not taken.
|
1556 | vector<string> this_group = SplitString(proxy_groups[i], '|'); |
2894 | // Construct ProxyInfo objects from proxy string and DNS resolver result for | ||
2895 | // every proxy in this_group. One URL can result in multiple ProxyInfo | ||
2896 | // objects, one for each IP address. | ||
2897 | 1556 | vector<ProxyInfo> infos; | |
2898 |
2/2✓ Branch 1 taken 1556 times.
✓ Branch 2 taken 1556 times.
|
3112 | for (unsigned j = 0; j < this_group.size(); ++j, ++num_proxy) { |
2899 |
1/2✓ Branch 2 taken 1556 times.
✗ Branch 3 not taken.
|
1556 | this_group[j] = dns::AddDefaultScheme(this_group[j]); |
2900 |
2/2✓ Branch 2 taken 1492 times.
✓ Branch 3 taken 64 times.
|
1556 | if (this_group[j] == "DIRECT") { |
2901 |
3/6✓ Branch 2 taken 1492 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1492 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1492 times.
✗ Branch 9 not taken.
|
1492 | infos.push_back(ProxyInfo("DIRECT")); |
2902 | 1492 | continue; | |
2903 | } | ||
2904 | |||
2905 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 64 times.
|
64 | if (hosts[num_proxy].status() != dns::kFailOk) { |
2906 | ✗ | LogCvmfs(kLogDownload, kLogDebug | kLogSyslogWarn, | |
2907 | "(manager '%s') " | ||
2908 | "failed to resolve IP addresses for %s (%d - %s)", | ||
2909 | ✗ | name_.c_str(), hosts[num_proxy].name().c_str(), | |
2910 | ✗ | hosts[num_proxy].status(), | |
2911 | ✗ | dns::Code2Ascii(hosts[num_proxy].status())); | |
2912 | const dns::Host failed_host = dns::Host::ExtendDeadline( | ||
2913 | ✗ | hosts[num_proxy], resolver_->min_ttl()); | |
2914 | ✗ | infos.push_back(ProxyInfo(failed_host, this_group[j])); | |
2915 | ✗ | continue; | |
2916 | } | ||
2917 | |||
2918 | // IPv4 addresses have precedence | ||
2919 | 64 | set<string> best_addresses = hosts[num_proxy].ViewBestAddresses( | |
2920 |
2/4✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 64 times.
✗ Branch 5 not taken.
|
64 | opt_ip_preference_); |
2921 | 64 | set<string>::const_iterator iter_ips = best_addresses.begin(); | |
2922 |
2/2✓ Branch 3 taken 64 times.
✓ Branch 4 taken 64 times.
|
128 | for (; iter_ips != best_addresses.end(); ++iter_ips) { |
2923 |
1/2✓ Branch 3 taken 64 times.
✗ Branch 4 not taken.
|
64 | const string url_ip = dns::RewriteUrl(this_group[j], *iter_ips); |
2924 |
2/4✓ Branch 2 taken 64 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 64 times.
✗ Branch 6 not taken.
|
64 | infos.push_back(ProxyInfo(hosts[num_proxy], url_ip)); |
2925 | |||
2926 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 64 times.
|
64 | if (sharding_policy_.UseCount() > 0) { |
2927 | ✗ | sharding_policy_->AddProxy(url_ip); | |
2928 | } | ||
2929 | 64 | } | |
2930 | 64 | } | |
2931 |
1/2✓ Branch 1 taken 1556 times.
✗ Branch 2 not taken.
|
1556 | opt_proxy_groups_->push_back(infos); |
2932 | 1556 | opt_num_proxies_ += infos.size(); | |
2933 | 1556 | } | |
2934 |
1/2✓ Branch 2 taken 1540 times.
✗ Branch 3 not taken.
|
1540 | LogCvmfs(kLogDownload, kLogDebug, |
2935 | "(manager '%s') installed %u proxies in %lu load-balance groups", | ||
2936 | 1540 | name_.c_str(), opt_num_proxies_, opt_proxy_groups_->size()); | |
2937 | 1540 | opt_proxy_groups_current_ = 0; | |
2938 | 1540 | opt_proxy_groups_current_burned_ = 0; | |
2939 | |||
2940 | // Select random start proxy from the first group. | ||
2941 |
1/2✓ Branch 1 taken 1540 times.
✗ Branch 2 not taken.
|
1540 | if (opt_proxy_groups_->size() > 0) { |
2942 | // Select random start proxy from the first group. | ||
2943 |
2/4✓ Branch 2 taken 1540 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1540 times.
✗ Branch 6 not taken.
|
1540 | UpdateProxiesUnlocked("set random start proxy from the first proxy group"); |
2944 | } | ||
2945 |
3/6✓ Branch 5 taken 1540 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1540 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 1540 times.
✗ Branch 12 not taken.
|
1540 | } |
2946 | |||
2947 | |||
2948 | /** | ||
2949 | * Retrieves the proxy chain, optionally the currently active load-balancing | ||
2950 | * group, and optionally the index of the first fallback proxy group. | ||
2951 | * If there are no fallback proxies, the index will equal the size of | ||
2952 | * the proxy chain. | ||
2953 | */ | ||
2954 | ✗ | void DownloadManager::GetProxyInfo(vector<vector<ProxyInfo> > *proxy_chain, | |
2955 | unsigned *current_group, | ||
2956 | unsigned *fallback_group) { | ||
2957 | ✗ | assert(proxy_chain != NULL); | |
2958 | ✗ | const MutexLockGuard m(lock_options_); | |
2959 | |||
2960 | ✗ | if (!opt_proxy_groups_) { | |
2961 | ✗ | const vector<vector<ProxyInfo> > empty_chain; | |
2962 | ✗ | *proxy_chain = empty_chain; | |
2963 | ✗ | if (current_group != NULL) | |
2964 | ✗ | *current_group = 0; | |
2965 | ✗ | if (fallback_group != NULL) | |
2966 | ✗ | *fallback_group = 0; | |
2967 | ✗ | return; | |
2968 | } | ||
2969 | |||
2970 | ✗ | *proxy_chain = *opt_proxy_groups_; | |
2971 | ✗ | if (current_group != NULL) | |
2972 | ✗ | *current_group = opt_proxy_groups_current_; | |
2973 | ✗ | if (fallback_group != NULL) | |
2974 | ✗ | *fallback_group = opt_proxy_groups_fallback_; | |
2975 | } | ||
2976 | |||
2977 | ✗ | string DownloadManager::GetProxyList() { return opt_proxy_list_; } | |
2978 | |||
2979 | ✗ | string DownloadManager::GetFallbackProxyList() { | |
2980 | ✗ | return opt_proxy_fallback_list_; | |
2981 | } | ||
2982 | |||
2983 | /** | ||
2984 | * Choose proxy | ||
2985 | */ | ||
2986 | 3229 | DownloadManager::ProxyInfo *DownloadManager::ChooseProxyUnlocked( | |
2987 | const shash::Any *hash) { | ||
2988 |
2/2✓ Branch 0 taken 1794 times.
✓ Branch 1 taken 1435 times.
|
3229 | if (!opt_proxy_groups_) |
2989 | 1794 | return NULL; | |
2990 | |||
2991 |
2/2✓ Branch 0 taken 812 times.
✓ Branch 1 taken 623 times.
|
1435 | const uint32_t key = (hash ? hash->Partial32() : 0); |
2992 |
1/2✓ Branch 1 taken 1435 times.
✗ Branch 2 not taken.
|
1435 | const map<uint32_t, ProxyInfo *>::iterator it = opt_proxy_map_.lower_bound( |
2993 | key); | ||
2994 | 1435 | ProxyInfo *proxy = it->second; | |
2995 | |||
2996 | 1435 | return proxy; | |
2997 | } | ||
2998 | |||
2999 | /** | ||
3000 | * Update currently selected proxy | ||
3001 | */ | ||
3002 | 2302 | void DownloadManager::UpdateProxiesUnlocked(const string &reason) { | |
3003 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2302 times.
|
2302 | if (!opt_proxy_groups_) |
3004 | ✗ | return; | |
3005 | |||
3006 | // Identify number of non-burned proxies within the current group | ||
3007 | 2302 | vector<ProxyInfo> *group = current_proxy_group(); | |
3008 | 2302 | const unsigned num_alive = (group->size() - opt_proxy_groups_current_burned_); | |
3009 |
2/4✓ Branch 2 taken 2302 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2302 times.
✗ Branch 6 not taken.
|
4604 | const string old_proxy = JoinStrings(opt_proxies_, "|"); |
3010 | |||
3011 | // Rebuild proxy map and URL list | ||
3012 | 2302 | opt_proxy_map_.clear(); | |
3013 | 2302 | opt_proxies_.clear(); | |
3014 | 2302 | const uint32_t max_key = 0xffffffffUL; | |
3015 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2302 times.
|
2302 | if (opt_proxy_shard_) { |
3016 | // Build a consistent map with multiple entries for each proxy | ||
3017 | ✗ | for (unsigned i = 0; i < num_alive; ++i) { | |
3018 | ✗ | ProxyInfo *proxy = &(*group)[i]; | |
3019 | ✗ | shash::Any proxy_hash(shash::kSha1); | |
3020 | ✗ | HashString(proxy->url, &proxy_hash); | |
3021 | ✗ | Prng prng; | |
3022 | ✗ | prng.InitSeed(proxy_hash.Partial32()); | |
3023 | ✗ | for (unsigned j = 0; j < kProxyMapScale; ++j) { | |
3024 | ✗ | const std::pair<uint32_t, ProxyInfo *> entry(prng.Next(max_key), proxy); | |
3025 | ✗ | opt_proxy_map_.insert(entry); | |
3026 | } | ||
3027 | ✗ | const std::string proxy_name = proxy->host.name().empty() | |
3028 | ? "" | ||
3029 | ✗ | : " (" + proxy->host.name() + ")"; | |
3030 | ✗ | opt_proxies_.push_back(proxy->url + proxy_name); | |
3031 | } | ||
3032 | // Ensure lower_bound() finds a value for all keys | ||
3033 | ✗ | ProxyInfo *first_proxy = opt_proxy_map_.begin()->second; | |
3034 | ✗ | const std::pair<uint32_t, ProxyInfo *> last_entry(max_key, first_proxy); | |
3035 | ✗ | opt_proxy_map_.insert(last_entry); | |
3036 | } else { | ||
3037 | // Build a map with a single entry for one randomly selected proxy | ||
3038 | 2302 | const unsigned select = prng_.Next(num_alive); | |
3039 | 2302 | ProxyInfo *proxy = &(*group)[select]; | |
3040 | 2302 | const std::pair<uint32_t, ProxyInfo *> entry(max_key, proxy); | |
3041 |
1/2✓ Branch 1 taken 2302 times.
✗ Branch 2 not taken.
|
2302 | opt_proxy_map_.insert(entry); |
3042 | 2302 | const std::string proxy_name = proxy->host.name().empty() | |
3043 | ? "" | ||
3044 |
9/18✓ Branch 0 taken 2238 times.
✓ Branch 1 taken 64 times.
✓ Branch 4 taken 2238 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 64 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 64 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 64 times.
✓ Branch 14 taken 2238 times.
✓ Branch 15 taken 2238 times.
✓ Branch 16 taken 64 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
|
2366 | : " (" + proxy->host.name() + ")"; |
3045 |
2/4✓ Branch 1 taken 2302 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2302 times.
✗ Branch 5 not taken.
|
2302 | opt_proxies_.push_back(proxy->url + proxy_name); |
3046 | 2302 | } | |
3047 |
1/2✓ Branch 3 taken 2302 times.
✗ Branch 4 not taken.
|
2302 | sort(opt_proxies_.begin(), opt_proxies_.end()); |
3048 | |||
3049 | // Report any change in proxy usage | ||
3050 |
2/4✓ Branch 2 taken 2302 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2302 times.
✗ Branch 6 not taken.
|
4604 | const string new_proxy = JoinStrings(opt_proxies_, "|"); |
3051 | const string curr_host = "Current host: " | ||
3052 | 2302 | + (opt_host_.chain | |
3053 |
6/12✓ Branch 0 taken 2238 times.
✓ Branch 1 taken 64 times.
✓ Branch 4 taken 2238 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 64 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 64 times.
✓ Branch 11 taken 2238 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
4604 | ? (*opt_host_.chain)[opt_host_.current] |
3054 |
1/2✓ Branch 1 taken 2302 times.
✗ Branch 2 not taken.
|
2302 | : ""); |
3055 |
2/2✓ Branch 1 taken 1556 times.
✓ Branch 2 taken 746 times.
|
2302 | if (new_proxy != old_proxy) { |
3056 |
4/9✗ Branch 2 not taken.
✓ Branch 3 taken 1556 times.
✓ Branch 4 taken 1540 times.
✓ Branch 5 taken 16 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1556 times.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
4684 | LogCvmfs(kLogDownload, kLogDebug | kLogSyslogWarn, |
3057 | "(manager '%s') switching proxy from %s to %s. Reason: %s [%s]", | ||
3058 | 1572 | name_.c_str(), (old_proxy.empty() ? "(none)" : old_proxy.c_str()), | |
3059 | 3112 | (new_proxy.empty() ? "(none)" : new_proxy.c_str()), reason.c_str(), | |
3060 | curr_host.c_str()); | ||
3061 | } | ||
3062 | 2302 | } | |
3063 | |||
3064 | /** | ||
3065 | * Enable proxy sharding | ||
3066 | */ | ||
3067 | ✗ | void DownloadManager::ShardProxies() { | |
3068 | ✗ | opt_proxy_shard_ = true; | |
3069 | ✗ | RebalanceProxiesUnlocked("enable sharding"); | |
3070 | } | ||
3071 | |||
3072 | /** | ||
3073 | * Selects a new random proxy in the current load-balancing group. Resets the | ||
3074 | * "burned" counter. | ||
3075 | */ | ||
3076 | ✗ | void DownloadManager::RebalanceProxiesUnlocked(const string &reason) { | |
3077 | ✗ | if (!opt_proxy_groups_) | |
3078 | ✗ | return; | |
3079 | |||
3080 | ✗ | opt_timestamp_failover_proxies_ = 0; | |
3081 | ✗ | opt_proxy_groups_current_burned_ = 0; | |
3082 | ✗ | UpdateProxiesUnlocked(reason); | |
3083 | } | ||
3084 | |||
3085 | |||
3086 | ✗ | void DownloadManager::RebalanceProxies() { | |
3087 | ✗ | const MutexLockGuard m(lock_options_); | |
3088 | ✗ | RebalanceProxiesUnlocked("rebalance invoked manually"); | |
3089 | } | ||
3090 | |||
3091 | |||
3092 | /** | ||
3093 | * Switches to the next load-balancing group of proxy servers. | ||
3094 | */ | ||
3095 | ✗ | void DownloadManager::SwitchProxyGroup() { | |
3096 | ✗ | const MutexLockGuard m(lock_options_); | |
3097 | |||
3098 | ✗ | if (!opt_proxy_groups_ || (opt_proxy_groups_->size() < 2)) { | |
3099 | ✗ | return; | |
3100 | } | ||
3101 | |||
3102 | ✗ | opt_proxy_groups_current_ = (opt_proxy_groups_current_ + 1) | |
3103 | ✗ | % opt_proxy_groups_->size(); | |
3104 | ✗ | opt_timestamp_backup_proxies_ = time(NULL); | |
3105 | |||
3106 | const std::string msg = "switch to proxy group " | ||
3107 | ✗ | + StringifyUint(opt_proxy_groups_current_); | |
3108 | ✗ | RebalanceProxiesUnlocked(msg); | |
3109 | } | ||
3110 | |||
3111 | |||
3112 | ✗ | void DownloadManager::SetProxyGroupResetDelay(const unsigned seconds) { | |
3113 | ✗ | const MutexLockGuard m(lock_options_); | |
3114 | ✗ | opt_proxy_groups_reset_after_ = seconds; | |
3115 | ✗ | if (opt_proxy_groups_reset_after_ == 0) { | |
3116 | ✗ | opt_timestamp_backup_proxies_ = 0; | |
3117 | ✗ | opt_timestamp_failover_proxies_ = 0; | |
3118 | } | ||
3119 | } | ||
3120 | |||
3121 | |||
3122 | ✗ | void DownloadManager::SetMetalinkResetDelay(const unsigned seconds) { | |
3123 | ✗ | const MutexLockGuard m(lock_options_); | |
3124 | ✗ | opt_metalink_.reset_after = seconds; | |
3125 | ✗ | if (opt_metalink_.reset_after == 0) | |
3126 | ✗ | opt_metalink_.timestamp_backup = 0; | |
3127 | } | ||
3128 | |||
3129 | |||
3130 | ✗ | void DownloadManager::SetHostResetDelay(const unsigned seconds) { | |
3131 | ✗ | const MutexLockGuard m(lock_options_); | |
3132 | ✗ | opt_host_.reset_after = seconds; | |
3133 | ✗ | if (opt_host_.reset_after == 0) | |
3134 | ✗ | opt_host_.timestamp_backup = 0; | |
3135 | } | ||
3136 | |||
3137 | |||
3138 | 1475 | void DownloadManager::SetRetryParameters(const unsigned max_retries, | |
3139 | const unsigned backoff_init_ms, | ||
3140 | const unsigned backoff_max_ms) { | ||
3141 | 1475 | const MutexLockGuard m(lock_options_); | |
3142 | 1475 | opt_max_retries_ = max_retries; | |
3143 | 1475 | opt_backoff_init_ms_ = backoff_init_ms; | |
3144 | 1475 | opt_backoff_max_ms_ = backoff_max_ms; | |
3145 | 1475 | } | |
3146 | |||
3147 | |||
3148 | 778 | void DownloadManager::SetMaxIpaddrPerProxy(unsigned limit) { | |
3149 | 778 | const MutexLockGuard m(lock_options_); | |
3150 | 778 | resolver_->set_throttle(limit); | |
3151 | 778 | } | |
3152 | |||
3153 | |||
3154 | 1001 | void DownloadManager::SetProxyTemplates(const std::string &direct, | |
3155 | const std::string &forced) { | ||
3156 | 1001 | const MutexLockGuard m(lock_options_); | |
3157 |
1/2✓ Branch 1 taken 1001 times.
✗ Branch 2 not taken.
|
1001 | proxy_template_direct_ = direct; |
3158 |
1/2✓ Branch 1 taken 1001 times.
✗ Branch 2 not taken.
|
1001 | proxy_template_forced_ = forced; |
3159 | 1001 | } | |
3160 | |||
3161 | |||
3162 | ✗ | void DownloadManager::EnableInfoHeader() { enable_info_header_ = true; } | |
3163 | |||
3164 | |||
3165 | 522 | void DownloadManager::EnableRedirects() { follow_redirects_ = true; } | |
3166 | |||
3167 | ✗ | void DownloadManager::EnableIgnoreSignatureFailures() { | |
3168 | ✗ | ignore_signature_failures_ = true; | |
3169 | } | ||
3170 | |||
3171 | ✗ | void DownloadManager::EnableHTTPTracing() { enable_http_tracing_ = true; } | |
3172 | |||
3173 | ✗ | void DownloadManager::AddHTTPTracingHeader(const std::string &header) { | |
3174 | ✗ | http_tracing_headers_.push_back(header); | |
3175 | } | ||
3176 | |||
3177 | 474 | void DownloadManager::UseSystemCertificatePath() { | |
3178 | 474 | ssl_certificate_store_.UseSystemCertificatePath(); | |
3179 | 474 | } | |
3180 | |||
3181 | ✗ | bool DownloadManager::SetShardingPolicy(const ShardingPolicySelector type) { | |
3182 | ✗ | const bool success = false; | |
3183 | ✗ | switch (type) { | |
3184 | default: | ||
3185 | ✗ | LogCvmfs( | |
3186 | kLogDownload, kLogDebug | kLogSyslogErr, | ||
3187 | "(manager '%s') " | ||
3188 | "Proposed sharding policy does not exist. Falling back to default", | ||
3189 | name_.c_str()); | ||
3190 | } | ||
3191 | ✗ | return success; | |
3192 | } | ||
3193 | |||
3194 | ✗ | void DownloadManager::SetFailoverIndefinitely() { | |
3195 | ✗ | failover_indefinitely_ = true; | |
3196 | } | ||
3197 | |||
3198 | /** | ||
3199 | * Creates a copy of the existing download manager. Must only be called in | ||
3200 | * single-threaded stage because it calls curl_global_init(). | ||
3201 | */ | ||
3202 | 778 | DownloadManager *DownloadManager::Clone( | |
3203 | const perf::StatisticsTemplate &statistics, | ||
3204 | const std::string &cloned_name) { | ||
3205 | DownloadManager *clone = new DownloadManager(pool_max_handles_, statistics, | ||
3206 |
1/2✓ Branch 2 taken 778 times.
✗ Branch 3 not taken.
|
778 | cloned_name); |
3207 | |||
3208 | 778 | clone->SetDnsParameters(resolver_->retries(), resolver_->timeout_ms()); | |
3209 | 778 | clone->SetDnsTtlLimits(resolver_->min_ttl(), resolver_->max_ttl()); | |
3210 | 778 | clone->SetMaxIpaddrPerProxy(resolver_->throttle()); | |
3211 | |||
3212 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 778 times.
|
778 | if (!opt_dns_server_.empty()) |
3213 | ✗ | clone->SetDnsServer(opt_dns_server_); | |
3214 | 778 | clone->opt_timeout_proxy_ = opt_timeout_proxy_; | |
3215 | 778 | clone->opt_timeout_direct_ = opt_timeout_direct_; | |
3216 | 778 | clone->opt_low_speed_limit_ = opt_low_speed_limit_; | |
3217 | 778 | clone->opt_max_retries_ = opt_max_retries_; | |
3218 | 778 | clone->opt_backoff_init_ms_ = opt_backoff_init_ms_; | |
3219 | 778 | clone->opt_backoff_max_ms_ = opt_backoff_max_ms_; | |
3220 | 778 | clone->enable_info_header_ = enable_info_header_; | |
3221 | 778 | clone->enable_http_tracing_ = enable_http_tracing_; | |
3222 | 778 | clone->http_tracing_headers_ = http_tracing_headers_; | |
3223 | 778 | clone->follow_redirects_ = follow_redirects_; | |
3224 | 778 | clone->ignore_signature_failures_ = ignore_signature_failures_; | |
3225 |
2/2✓ Branch 0 taken 746 times.
✓ Branch 1 taken 32 times.
|
778 | if (opt_host_.chain) { |
3226 |
1/2✓ Branch 2 taken 746 times.
✗ Branch 3 not taken.
|
746 | clone->opt_host_.chain = new vector<string>(*opt_host_.chain); |
3227 |
1/2✓ Branch 2 taken 746 times.
✗ Branch 3 not taken.
|
746 | clone->opt_host_chain_rtt_ = new vector<int>(*opt_host_chain_rtt_); |
3228 | } | ||
3229 | |||
3230 | 778 | CloneProxyConfig(clone); | |
3231 | 778 | clone->opt_ip_preference_ = opt_ip_preference_; | |
3232 | 778 | clone->proxy_template_direct_ = proxy_template_direct_; | |
3233 | 778 | clone->proxy_template_forced_ = proxy_template_forced_; | |
3234 | 778 | clone->opt_proxy_groups_reset_after_ = opt_proxy_groups_reset_after_; | |
3235 | 778 | clone->opt_metalink_.reset_after = opt_metalink_.reset_after; | |
3236 | 778 | clone->opt_host_.reset_after = opt_host_.reset_after; | |
3237 | 778 | clone->credentials_attachment_ = credentials_attachment_; | |
3238 | 778 | clone->ssl_certificate_store_ = ssl_certificate_store_; | |
3239 | |||
3240 | 778 | clone->health_check_ = health_check_; | |
3241 | 778 | clone->sharding_policy_ = sharding_policy_; | |
3242 | 778 | clone->failover_indefinitely_ = failover_indefinitely_; | |
3243 | 778 | clone->fqrn_ = fqrn_; | |
3244 | |||
3245 | 778 | return clone; | |
3246 | } | ||
3247 | |||
3248 | |||
3249 | 778 | void DownloadManager::CloneProxyConfig(DownloadManager *clone) { | |
3250 | 778 | clone->opt_proxy_groups_current_ = opt_proxy_groups_current_; | |
3251 | 778 | clone->opt_proxy_groups_current_burned_ = opt_proxy_groups_current_burned_; | |
3252 | 778 | clone->opt_proxy_groups_fallback_ = opt_proxy_groups_fallback_; | |
3253 | 778 | clone->opt_num_proxies_ = opt_num_proxies_; | |
3254 | 778 | clone->opt_proxy_shard_ = opt_proxy_shard_; | |
3255 | 778 | clone->opt_proxy_list_ = opt_proxy_list_; | |
3256 | 778 | clone->opt_proxy_fallback_list_ = opt_proxy_fallback_list_; | |
3257 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 746 times.
|
778 | if (opt_proxy_groups_ == NULL) |
3258 | 32 | return; | |
3259 | |||
3260 |
1/2✓ Branch 2 taken 746 times.
✗ Branch 3 not taken.
|
746 | clone->opt_proxy_groups_ = new vector<vector<ProxyInfo> >(*opt_proxy_groups_); |
3261 |
2/4✓ Branch 2 taken 746 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 746 times.
✗ Branch 6 not taken.
|
746 | clone->UpdateProxiesUnlocked("cloned"); |
3262 | } | ||
3263 | |||
3264 | } // namespace download | ||
3265 |