Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: CPL - Common Portability Library
4 : * Purpose: Implement VSI large file api for OpenStack Swift
5 : * Author: Even Rouault, even.rouault at spatialys.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2017-2018, Even Rouault <even.rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_json.h"
14 : #include "cpl_port.h"
15 : #include "cpl_http.h"
16 : #include "cpl_time.h"
17 : #include "cpl_vsil_curl_priv.h"
18 : #include "cpl_vsil_curl_class.h"
19 :
20 : #include <errno.h>
21 :
22 : #include <algorithm>
23 : #include <set>
24 : #include <map>
25 : #include <memory>
26 :
27 : #include "cpl_swift.h"
28 :
29 : #ifndef HAVE_CURL
30 :
31 : void VSIInstallSwiftFileHandler(void)
32 : {
33 : // Not supported
34 : }
35 :
36 : #else
37 :
38 : //! @cond Doxygen_Suppress
39 : #ifndef DOXYGEN_SKIP
40 :
41 : #define ENABLE_DEBUG 0
42 :
43 : #define unchecked_curl_easy_setopt(handle, opt, param) \
44 : CPL_IGNORE_RET_VAL(curl_easy_setopt(handle, opt, param))
45 :
46 : namespace cpl
47 : {
48 :
49 : /************************************************************************/
50 : /* AnalyseSwiftFileList() */
51 : /************************************************************************/
52 :
53 13 : void VSICurlFilesystemHandlerBase::AnalyseSwiftFileList(
54 : const std::string &osBaseURL, const std::string &osPrefix,
55 : const char *pszJson, CPLStringList &osFileList, int nMaxFilesThisQuery,
56 : int nMaxFiles, bool &bIsTruncated, std::string &osNextMarker)
57 : {
58 : #if DEBUG_VERBOSE
59 : CPLDebug("SWIFT", "%s", pszJson);
60 : #endif
61 13 : osNextMarker = "";
62 13 : bIsTruncated = false;
63 :
64 13 : CPLJSONDocument oDoc;
65 13 : if (!oDoc.LoadMemory(reinterpret_cast<const GByte *>(pszJson)))
66 0 : return;
67 :
68 26 : std::vector<std::pair<std::string, FileProp>> aoProps;
69 : // Count the number of occurrences of a path. Can be 1 or 2. 2 in the case
70 : // that both a filename and directory exist
71 26 : std::map<std::string, int> aoNameCount;
72 :
73 26 : CPLJSONArray oArray = oDoc.GetRoot().ToArray();
74 23 : for (int i = 0; i < oArray.Size(); i++)
75 : {
76 10 : CPLJSONObject oItem = oArray[i];
77 20 : std::string osName = oItem.GetString("name");
78 10 : GInt64 nSize = oItem.GetLong("bytes");
79 20 : std::string osLastModified = oItem.GetString("last_modified");
80 20 : std::string osSubdir = oItem.GetString("subdir");
81 10 : bool bHasCount = oItem.GetLong("count", -1) >= 0;
82 10 : if (!osName.empty())
83 : {
84 6 : osNextMarker = osName;
85 12 : if (osName.size() > osPrefix.size() &&
86 12 : osName.substr(0, osPrefix.size()) == osPrefix)
87 : {
88 6 : if (bHasCount)
89 : {
90 : // Case when listing /vsiswift/
91 2 : FileProp prop;
92 2 : prop.eExists = EXIST_YES;
93 2 : prop.bIsDirectory = true;
94 2 : prop.bHasComputedFileSize = true;
95 2 : prop.fileSize = 0;
96 2 : prop.mTime = 0;
97 :
98 2 : aoProps.push_back(
99 4 : std::pair<std::string, FileProp>(osName, prop));
100 2 : aoNameCount[osName]++;
101 : }
102 : else
103 : {
104 4 : FileProp prop;
105 4 : prop.eExists = EXIST_YES;
106 4 : prop.bHasComputedFileSize = true;
107 4 : prop.fileSize = static_cast<GUIntBig>(nSize);
108 4 : prop.bIsDirectory = false;
109 4 : prop.mTime = 0;
110 : int nYear, nMonth, nDay, nHour, nMin, nSec;
111 4 : if (sscanf(osLastModified.c_str(),
112 : "%04d-%02d-%02dT%02d:%02d:%02d", &nYear, &nMonth,
113 4 : &nDay, &nHour, &nMin, &nSec) == 6)
114 : {
115 : struct tm brokendowntime;
116 4 : brokendowntime.tm_year = nYear - 1900;
117 4 : brokendowntime.tm_mon = nMonth - 1;
118 4 : brokendowntime.tm_mday = nDay;
119 4 : brokendowntime.tm_hour = nHour;
120 4 : brokendowntime.tm_min = nMin;
121 4 : brokendowntime.tm_sec = nSec;
122 4 : prop.mTime = static_cast<time_t>(
123 4 : CPLYMDHMSToUnixTime(&brokendowntime));
124 : }
125 :
126 4 : aoProps.push_back(std::pair<std::string, FileProp>(
127 8 : osName.substr(osPrefix.size()), prop));
128 4 : aoNameCount[osName.substr(osPrefix.size())]++;
129 : }
130 : }
131 : }
132 4 : else if (!osSubdir.empty())
133 : {
134 4 : osNextMarker = osSubdir;
135 4 : if (osSubdir.back() == '/')
136 4 : osSubdir.pop_back();
137 4 : if (STARTS_WITH(osSubdir.c_str(), osPrefix.c_str()))
138 : {
139 :
140 4 : FileProp prop;
141 4 : prop.eExists = EXIST_YES;
142 4 : prop.bIsDirectory = true;
143 4 : prop.bHasComputedFileSize = true;
144 4 : prop.fileSize = 0;
145 4 : prop.mTime = 0;
146 :
147 4 : aoProps.push_back(std::pair<std::string, FileProp>(
148 8 : osSubdir.substr(osPrefix.size()), prop));
149 4 : aoNameCount[osSubdir.substr(osPrefix.size())]++;
150 : }
151 : }
152 :
153 10 : if (nMaxFiles > 0 && aoProps.size() > static_cast<unsigned>(nMaxFiles))
154 0 : break;
155 : }
156 :
157 13 : bIsTruncated = aoProps.size() >= static_cast<unsigned>(nMaxFilesThisQuery);
158 13 : if (!bIsTruncated)
159 : {
160 11 : osNextMarker.clear();
161 : }
162 :
163 23 : for (size_t i = 0; i < aoProps.size(); i++)
164 : {
165 10 : std::string osSuffix;
166 12 : if (aoNameCount[aoProps[i].first] == 2 &&
167 2 : aoProps[i].second.bIsDirectory)
168 : {
169 : // Add a / suffix to disambiguish the situation
170 : // Normally we don't suffix directories with /, but we have
171 : // no alternative here
172 1 : osSuffix = "/";
173 : }
174 10 : if (nMaxFiles != 1)
175 : {
176 : std::string osCachedFilename =
177 20 : osBaseURL + "/" + CPLAWSURLEncode(osPrefix, false) +
178 30 : CPLAWSURLEncode(aoProps[i].first, false) + osSuffix;
179 : #if DEBUG_VERBOSE
180 : CPLDebug("SWIFT", "Cache %s", osCachedFilename.c_str());
181 : #endif
182 10 : SetCachedFileProp(osCachedFilename.c_str(), aoProps[i].second);
183 : }
184 10 : osFileList.AddString((aoProps[i].first + osSuffix).c_str());
185 : }
186 : }
187 :
188 : /************************************************************************/
189 : /* VSISwiftFSHandler */
190 : /************************************************************************/
191 :
192 : class VSISwiftFSHandler final : public IVSIS3LikeFSHandler
193 : {
194 : const std::string m_osPrefix;
195 : CPL_DISALLOW_COPY_ASSIGN(VSISwiftFSHandler)
196 :
197 : protected:
198 : VSICurlHandle *CreateFileHandle(const char *pszFilename) override;
199 : std::string
200 : GetURLFromFilename(const std::string &osFilename) const override;
201 :
202 28 : const char *GetDebugKey() const override
203 : {
204 28 : return "SWIFT";
205 : }
206 :
207 : IVSIS3LikeHandleHelper *CreateHandleHelper(const char *pszURI,
208 : bool bAllowNoObject) override;
209 :
210 407 : std::string GetFSPrefix() const override
211 : {
212 407 : return m_osPrefix;
213 : }
214 :
215 : char **GetFileList(const char *pszFilename, int nMaxFiles,
216 : bool *pbGotFileList) override;
217 :
218 : void ClearCache() override;
219 :
220 : VSIVirtualHandleUniquePtr
221 : CreateWriteHandle(const char *pszFilename,
222 : CSLConstList papszOptions) override;
223 :
224 : public:
225 1304 : explicit VSISwiftFSHandler(const char *pszPrefix) : m_osPrefix(pszPrefix)
226 : {
227 1304 : }
228 :
229 : ~VSISwiftFSHandler() override;
230 :
231 : int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
232 : int nFlags) override;
233 :
234 0 : VSIDIR *OpenDir(const char *pszPath, int nRecurseDepth,
235 : const char *const *papszOptions) override
236 : {
237 0 : return VSICurlFilesystemHandlerBase::OpenDir(pszPath, nRecurseDepth,
238 0 : papszOptions);
239 : }
240 :
241 : const char *GetOptions() override;
242 :
243 : std::string
244 0 : GetStreamingFilename(const std::string &osFilename) const override
245 : {
246 0 : return osFilename;
247 : }
248 :
249 0 : VSIFilesystemHandler *Duplicate(const char *pszPrefix) override
250 : {
251 0 : return new VSISwiftFSHandler(pszPrefix);
252 : }
253 : };
254 :
255 : /************************************************************************/
256 : /* VSISwiftHandle */
257 : /************************************************************************/
258 :
259 : class VSISwiftHandle final : public IVSIS3LikeHandle
260 : {
261 : CPL_DISALLOW_COPY_ASSIGN(VSISwiftHandle)
262 :
263 : VSISwiftHandleHelper *m_poHandleHelper = nullptr;
264 :
265 : protected:
266 : struct curl_slist *
267 : GetCurlHeaders(const std::string &osVerb,
268 : const struct curl_slist *psExistingHeaders) override;
269 : virtual bool Authenticate(const char *pszFilename) override;
270 :
271 : public:
272 : VSISwiftHandle(VSISwiftFSHandler *poFS, const char *pszFilename,
273 : VSISwiftHandleHelper *poHandleHelper);
274 : ~VSISwiftHandle() override;
275 : };
276 :
277 : /************************************************************************/
278 : /* CreateWriteHandle() */
279 : /************************************************************************/
280 :
281 : VSIVirtualHandleUniquePtr
282 3 : VSISwiftFSHandler::CreateWriteHandle(const char *pszFilename,
283 : CSLConstList papszOptions)
284 : {
285 : auto poHandleHelper =
286 3 : CreateHandleHelper(pszFilename + GetFSPrefix().size(), false);
287 3 : if (poHandleHelper == nullptr)
288 0 : return nullptr;
289 : auto poHandle = std::make_unique<VSIChunkedWriteHandle>(
290 6 : this, pszFilename, poHandleHelper, papszOptions);
291 3 : return VSIVirtualHandleUniquePtr(poHandle.release());
292 : }
293 :
294 : /************************************************************************/
295 : /* ~VSISwiftFSHandler() */
296 : /************************************************************************/
297 :
298 1866 : VSISwiftFSHandler::~VSISwiftFSHandler()
299 : {
300 933 : VSISwiftFSHandler::ClearCache();
301 933 : VSISwiftHandleHelper::CleanMutex();
302 1866 : }
303 :
304 : /************************************************************************/
305 : /* ClearCache() */
306 : /************************************************************************/
307 :
308 1232 : void VSISwiftFSHandler::ClearCache()
309 : {
310 1232 : VSICurlFilesystemHandlerBase::ClearCache();
311 :
312 1232 : VSISwiftHandleHelper::ClearCache();
313 1232 : }
314 :
315 : /************************************************************************/
316 : /* GetOptions() */
317 : /************************************************************************/
318 :
319 2 : const char *VSISwiftFSHandler::GetOptions()
320 : {
321 : static std::string osOptions(
322 2 : std::string("<Options>") +
323 : " <Option name='SWIFT_STORAGE_URL' type='string' "
324 : "description='Storage URL. To use with SWIFT_AUTH_TOKEN'/>"
325 : " <Option name='SWIFT_AUTH_TOKEN' type='string' "
326 : "description='Authorization token'/>"
327 : " <Option name='SWIFT_AUTH_V1_URL' type='string' "
328 : "description='Authentication V1 URL. To use with SWIFT_USER and "
329 : "SWIFT_KEY'/>"
330 : " <Option name='SWIFT_USER' type='string' "
331 : "description='User name to use with authentication V1'/>"
332 : " <Option name='SWIFT_KEY' type='string' "
333 : "description='Key/password to use with authentication V1'/>"
334 : " <Option name='OS_IDENTITY_API_VERSION' type='string' "
335 : "description='OpenStack identity API version'/>"
336 : " <Option name='OS_AUTH_TYPE' type='string' "
337 : "description='Authentication URL'/>"
338 : " <Option name='OS_USERNAME' type='string' "
339 : "description='User name'/>"
340 : " <Option name='OS_PASSWORD' type='string' "
341 : "description='Password'/>"
342 : " <Option name='OS_USER_DOMAIN_NAME' type='string' "
343 : "description='User domain name'/>"
344 : " <Option name='OS_PROJECT_NAME' type='string' "
345 : "description='Project name'/>"
346 : " <Option name='OS_PROJECT_DOMAIN_NAME' type='string' "
347 : "description='Project domain name'/>"
348 : " <Option name='OS_REGION_NAME' type='string' "
349 3 : "description='Region name'/>" +
350 3 : VSICurlFilesystemHandlerBase::GetOptionsStatic() + "</Options>");
351 2 : return osOptions.c_str();
352 : }
353 :
354 : /************************************************************************/
355 : /* CreateFileHandle() */
356 : /************************************************************************/
357 :
358 21 : VSICurlHandle *VSISwiftFSHandler::CreateFileHandle(const char *pszFilename)
359 : {
360 42 : VSISwiftHandleHelper *poHandleHelper = VSISwiftHandleHelper::BuildFromURI(
361 63 : pszFilename + GetFSPrefix().size(), GetFSPrefix().c_str());
362 21 : if (poHandleHelper)
363 : {
364 19 : return new VSISwiftHandle(this, pszFilename, poHandleHelper);
365 : }
366 2 : return nullptr;
367 : }
368 :
369 : /************************************************************************/
370 : /* GetURLFromFilename() */
371 : /************************************************************************/
372 :
373 : std::string
374 21 : VSISwiftFSHandler::GetURLFromFilename(const std::string &osFilename) const
375 : {
376 : const std::string osFilenameWithoutPrefix =
377 42 : osFilename.substr(GetFSPrefix().size());
378 :
379 : auto poHandleHelper = std::unique_ptr<VSISwiftHandleHelper>(
380 : VSISwiftHandleHelper::BuildFromURI(osFilenameWithoutPrefix.c_str(),
381 42 : GetFSPrefix().c_str()));
382 21 : if (!poHandleHelper)
383 : {
384 0 : return std::string();
385 : }
386 42 : std::string osBaseURL(poHandleHelper->GetURL());
387 21 : if (!osBaseURL.empty() && osBaseURL.back() == '/')
388 2 : osBaseURL.pop_back();
389 21 : return osBaseURL;
390 : }
391 :
392 : /************************************************************************/
393 : /* CreateHandleHelper() */
394 : /************************************************************************/
395 :
396 : IVSIS3LikeHandleHelper *
397 26 : VSISwiftFSHandler::CreateHandleHelper(const char *pszURI, bool)
398 : {
399 26 : return VSISwiftHandleHelper::BuildFromURI(pszURI, GetFSPrefix().c_str());
400 : }
401 :
402 : /************************************************************************/
403 : /* Stat() */
404 : /************************************************************************/
405 :
406 13 : int VSISwiftFSHandler::Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
407 : int nFlags)
408 : {
409 13 : if (!STARTS_WITH_CI(pszFilename, GetFSPrefix().c_str()))
410 0 : return -1;
411 :
412 13 : if ((nFlags & VSI_STAT_CACHE_ONLY) != 0)
413 2 : return VSICurlFilesystemHandlerBase::Stat(pszFilename, pStatBuf,
414 2 : nFlags);
415 :
416 22 : std::string osFilename(pszFilename);
417 11 : if (osFilename.back() == '/')
418 6 : osFilename.pop_back();
419 :
420 11 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
421 :
422 11 : if (VSICurlFilesystemHandlerBase::Stat(pszFilename, pStatBuf, nFlags) == 0)
423 : {
424 : // if querying /vsiswift/container_name, the GET will succeed and
425 : // we would consider this as a file whereas it should be exposed as
426 : // a directory
427 6 : if (std::count(osFilename.begin(), osFilename.end(), '/') <= 2)
428 : {
429 :
430 : auto poHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
431 1 : CreateHandleHelper(pszFilename + GetFSPrefix().size(), true));
432 1 : if (poHandleHelper)
433 : {
434 2 : FileProp cachedFileProp;
435 1 : cachedFileProp.eExists = EXIST_YES;
436 1 : cachedFileProp.bHasComputedFileSize = false;
437 1 : cachedFileProp.fileSize = 0;
438 1 : cachedFileProp.bIsDirectory = true;
439 1 : cachedFileProp.mTime = 0;
440 1 : cachedFileProp.nMode = S_IFDIR;
441 1 : SetCachedFileProp(poHandleHelper->GetURL().c_str(),
442 : cachedFileProp);
443 : }
444 :
445 1 : pStatBuf->st_size = 0;
446 1 : pStatBuf->st_mode = S_IFDIR;
447 : }
448 6 : return 0;
449 : }
450 :
451 : // In the case of a directory, a GET on it will not work, so we have to
452 : // query the upper directory contents
453 5 : if (std::count(osFilename.begin(), osFilename.end(), '/') < 2)
454 0 : return -1;
455 :
456 5 : char **papszContents = VSIReadDir(CPLGetPath(osFilename.c_str()));
457 5 : int nRet = CSLFindStringCaseSensitive(
458 : papszContents, CPLGetFilename(osFilename.c_str())) >= 0
459 5 : ? 0
460 5 : : -1;
461 5 : CSLDestroy(papszContents);
462 :
463 10 : FileProp cachedFileProp;
464 5 : if (nRet == 0)
465 : {
466 2 : pStatBuf->st_mode = S_IFDIR;
467 :
468 2 : cachedFileProp.eExists = EXIST_YES;
469 2 : cachedFileProp.bHasComputedFileSize = false;
470 2 : cachedFileProp.fileSize = 0;
471 2 : cachedFileProp.bIsDirectory = true;
472 2 : cachedFileProp.mTime = 0;
473 2 : cachedFileProp.nMode = S_IFDIR;
474 : }
475 : else
476 : {
477 3 : cachedFileProp.eExists = EXIST_NO;
478 : }
479 :
480 : auto poHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
481 5 : CreateHandleHelper(pszFilename + GetFSPrefix().size(), true));
482 5 : if (poHandleHelper)
483 : {
484 5 : SetCachedFileProp(poHandleHelper->GetURL().c_str(), cachedFileProp);
485 : }
486 :
487 5 : return nRet;
488 : }
489 :
490 : /************************************************************************/
491 : /* GetFileList() */
492 : /************************************************************************/
493 :
494 14 : char **VSISwiftFSHandler::GetFileList(const char *pszDirname, int nMaxFiles,
495 : bool *pbGotFileList)
496 : {
497 : if (ENABLE_DEBUG)
498 : CPLDebug(GetDebugKey(), "GetFileList(%s)", pszDirname);
499 14 : *pbGotFileList = false;
500 14 : CPLAssert(strlen(pszDirname) >= GetFSPrefix().size());
501 42 : std::string osDirnameWithoutPrefix = pszDirname + GetFSPrefix().size();
502 14 : if (!osDirnameWithoutPrefix.empty() && osDirnameWithoutPrefix.back() == '/')
503 : {
504 0 : osDirnameWithoutPrefix.pop_back();
505 : }
506 :
507 28 : std::string osBucket(osDirnameWithoutPrefix);
508 28 : std::string osObjectKey;
509 14 : size_t nSlashPos = osDirnameWithoutPrefix.find('/');
510 14 : if (nSlashPos != std::string::npos)
511 : {
512 3 : osBucket = osDirnameWithoutPrefix.substr(0, nSlashPos);
513 3 : osObjectKey = osDirnameWithoutPrefix.substr(nSlashPos + 1);
514 : }
515 :
516 : IVSIS3LikeHandleHelper *poS3HandleHelper =
517 14 : CreateHandleHelper(osBucket.c_str(), true);
518 14 : if (poS3HandleHelper == nullptr)
519 : {
520 0 : return nullptr;
521 : }
522 :
523 14 : WriteFuncStruct sWriteFuncData;
524 :
525 28 : CPLStringList osFileList; // must be left in this scope !
526 28 : std::string osNextMarker; // must be left in this scope !
527 :
528 28 : std::string osMaxKeys = CPLGetConfigOption("SWIFT_MAX_KEYS", "10000");
529 14 : int nMaxFilesThisQuery = atoi(osMaxKeys.c_str());
530 14 : if (nMaxFiles > 0 && nMaxFiles <= 100 && nMaxFiles < nMaxFilesThisQuery)
531 : {
532 2 : nMaxFilesThisQuery = nMaxFiles + 1;
533 : }
534 14 : const std::string osPrefix(osObjectKey.empty() ? std::string()
535 28 : : osObjectKey + "/");
536 :
537 28 : const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszDirname));
538 28 : const CPLHTTPRetryParameters oRetryParameters(aosHTTPOptions);
539 :
540 : while (true)
541 : {
542 16 : CPLHTTPRetryContext oRetryContext(oRetryParameters);
543 : bool bRetry;
544 2 : do
545 : {
546 16 : bRetry = false;
547 16 : poS3HandleHelper->ResetQueryParameters();
548 16 : std::string osBaseURL(poS3HandleHelper->GetURL());
549 :
550 16 : CURLM *hCurlMultiHandle = GetCurlMultiHandleFor(osBaseURL);
551 16 : CURL *hCurlHandle = curl_easy_init();
552 :
553 16 : if (!osBucket.empty())
554 : {
555 13 : poS3HandleHelper->AddQueryParameter("delimiter", "/");
556 13 : if (!osNextMarker.empty())
557 2 : poS3HandleHelper->AddQueryParameter("marker", osNextMarker);
558 13 : poS3HandleHelper->AddQueryParameter(
559 : "limit", CPLSPrintf("%d", nMaxFilesThisQuery));
560 13 : if (!osPrefix.empty())
561 3 : poS3HandleHelper->AddQueryParameter("prefix", osPrefix);
562 : }
563 :
564 16 : struct curl_slist *headers = VSICurlSetOptions(
565 16 : hCurlHandle, poS3HandleHelper->GetURL().c_str(), nullptr);
566 : // Disable automatic redirection
567 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_FOLLOWLOCATION, 0);
568 :
569 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_RANGE, nullptr);
570 :
571 16 : VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr,
572 : nullptr);
573 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA,
574 : &sWriteFuncData);
575 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
576 : VSICurlHandleWriteFunc);
577 :
578 16 : WriteFuncStruct sWriteFuncHeaderData;
579 16 : VSICURLInitWriteFuncStruct(&sWriteFuncHeaderData, nullptr, nullptr,
580 : nullptr);
581 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA,
582 : &sWriteFuncHeaderData);
583 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION,
584 : VSICurlHandleWriteFunc);
585 :
586 16 : char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
587 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER,
588 : szCurlErrBuf);
589 :
590 16 : headers = VSICurlMergeHeaders(
591 16 : headers, poS3HandleHelper->GetCurlHeaders("GET", headers));
592 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER,
593 : headers);
594 :
595 16 : VSICURLMultiPerform(hCurlMultiHandle, hCurlHandle);
596 :
597 16 : VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
598 :
599 16 : if (headers != nullptr)
600 16 : curl_slist_free_all(headers);
601 :
602 16 : if (sWriteFuncData.pBuffer == nullptr)
603 : {
604 3 : delete poS3HandleHelper;
605 3 : curl_easy_cleanup(hCurlHandle);
606 3 : CPLFree(sWriteFuncHeaderData.pBuffer);
607 3 : return nullptr;
608 : }
609 :
610 13 : long response_code = 0;
611 13 : curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
612 13 : if (response_code != 200)
613 : {
614 : // Look if we should attempt a retry
615 0 : if (oRetryContext.CanRetry(static_cast<int>(response_code),
616 0 : sWriteFuncHeaderData.pBuffer,
617 : szCurlErrBuf))
618 : {
619 0 : CPLError(CE_Warning, CPLE_AppDefined,
620 : "HTTP error code: %d - %s. "
621 : "Retrying again in %.1f secs",
622 : static_cast<int>(response_code),
623 0 : poS3HandleHelper->GetURL().c_str(),
624 : oRetryContext.GetCurrentDelay());
625 0 : CPLSleep(oRetryContext.GetCurrentDelay());
626 0 : bRetry = true;
627 0 : CPLFree(sWriteFuncData.pBuffer);
628 0 : CPLFree(sWriteFuncHeaderData.pBuffer);
629 : }
630 : else
631 : {
632 0 : CPLDebug(GetDebugKey(), "%s", sWriteFuncData.pBuffer);
633 0 : CPLFree(sWriteFuncData.pBuffer);
634 0 : CPLFree(sWriteFuncHeaderData.pBuffer);
635 0 : delete poS3HandleHelper;
636 0 : curl_easy_cleanup(hCurlHandle);
637 0 : return nullptr;
638 : }
639 : }
640 : else
641 : {
642 13 : *pbGotFileList = true;
643 : bool bIsTruncated;
644 13 : AnalyseSwiftFileList(
645 13 : osBaseURL, osPrefix, sWriteFuncData.pBuffer, osFileList,
646 : nMaxFilesThisQuery, nMaxFiles, bIsTruncated, osNextMarker);
647 :
648 13 : CPLFree(sWriteFuncData.pBuffer);
649 13 : CPLFree(sWriteFuncHeaderData.pBuffer);
650 :
651 13 : if (osNextMarker.empty())
652 : {
653 11 : delete poS3HandleHelper;
654 11 : curl_easy_cleanup(hCurlHandle);
655 11 : return osFileList.StealList();
656 : }
657 : }
658 :
659 2 : curl_easy_cleanup(hCurlHandle);
660 : } while (bRetry);
661 2 : }
662 : }
663 :
664 : /************************************************************************/
665 : /* VSISwiftHandle() */
666 : /************************************************************************/
667 :
668 19 : VSISwiftHandle::VSISwiftHandle(VSISwiftFSHandler *poFSIn,
669 : const char *pszFilename,
670 19 : VSISwiftHandleHelper *poHandleHelper)
671 19 : : IVSIS3LikeHandle(poFSIn, pszFilename, poHandleHelper->GetURL().c_str()),
672 19 : m_poHandleHelper(poHandleHelper)
673 : {
674 19 : }
675 :
676 : /************************************************************************/
677 : /* ~VSISwiftHandle() */
678 : /************************************************************************/
679 :
680 38 : VSISwiftHandle::~VSISwiftHandle()
681 : {
682 19 : delete m_poHandleHelper;
683 38 : }
684 :
685 : /************************************************************************/
686 : /* GetCurlHeaders() */
687 : /************************************************************************/
688 :
689 : struct curl_slist *
690 17 : VSISwiftHandle::GetCurlHeaders(const std::string &osVerb,
691 : const struct curl_slist *psExistingHeaders)
692 : {
693 17 : return m_poHandleHelper->GetCurlHeaders(osVerb, psExistingHeaders);
694 : }
695 :
696 : /************************************************************************/
697 : /* Authenticate() */
698 : /************************************************************************/
699 :
700 0 : bool VSISwiftHandle::Authenticate(const char *pszFilename)
701 : {
702 0 : return m_poHandleHelper->Authenticate(pszFilename);
703 : }
704 :
705 : } /* end of namespace cpl */
706 :
707 : #endif // DOXYGEN_SKIP
708 : //! @endcond
709 :
710 : /************************************************************************/
711 : /* VSIInstallSwiftFileHandler() */
712 : /************************************************************************/
713 :
714 : /*!
715 : \brief Install /vsiswift/ OpenStack Swif Object Storage (Swift) file
716 : system handler (requires libcurl)
717 :
718 : \verbatim embed:rst
719 : See :ref:`/vsiswift/ documentation <vsiswift>`
720 : \endverbatim
721 :
722 : @since GDAL 2.3
723 : */
724 1304 : void VSIInstallSwiftFileHandler(void)
725 : {
726 1304 : VSIFileManager::InstallHandler("/vsiswift/",
727 1304 : new cpl::VSISwiftFSHandler("/vsiswift/"));
728 1304 : }
729 :
730 : #endif /* HAVE_CURL */
|