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 1392 : explicit VSISwiftFSHandler(const char *pszPrefix) : m_osPrefix(pszPrefix)
226 : {
227 1392 : }
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 1882 : VSISwiftFSHandler::~VSISwiftFSHandler()
299 : {
300 941 : VSISwiftFSHandler::ClearCache();
301 941 : VSISwiftHandleHelper::CleanMutex();
302 1882 : }
303 :
304 : /************************************************************************/
305 : /* ClearCache() */
306 : /************************************************************************/
307 :
308 1254 : void VSISwiftFSHandler::ClearCache()
309 : {
310 1254 : VSICurlFilesystemHandlerBase::ClearCache();
311 :
312 1254 : VSISwiftHandleHelper::ClearCache();
313 1254 : }
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 : char **papszContents =
457 5 : VSIReadDir(CPLGetPathSafe(osFilename.c_str()).c_str());
458 5 : int nRet = CSLFindStringCaseSensitive(
459 : papszContents, CPLGetFilename(osFilename.c_str())) >= 0
460 5 : ? 0
461 5 : : -1;
462 5 : CSLDestroy(papszContents);
463 :
464 10 : FileProp cachedFileProp;
465 5 : if (nRet == 0)
466 : {
467 2 : pStatBuf->st_mode = S_IFDIR;
468 :
469 2 : cachedFileProp.eExists = EXIST_YES;
470 2 : cachedFileProp.bHasComputedFileSize = false;
471 2 : cachedFileProp.fileSize = 0;
472 2 : cachedFileProp.bIsDirectory = true;
473 2 : cachedFileProp.mTime = 0;
474 2 : cachedFileProp.nMode = S_IFDIR;
475 : }
476 : else
477 : {
478 3 : cachedFileProp.eExists = EXIST_NO;
479 : }
480 :
481 : auto poHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
482 5 : CreateHandleHelper(pszFilename + GetFSPrefix().size(), true));
483 5 : if (poHandleHelper)
484 : {
485 5 : SetCachedFileProp(poHandleHelper->GetURL().c_str(), cachedFileProp);
486 : }
487 :
488 5 : return nRet;
489 : }
490 :
491 : /************************************************************************/
492 : /* GetFileList() */
493 : /************************************************************************/
494 :
495 14 : char **VSISwiftFSHandler::GetFileList(const char *pszDirname, int nMaxFiles,
496 : bool *pbGotFileList)
497 : {
498 : if (ENABLE_DEBUG)
499 : CPLDebug(GetDebugKey(), "GetFileList(%s)", pszDirname);
500 14 : *pbGotFileList = false;
501 14 : CPLAssert(strlen(pszDirname) >= GetFSPrefix().size());
502 42 : std::string osDirnameWithoutPrefix = pszDirname + GetFSPrefix().size();
503 14 : if (!osDirnameWithoutPrefix.empty() && osDirnameWithoutPrefix.back() == '/')
504 : {
505 0 : osDirnameWithoutPrefix.pop_back();
506 : }
507 :
508 28 : std::string osBucket(osDirnameWithoutPrefix);
509 28 : std::string osObjectKey;
510 14 : size_t nSlashPos = osDirnameWithoutPrefix.find('/');
511 14 : if (nSlashPos != std::string::npos)
512 : {
513 3 : osBucket = osDirnameWithoutPrefix.substr(0, nSlashPos);
514 3 : osObjectKey = osDirnameWithoutPrefix.substr(nSlashPos + 1);
515 : }
516 :
517 : IVSIS3LikeHandleHelper *poS3HandleHelper =
518 14 : CreateHandleHelper(osBucket.c_str(), true);
519 14 : if (poS3HandleHelper == nullptr)
520 : {
521 0 : return nullptr;
522 : }
523 :
524 14 : WriteFuncStruct sWriteFuncData;
525 :
526 28 : CPLStringList osFileList; // must be left in this scope !
527 28 : std::string osNextMarker; // must be left in this scope !
528 :
529 28 : std::string osMaxKeys = CPLGetConfigOption("SWIFT_MAX_KEYS", "10000");
530 14 : int nMaxFilesThisQuery = atoi(osMaxKeys.c_str());
531 14 : if (nMaxFiles > 0 && nMaxFiles <= 100 && nMaxFiles < nMaxFilesThisQuery)
532 : {
533 2 : nMaxFilesThisQuery = nMaxFiles + 1;
534 : }
535 14 : const std::string osPrefix(osObjectKey.empty() ? std::string()
536 28 : : osObjectKey + "/");
537 :
538 28 : const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszDirname));
539 28 : const CPLHTTPRetryParameters oRetryParameters(aosHTTPOptions);
540 :
541 : while (true)
542 : {
543 16 : CPLHTTPRetryContext oRetryContext(oRetryParameters);
544 : bool bRetry;
545 2 : do
546 : {
547 16 : bRetry = false;
548 16 : poS3HandleHelper->ResetQueryParameters();
549 16 : std::string osBaseURL(poS3HandleHelper->GetURL());
550 :
551 16 : CURLM *hCurlMultiHandle = GetCurlMultiHandleFor(osBaseURL);
552 16 : CURL *hCurlHandle = curl_easy_init();
553 :
554 16 : if (!osBucket.empty())
555 : {
556 13 : poS3HandleHelper->AddQueryParameter("delimiter", "/");
557 13 : if (!osNextMarker.empty())
558 2 : poS3HandleHelper->AddQueryParameter("marker", osNextMarker);
559 13 : poS3HandleHelper->AddQueryParameter(
560 : "limit", CPLSPrintf("%d", nMaxFilesThisQuery));
561 13 : if (!osPrefix.empty())
562 3 : poS3HandleHelper->AddQueryParameter("prefix", osPrefix);
563 : }
564 :
565 16 : struct curl_slist *headers = VSICurlSetOptions(
566 16 : hCurlHandle, poS3HandleHelper->GetURL().c_str(), nullptr);
567 : // Disable automatic redirection
568 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_FOLLOWLOCATION, 0);
569 :
570 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_RANGE, nullptr);
571 :
572 16 : VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr,
573 : nullptr);
574 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA,
575 : &sWriteFuncData);
576 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
577 : VSICurlHandleWriteFunc);
578 :
579 16 : WriteFuncStruct sWriteFuncHeaderData;
580 16 : VSICURLInitWriteFuncStruct(&sWriteFuncHeaderData, nullptr, nullptr,
581 : nullptr);
582 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA,
583 : &sWriteFuncHeaderData);
584 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION,
585 : VSICurlHandleWriteFunc);
586 :
587 16 : char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
588 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER,
589 : szCurlErrBuf);
590 :
591 16 : headers = VSICurlMergeHeaders(
592 16 : headers, poS3HandleHelper->GetCurlHeaders("GET", headers));
593 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER,
594 : headers);
595 :
596 16 : VSICURLMultiPerform(hCurlMultiHandle, hCurlHandle);
597 :
598 16 : VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
599 :
600 16 : if (headers != nullptr)
601 16 : curl_slist_free_all(headers);
602 :
603 16 : if (sWriteFuncData.pBuffer == nullptr)
604 : {
605 3 : delete poS3HandleHelper;
606 3 : curl_easy_cleanup(hCurlHandle);
607 3 : CPLFree(sWriteFuncHeaderData.pBuffer);
608 3 : return nullptr;
609 : }
610 :
611 13 : long response_code = 0;
612 13 : curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
613 13 : if (response_code != 200)
614 : {
615 : // Look if we should attempt a retry
616 0 : if (oRetryContext.CanRetry(static_cast<int>(response_code),
617 0 : sWriteFuncHeaderData.pBuffer,
618 : szCurlErrBuf))
619 : {
620 0 : CPLError(CE_Warning, CPLE_AppDefined,
621 : "HTTP error code: %d - %s. "
622 : "Retrying again in %.1f secs",
623 : static_cast<int>(response_code),
624 0 : poS3HandleHelper->GetURL().c_str(),
625 : oRetryContext.GetCurrentDelay());
626 0 : CPLSleep(oRetryContext.GetCurrentDelay());
627 0 : bRetry = true;
628 0 : CPLFree(sWriteFuncData.pBuffer);
629 0 : CPLFree(sWriteFuncHeaderData.pBuffer);
630 : }
631 : else
632 : {
633 0 : CPLDebug(GetDebugKey(), "%s", sWriteFuncData.pBuffer);
634 0 : CPLFree(sWriteFuncData.pBuffer);
635 0 : CPLFree(sWriteFuncHeaderData.pBuffer);
636 0 : delete poS3HandleHelper;
637 0 : curl_easy_cleanup(hCurlHandle);
638 0 : return nullptr;
639 : }
640 : }
641 : else
642 : {
643 13 : *pbGotFileList = true;
644 : bool bIsTruncated;
645 13 : AnalyseSwiftFileList(
646 13 : osBaseURL, osPrefix, sWriteFuncData.pBuffer, osFileList,
647 : nMaxFilesThisQuery, nMaxFiles, bIsTruncated, osNextMarker);
648 :
649 13 : CPLFree(sWriteFuncData.pBuffer);
650 13 : CPLFree(sWriteFuncHeaderData.pBuffer);
651 :
652 13 : if (osNextMarker.empty())
653 : {
654 11 : delete poS3HandleHelper;
655 11 : curl_easy_cleanup(hCurlHandle);
656 11 : return osFileList.StealList();
657 : }
658 : }
659 :
660 2 : curl_easy_cleanup(hCurlHandle);
661 : } while (bRetry);
662 2 : }
663 : }
664 :
665 : /************************************************************************/
666 : /* VSISwiftHandle() */
667 : /************************************************************************/
668 :
669 19 : VSISwiftHandle::VSISwiftHandle(VSISwiftFSHandler *poFSIn,
670 : const char *pszFilename,
671 19 : VSISwiftHandleHelper *poHandleHelper)
672 19 : : IVSIS3LikeHandle(poFSIn, pszFilename, poHandleHelper->GetURL().c_str()),
673 19 : m_poHandleHelper(poHandleHelper)
674 : {
675 19 : }
676 :
677 : /************************************************************************/
678 : /* ~VSISwiftHandle() */
679 : /************************************************************************/
680 :
681 38 : VSISwiftHandle::~VSISwiftHandle()
682 : {
683 19 : delete m_poHandleHelper;
684 38 : }
685 :
686 : /************************************************************************/
687 : /* GetCurlHeaders() */
688 : /************************************************************************/
689 :
690 : struct curl_slist *
691 17 : VSISwiftHandle::GetCurlHeaders(const std::string &osVerb,
692 : const struct curl_slist *psExistingHeaders)
693 : {
694 17 : return m_poHandleHelper->GetCurlHeaders(osVerb, psExistingHeaders);
695 : }
696 :
697 : /************************************************************************/
698 : /* Authenticate() */
699 : /************************************************************************/
700 :
701 0 : bool VSISwiftHandle::Authenticate(const char *pszFilename)
702 : {
703 0 : return m_poHandleHelper->Authenticate(pszFilename);
704 : }
705 :
706 : } /* end of namespace cpl */
707 :
708 : #endif // DOXYGEN_SKIP
709 : //! @endcond
710 :
711 : /************************************************************************/
712 : /* VSIInstallSwiftFileHandler() */
713 : /************************************************************************/
714 :
715 : /*!
716 : \brief Install /vsiswift/ OpenStack Swif Object Storage (Swift) file
717 : system handler (requires libcurl)
718 :
719 : \verbatim embed:rst
720 : See :ref:`/vsiswift/ documentation <vsiswift>`
721 : \endverbatim
722 :
723 : @since GDAL 2.3
724 : */
725 1392 : void VSIInstallSwiftFileHandler(void)
726 : {
727 1392 : VSIFileManager::InstallHandler("/vsiswift/",
728 1392 : new cpl::VSISwiftFSHandler("/vsiswift/"));
729 1392 : }
730 :
731 : #endif /* HAVE_CURL */
|