GDAL
cpl_vsil_curl_class.h
1/******************************************************************************
2 *
3 * Project: CPL - Common Portability Library
4 * Purpose: Declarations for /vsicurl/ and related file systems
5 * Author: Even Rouault, even.rouault at spatialys.com
6 *
7 ******************************************************************************
8 * Copyright (c) 2010-2018, Even Rouault <even.rouault at spatialys.com>
9 *
10 * SPDX-License-Identifier: MIT
11 ****************************************************************************/
12
13#ifndef CPL_VSIL_CURL_CLASS_H_INCLUDED
14#define CPL_VSIL_CURL_CLASS_H_INCLUDED
15
16#ifdef HAVE_CURL
17
18#include "cpl_aws.h"
19#include "cpl_azure.h"
20#include "cpl_port.h"
21#include "cpl_json.h"
22#include "cpl_http.h"
23#include "cpl_string.h"
24#include "cpl_vsil_curl_priv.h"
25#include "cpl_mem_cache.h"
26#include "cpl_multiproc.h"
27
28#include "cpl_curl_priv.h"
29
30#include <algorithm>
31#include <atomic>
32#include <condition_variable>
33#include <set>
34#include <map>
35#include <memory>
36#include <mutex>
37#include <thread>
38#include <utility>
39
40// To avoid aliasing to CopyFile to CopyFileA on Windows
41#ifdef CopyFile
42#undef CopyFile
43#endif
44
46
47// Leave it for backward compatibility, but deprecate.
48#define HAVE_CURLINFO_REDIRECT_URL
49
50void VSICurlStreamingClearCache(void); // from cpl_vsil_curl_streaming.cpp
51
52struct curl_slist *VSICurlSetOptions(CURL *hCurlHandle, const char *pszURL,
53 const char *const *papszOptions);
54
55struct curl_slist *VSICurlSetContentTypeFromExt(struct curl_slist *polist,
56 const char *pszPath);
57
58struct curl_slist *VSICurlSetCreationHeadersFromOptions(
59 struct curl_slist *headers, CSLConstList papszOptions, const char *pszPath);
60
61namespace cpl
62{
63
64typedef enum
65{
66 EXIST_UNKNOWN = -1,
67 EXIST_NO,
68 EXIST_YES,
69} ExistStatus;
70
71class FileProp
72{
73 public:
74 unsigned int nGenerationAuthParameters = 0;
75 ExistStatus eExists = EXIST_UNKNOWN;
76 int nHTTPCode = 0;
77 vsi_l_offset fileSize = 0;
78 time_t mTime = 0;
79 time_t nExpireTimestampLocal = 0;
80 std::string osRedirectURL{};
81 bool bHasComputedFileSize = false;
82 bool bIsDirectory = false;
83 bool bIsAzureFolder = false;
84 int nMode = 0; // st_mode member of struct stat
85 bool bS3LikeRedirect = false;
86 std::string ETag{};
87};
88
89struct CachedDirList
90{
91 bool bGotFileList = false;
92 unsigned int nGenerationAuthParameters = 0;
93 CPLStringList oFileList{}; /* only file name without path */
94};
95
96struct WriteFuncStruct
97{
98 char *pBuffer = nullptr;
99 size_t nSize = 0;
100 bool bIsHTTP = false;
101 bool bMultiRange = false;
102 vsi_l_offset nStartOffset = 0;
103 vsi_l_offset nEndOffset = 0;
104 int nHTTPCode = 0; // potentially after redirect
105 int nFirstHTTPCode = 0; // the one of the redirect
106 vsi_l_offset nContentLength = 0;
107 bool bFoundContentRange = false;
108 bool bError = false;
109 bool bInterruptDownload = false;
110 bool bDetectRangeDownloadingError = false;
111 GIntBig nTimestampDate = 0; // Corresponds to Date: header field
112
113 VSILFILE *fp = nullptr;
114 VSICurlReadCbkFunc pfnReadCbk = nullptr;
115 void *pReadCbkUserData = nullptr;
116 bool bInterrupted = false;
117};
118
119struct PutData
120{
121 const GByte *pabyData = nullptr;
122 size_t nOff = 0;
123 size_t nTotalSize = 0;
124
125 static size_t ReadCallBackBuffer(char *buffer, size_t size, size_t nitems,
126 void *instream)
127 {
128 PutData *poThis = static_cast<PutData *>(instream);
129 const size_t nSizeMax = size * nitems;
130 const size_t nSizeToWrite =
131 std::min(nSizeMax, poThis->nTotalSize - poThis->nOff);
132 memcpy(buffer, poThis->pabyData + poThis->nOff, nSizeToWrite);
133 poThis->nOff += nSizeToWrite;
134 return nSizeToWrite;
135 }
136};
137
138/************************************************************************/
139/* VSICurlFilesystemHandler */
140/************************************************************************/
141
142class VSICurlHandle;
143
144class VSICurlFilesystemHandlerBase : public VSIFilesystemHandler
145{
146 CPL_DISALLOW_COPY_ASSIGN(VSICurlFilesystemHandlerBase)
147
148 struct FilenameOffsetPair
149 {
150 std::string filename_;
151 vsi_l_offset offset_;
152
153 FilenameOffsetPair(const std::string &filename, vsi_l_offset offset)
154 : filename_(filename), offset_(offset)
155 {
156 }
157
158 bool operator==(const FilenameOffsetPair &other) const
159 {
160 return filename_ == other.filename_ && offset_ == other.offset_;
161 }
162 };
163
164 struct FilenameOffsetPairHasher
165 {
166 std::size_t operator()(const FilenameOffsetPair &k) const
167 {
168 return std::hash<std::string>()(k.filename_) ^
169 std::hash<vsi_l_offset>()(k.offset_);
170 }
171 };
172
173 using RegionCacheType = lru11::Cache<
174 FilenameOffsetPair, std::shared_ptr<std::string>, lru11::NullLock,
175 std::unordered_map<
176 FilenameOffsetPair,
177 typename std::list<lru11::KeyValuePair<
178 FilenameOffsetPair, std::shared_ptr<std::string>>>::iterator,
179 FilenameOffsetPairHasher>>;
180
181 std::unique_ptr<RegionCacheType>
182 m_poRegionCacheDoNotUseDirectly{}; // do not access directly. Use
183 // GetRegionCache();
184 RegionCacheType *GetRegionCache();
185
186 // LRU cache that just keeps in memory if this file system handler is
187 // spposed to know the file properties of a file. The actual cache is a
188 // shared one among all network file systems.
189 // The aim of that design is that invalidating /vsis3/foo results in
190 // /vsis3_streaming/foo to be invalidated as well.
191 lru11::Cache<std::string, bool> oCacheFileProp;
192
193 int nCachedFilesInDirList = 0;
194 lru11::Cache<std::string, CachedDirList> oCacheDirList;
195
196 char **ParseHTMLFileList(const char *pszFilename, int nMaxFiles,
197 char *pszData, bool *pbGotFileList);
198
199 // Data structure and map to store regions that are in progress, to
200 // avoid simultaneous downloads of the same region in different threads
201 // Cf https://github.com/OSGeo/gdal/issues/8041
202 struct RegionInDownload
203 {
204 std::mutex oMutex{};
205 std::condition_variable oCond{};
206 bool bDownloadInProgress = false;
207 int nWaiters = 0;
208 std::string osData{};
209 };
210
211 std::mutex m_oMutex{};
212 std::map<std::string, std::unique_ptr<RegionInDownload>>
213 m_oMapRegionInDownload{};
214
215 protected:
216 CPLMutex *hMutex = nullptr;
217
218 virtual VSICurlHandle *CreateFileHandle(const char *pszFilename);
219 virtual char **GetFileList(const char *pszFilename, int nMaxFiles,
220 bool *pbGotFileList);
221
222 void RegisterEmptyDir(const std::string &osDirname);
223
224 bool
225 AnalyseS3FileList(const std::string &osBaseURL, const char *pszXML,
226 CPLStringList &osFileList, int nMaxFiles,
227 const std::set<std::string> &oSetIgnoredStorageClasses,
228 bool &bIsTruncated);
229
230 void AnalyseSwiftFileList(const std::string &osBaseURL,
231 const std::string &osPrefix, const char *pszJson,
232 CPLStringList &osFileList, int nMaxFilesThisQuery,
233 int nMaxFiles, bool &bIsTruncated,
234 std::string &osNextMarker);
235
236 static const char *GetOptionsStatic();
237
238 VSICurlFilesystemHandlerBase();
239
240 public:
241 ~VSICurlFilesystemHandlerBase() override;
242
243 static bool IsAllowedFilename(const char *pszFilename);
244
245 VSIVirtualHandleUniquePtr Open(const char *pszFilename,
246 const char *pszAccess, bool bSetError,
247 CSLConstList /* papszOptions */) override;
248
249 int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
250 int nFlags) override;
251 char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
252 char **SiblingFiles(const char *pszFilename) override;
253
254 int HasOptimizedReadMultiRange(const char * /* pszPath */) override
255 {
256 return true;
257 }
258
259 const char *GetActualURL(const char *pszFilename) override;
260
261 const char *GetOptions() override;
262
263 char **GetFileMetadata(const char *pszFilename, const char *pszDomain,
264 CSLConstList papszOptions) override;
265
266 char **ReadDirInternal(const char *pszDirname, int nMaxFiles,
267 bool *pbGotFileList);
268 void InvalidateDirContent(const std::string &osDirname);
269
270 virtual const char *GetDebugKey() const = 0;
271
272 virtual std::string GetFSPrefix() const = 0;
273 virtual bool AllowCachedDataFor(const char *pszFilename);
274
275 bool IsLocal(const char * /* pszPath */) const override
276 {
277 return false;
278 }
279
280 virtual bool
281 SupportsSequentialWrite(const char * /* pszPath */,
282 bool /* bAllowLocalTempFile */) override
283 {
284 return false;
285 }
286
287 virtual bool SupportsRandomWrite(const char * /* pszPath */,
288 bool /* bAllowLocalTempFile */) override
289 {
290 return false;
291 }
292
293 std::shared_ptr<std::string> GetRegion(const char *pszURL,
294 vsi_l_offset nFileOffsetStart);
295
296 void AddRegion(const char *pszURL, vsi_l_offset nFileOffsetStart,
297 size_t nSize, const char *pData);
298
299 std::pair<bool, std::string>
300 NotifyStartDownloadRegion(const std::string &osURL,
301 vsi_l_offset startOffset, int nBlocks);
302 void NotifyStopDownloadRegion(const std::string &osURL,
303 vsi_l_offset startOffset, int nBlocks,
304 const std::string &osData);
305
306 bool GetCachedFileProp(const char *pszURL, FileProp &oFileProp);
307 void SetCachedFileProp(const char *pszURL, FileProp &oFileProp);
308 void InvalidateCachedData(const char *pszURL);
309
310 CURLM *GetCurlMultiHandleFor(const std::string &osURL);
311
312 virtual void ClearCache();
313 virtual void PartialClearCache(const char *pszFilename);
314
315 bool GetCachedDirList(const char *pszURL, CachedDirList &oCachedDirList);
316 void SetCachedDirList(const char *pszURL, CachedDirList &oCachedDirList);
317 bool ExistsInCacheDirList(const std::string &osDirname, bool *pbIsDir);
318
319 virtual std::string GetURLFromFilename(const std::string &osFilename) const;
320
321 std::string
322 GetStreamingFilename(const std::string &osFilename) const override = 0;
323
324 static std::set<std::string> GetS3IgnoredStorageClasses();
325};
326
327class VSICurlFilesystemHandler final : public VSICurlFilesystemHandlerBase
328{
329 CPL_DISALLOW_COPY_ASSIGN(VSICurlFilesystemHandler)
330
331 public:
332 VSICurlFilesystemHandler() = default;
333
334 const char *GetDebugKey() const override
335 {
336 return "VSICURL";
337 }
338
339 std::string GetFSPrefix() const override
340 {
341 return "/vsicurl/";
342 }
343
344 std::string
345 GetStreamingFilename(const std::string &osFilename) const override;
346
347 std::string
348 GetHintForPotentiallyRecognizedPath(const std::string &osPath) override;
349};
350
351/************************************************************************/
352/* VSICurlHandle */
353/************************************************************************/
354
355class VSICurlHandle /* non final*/ : public VSIVirtualHandle
356{
357 CPL_DISALLOW_COPY_ASSIGN(VSICurlHandle)
358
359 protected:
360 VSICurlFilesystemHandlerBase *poFS = nullptr;
361
362 bool m_bCached = true;
363
364 mutable FileProp oFileProp{};
365
366 mutable std::mutex m_oMutex{};
367 std::string m_osFilename{}; // e.g "/vsicurl/http://example.com/foo"
368 char *m_pszURL = nullptr; // e.g "http://example.com/foo"
369 mutable std::string m_osQueryString{}; // e.g. an Azure SAS
370
371 CPLStringList m_aosHTTPOptions{};
372 CPLHTTPRetryParameters
373 m_oRetryParameters; // must be initialized in constructor
374
375 vsi_l_offset lastDownloadedOffset = VSI_L_OFFSET_MAX;
376 int nBlocksToDownload = 1;
377
378 bool bStopOnInterruptUntilUninstall = false;
379 bool bInterrupted = false;
380 VSICurlReadCbkFunc pfnReadCbk = nullptr;
381 void *pReadCbkUserData = nullptr;
382
383 CPLStringList m_aosHeaders{};
384
385 void DownloadRegionPostProcess(const vsi_l_offset startOffset,
386 const int nBlocks, const char *pBuffer,
387 size_t nSize);
388
389 private:
390 vsi_l_offset curOffset = 0;
391
392 bool bEOF = false;
393 bool bError = false;
394
395 virtual std::string DownloadRegion(vsi_l_offset startOffset, int nBlocks);
396
397 bool m_bUseHead = false;
398 bool m_bUseRedirectURLIfNoQueryStringParams = false;
399
400 mutable std::atomic<bool> m_bInterrupt = false;
401
402 // Specific to Planetary Computer signing:
403 // https://planetarycomputer.microsoft.com/docs/concepts/sas/
404 mutable bool m_bPlanetaryComputerURLSigning = false;
405 mutable std::string m_osPlanetaryComputerCollection{};
406 void ManagePlanetaryComputerSigning() const;
407
408 void UpdateQueryString() const;
409
410 int ReadMultiRangeSingleGet(int nRanges, void **ppData,
411 const vsi_l_offset *panOffsets,
412 const size_t *panSizes);
413 std::string GetRedirectURLIfValid(bool &bHasExpired,
414 CPLStringList &aosHTTPOptions) const;
415
416 void UpdateRedirectInfo(CURL *hCurlHandle,
417 const WriteFuncStruct &sWriteFuncHeaderData);
418
419 // Used by AdviseRead()
420 struct AdviseReadRange
421 {
422 bool bDone = false;
423 bool bToRetry = true;
424 double dfSleepDelay = 0.0;
425 std::mutex oMutex{};
426 std::condition_variable oCV{};
427 vsi_l_offset nStartOffset = 0;
428 size_t nSize = 0;
429 std::vector<GByte> abyData{};
430 CPLHTTPRetryContext retryContext;
431
432 explicit AdviseReadRange(const CPLHTTPRetryParameters &oRetryParameters)
433 : retryContext(oRetryParameters)
434 {
435 }
436
437 AdviseReadRange(const AdviseReadRange &) = delete;
438 AdviseReadRange &operator=(const AdviseReadRange &) = delete;
439 AdviseReadRange(AdviseReadRange &&) = delete;
440 AdviseReadRange &operator=(AdviseReadRange &&) = delete;
441 };
442
443 std::vector<std::unique_ptr<AdviseReadRange>> m_aoAdviseReadRanges{};
444 std::thread m_oThreadAdviseRead{};
445 CURLM *m_hCurlMultiHandleForAdviseRead = nullptr;
446
447 protected:
448 virtual struct curl_slist *GetCurlHeaders(const std::string & /*osVerb*/,
449 struct curl_slist *psHeaders)
450 {
451 return psHeaders;
452 }
453
454 virtual bool AllowAutomaticRedirection()
455 {
456 return true;
457 }
458
459 virtual bool CanRestartOnError(const char *, const char *, bool)
460 {
461 return false;
462 }
463
464 virtual bool UseLimitRangeGetInsteadOfHead()
465 {
466 return false;
467 }
468
469 virtual bool IsDirectoryFromExists(const char * /*pszVerb*/,
470 int /*response_code*/)
471 {
472 return false;
473 }
474
475 virtual void ProcessGetFileSizeResult(const char * /* pszContent */)
476 {
477 }
478
479 void SetURL(const char *pszURL);
480
481 virtual bool Authenticate(const char * /* pszFilename */)
482 {
483 return false;
484 }
485
486 public:
487 VSICurlHandle(VSICurlFilesystemHandlerBase *poFS, const char *pszFilename,
488 const char *pszURLIn = nullptr);
489 ~VSICurlHandle() override;
490
491 int Seek(vsi_l_offset nOffset, int nWhence) override;
492 vsi_l_offset Tell() override;
493 size_t Read(void *pBuffer, size_t nBytes) override;
494 int ReadMultiRange(int nRanges, void **ppData,
495 const vsi_l_offset *panOffsets,
496 const size_t *panSizes) override;
497 size_t Write(const void *pBuffer, size_t nBytes) override;
498 void ClearErr() override;
499 int Eof() override;
500 int Error() override;
501 int Flush() override;
502 int Close() override;
503
504 void Interrupt() override
505 {
506 m_bInterrupt = true;
507 }
508
509 bool HasPRead() const override
510 {
511 return true;
512 }
513
514 size_t PRead(void *pBuffer, size_t nSize,
515 vsi_l_offset nOffset) const override;
516
517 void AdviseRead(int nRanges, const vsi_l_offset *panOffsets,
518 const size_t *panSizes) override;
519
520 size_t GetAdviseReadTotalBytesLimit() const override;
521
522 bool IsKnownFileSize() const
523 {
524 return oFileProp.bHasComputedFileSize;
525 }
526
527 vsi_l_offset GetFileSizeOrHeaders(bool bSetError, bool bGetHeaders);
528
529 virtual vsi_l_offset GetFileSize(bool bSetError)
530 {
531 return GetFileSizeOrHeaders(bSetError, false);
532 }
533
534 bool Exists(bool bSetError);
535
536 bool IsDirectory() const
537 {
538 return oFileProp.bIsDirectory;
539 }
540
541 int GetMode() const
542 {
543 return oFileProp.nMode;
544 }
545
546 time_t GetMTime() const
547 {
548 return oFileProp.mTime;
549 }
550
551 const CPLStringList &GetHeaders()
552 {
553 return m_aosHeaders;
554 }
555
556 int InstallReadCbk(VSICurlReadCbkFunc pfnReadCbk, void *pfnUserData,
557 int bStopOnInterruptUntilUninstall);
558 int UninstallReadCbk();
559
560 const char *GetURL() const
561 {
562 return m_pszURL;
563 }
564
566 void SetCache(bool bCache)
567 {
568 m_bCached = bCache;
569 }
570};
571
572/************************************************************************/
573/* VSICurlFilesystemHandlerBaseWritable */
574/************************************************************************/
575
576class VSICurlFilesystemHandlerBaseWritable /* non final */
577 : public VSICurlFilesystemHandlerBase
578{
579 CPL_DISALLOW_COPY_ASSIGN(VSICurlFilesystemHandlerBaseWritable)
580
581 protected:
582 VSICurlFilesystemHandlerBaseWritable() = default;
583
584 virtual VSIVirtualHandleUniquePtr
585 CreateWriteHandle(const char *pszFilename, CSLConstList papszOptions) = 0;
586
587 public:
588 VSIVirtualHandleUniquePtr Open(const char *pszFilename,
589 const char *pszAccess, bool bSetError,
590 CSLConstList /* papszOptions */) override;
591
592 bool SupportsSequentialWrite(const char * /* pszPath */,
593 bool /* bAllowLocalTempFile */) override
594 {
595 return true;
596 }
597
598 bool SupportsRandomWrite(const char * /* pszPath */,
599 bool /* bAllowLocalTempFile */) override;
600};
601
602/************************************************************************/
603/* IVSIS3LikeFSHandler */
604/************************************************************************/
605
606class IVSIS3LikeFSHandler /* non final */
607 : public VSICurlFilesystemHandlerBaseWritable
608{
609 CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeFSHandler)
610
611 virtual int MkdirInternal(const char *pszDirname, long nMode,
612 bool bDoStatCheck);
613
614 protected:
615 char **GetFileList(const char *pszFilename, int nMaxFiles,
616 bool *pbGotFileList) override;
617
618 virtual IVSIS3LikeHandleHelper *CreateHandleHelper(const char *pszURI,
619 bool bAllowNoObject) = 0;
620
621 virtual int CopyObject(const char *oldpath, const char *newpath,
622 CSLConstList papszMetadata);
623
624 int RmdirRecursiveInternal(const char *pszDirname, int nBatchSize);
625
626 virtual bool
627 IsAllowedHeaderForObjectCreation(const char * /* pszHeaderName */)
628 {
629 return false;
630 }
631
632 IVSIS3LikeFSHandler() = default;
633
634 public:
635 int Unlink(const char *pszFilename) override;
636 int Mkdir(const char *pszDirname, long nMode) override;
637 int Rmdir(const char *pszDirname) override;
638 int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
639 int nFlags) override;
640 int Rename(const char *oldpath, const char *newpath, GDALProgressFunc,
641 void *) override;
642
643 virtual int CopyFile(const char *pszSource, const char *pszTarget,
644 VSILFILE *fpSource, vsi_l_offset nSourceSize,
645 const char *const *papszOptions,
646 GDALProgressFunc pProgressFunc,
647 void *pProgressData) override;
648
649 virtual int DeleteObject(const char *pszFilename);
650
651 virtual int *DeleteObjectBatch(CSLConstList papszFilesOrDirs);
652
653 bool Sync(const char *pszSource, const char *pszTarget,
654 const char *const *papszOptions, GDALProgressFunc pProgressFunc,
655 void *pProgressData, char ***ppapszOutputs) override;
656
657 VSIDIR *OpenDir(const char *pszPath, int nRecurseDepth,
658 const char *const *papszOptions) override;
659};
660
661/************************************************************************/
662/* IVSIS3LikeFSHandlerWithMultipartUpload */
663/************************************************************************/
664
665class IVSIS3LikeFSHandlerWithMultipartUpload /* non final */
666 : public IVSIS3LikeFSHandler
667{
668 CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeFSHandlerWithMultipartUpload)
669
670 protected:
671 IVSIS3LikeFSHandlerWithMultipartUpload() = default;
672
673 public:
674 virtual bool SupportsNonSequentialMultipartUpload() const
675 {
676 return true;
677 }
678
679 virtual bool SupportsParallelMultipartUpload() const
680 {
681 return true;
682 }
683
684 virtual bool SupportsMultipartAbort() const = 0;
685
686 size_t GetUploadChunkSizeInBytes(const char *pszFilename,
687 const char *pszSpecifiedValInBytes);
688
689 virtual int CopyFileRestartable(const char *pszSource,
690 const char *pszTarget,
691 const char *pszInputPayload,
692 char **ppszOutputPayload,
693 CSLConstList papszOptions,
694 GDALProgressFunc pProgressFunc,
695 void *pProgressData) override;
696
698 // Limit currently used by S3 and GS.
699 // Cf https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html
700 // and https://cloud.google.com/storage/quotas#requests
701 virtual int GetMaximumPartCount()
702 {
703 return 10000;
704 }
705
707 // Limit currently used by S3 and GS.
708 // Cf https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html
709 // and https://cloud.google.com/storage/quotas#requests
710 virtual int GetMinimumPartSizeInMiB()
711 {
712 return 5;
713 }
714
716 // Limit currently used by S3 and GS.
717 // Cf https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html
718 // and https://cloud.google.com/storage/quotas#requests
719 virtual int GetMaximumPartSizeInMiB()
720 {
721#if SIZEOF_VOIDP == 8
722 return 5 * 1024;
723#else
724 // Cannot be larger than 4, otherwise integer overflow would occur
725 // 1 GiB is the maximum reasonable value on a 32-bit machine
726 return 1 * 1024;
727#endif
728 }
729
731 virtual int GetDefaultPartSizeInMiB()
732 {
733 return 50;
734 }
735
736 virtual std::string
737 InitiateMultipartUpload(const std::string &osFilename,
738 IVSIS3LikeHandleHelper *poS3HandleHelper,
739 const CPLHTTPRetryParameters &oRetryParameters,
740 CSLConstList papszOptions);
741
742 virtual std::string
743 UploadPart(const std::string &osFilename, int nPartNumber,
744 const std::string &osUploadID, vsi_l_offset nPosition,
745 const void *pabyBuffer, size_t nBufferSize,
746 IVSIS3LikeHandleHelper *poS3HandleHelper,
747 const CPLHTTPRetryParameters &oRetryParameters,
748 CSLConstList papszOptions);
749
750 virtual bool CompleteMultipart(
751 const std::string &osFilename, const std::string &osUploadID,
752 const std::vector<std::string> &aosEtags, vsi_l_offset nTotalSize,
753 IVSIS3LikeHandleHelper *poS3HandleHelper,
754 const CPLHTTPRetryParameters &oRetryParameters);
755
756 virtual bool AbortMultipart(const std::string &osFilename,
757 const std::string &osUploadID,
758 IVSIS3LikeHandleHelper *poS3HandleHelper,
759 const CPLHTTPRetryParameters &oRetryParameters);
760
761 bool AbortPendingUploads(const char *pszFilename) override;
762
763 bool MultipartUploadGetCapabilities(int *pbNonSequentialUploadSupported,
764 int *pbParallelUploadSupported,
765 int *pbAbortSupported,
766 size_t *pnMinPartSize,
767 size_t *pnMaxPartSize,
768 int *pnMaxPartCount) override;
769
770 char *MultipartUploadStart(const char *pszFilename,
771 CSLConstList papszOptions) override;
772
773 char *MultipartUploadAddPart(const char *pszFilename,
774 const char *pszUploadId, int nPartNumber,
775 vsi_l_offset nFileOffset, const void *pData,
776 size_t nDataLength,
777 CSLConstList papszOptions) override;
778
779 bool MultipartUploadEnd(const char *pszFilename, const char *pszUploadId,
780 size_t nPartIdsCount,
781 const char *const *apszPartIds,
782 vsi_l_offset nTotalSize,
783 CSLConstList papszOptions) override;
784
785 bool MultipartUploadAbort(const char *pszFilename, const char *pszUploadId,
786 CSLConstList papszOptions) override;
787};
788
789/************************************************************************/
790/* IVSIS3LikeHandle */
791/************************************************************************/
792
793class IVSIS3LikeHandle /* non final */ : public VSICurlHandle
794{
795 CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeHandle)
796
797 protected:
798 bool UseLimitRangeGetInsteadOfHead() override
799 {
800 return true;
801 }
802
803 bool IsDirectoryFromExists(const char *pszVerb, int response_code) override
804 {
805 // A bit dirty, but on S3, a GET on a existing directory returns a 416
806 return response_code == 416 && EQUAL(pszVerb, "GET") &&
807 std::string(m_pszURL).back() == '/';
808 }
809
810 void ProcessGetFileSizeResult(const char *pszContent) override
811 {
812 oFileProp.bIsDirectory =
813 strstr(pszContent, "ListBucketResult") != nullptr;
814 }
815
816 public:
817 IVSIS3LikeHandle(VSICurlFilesystemHandlerBase *poFSIn,
818 const char *pszFilename, const char *pszURLIn)
819 : VSICurlHandle(poFSIn, pszFilename, pszURLIn)
820 {
821 }
822
823 ~IVSIS3LikeHandle() override;
824};
825
826/************************************************************************/
827/* VSIMultipartWriteHandle */
828/************************************************************************/
829
830class VSIMultipartWriteHandle final : public VSIVirtualHandle
831{
832 CPL_DISALLOW_COPY_ASSIGN(VSIMultipartWriteHandle)
833
834 IVSIS3LikeFSHandlerWithMultipartUpload *m_poFS = nullptr;
835 std::string m_osFilename{};
836 IVSIS3LikeHandleHelper *m_poS3HandleHelper = nullptr;
837 CPLStringList m_aosOptions{};
838 CPLStringList m_aosHTTPOptions{};
839 CPLHTTPRetryParameters m_oRetryParameters;
840
841 vsi_l_offset m_nCurOffset = 0;
842 size_t m_nBufferOff = 0;
843 size_t m_nBufferSize = 0;
844 bool m_bClosed = false;
845 GByte *m_pabyBuffer = nullptr;
846 std::string m_osUploadID{};
847 int m_nPartNumber = 0;
848 std::vector<std::string> m_aosEtags{};
849 bool m_bError = false;
850
851 WriteFuncStruct m_sWriteFuncHeaderData{};
852
853 bool UploadPart();
854 bool DoSinglePartPUT();
855
856 void InvalidateParentDirectory();
857
858 public:
859 VSIMultipartWriteHandle(IVSIS3LikeFSHandlerWithMultipartUpload *poFS,
860 const char *pszFilename,
861 IVSIS3LikeHandleHelper *poS3HandleHelper,
862 CSLConstList papszOptions);
863 ~VSIMultipartWriteHandle() override;
864
865 int Seek(vsi_l_offset nOffset, int nWhence) override;
866 vsi_l_offset Tell() override;
867 size_t Read(void *pBuffer, size_t nBytes) override;
868 size_t Write(const void *pBuffer, size_t nBytes) override;
869
870 void ClearErr() override
871 {
872 }
873
874 int Error() override
875 {
876 return FALSE;
877 }
878
879 int Eof() override
880 {
881 return FALSE;
882 }
883
884 int Close() override;
885
886 bool IsOK()
887 {
888 return m_pabyBuffer != nullptr;
889 }
890};
891
892/************************************************************************/
893/* VSIChunkedWriteHandle() */
894/************************************************************************/
895
899class VSIChunkedWriteHandle final : public VSIVirtualHandle
900{
901 CPL_DISALLOW_COPY_ASSIGN(VSIChunkedWriteHandle)
902
903 IVSIS3LikeFSHandler *m_poFS = nullptr;
904 std::string m_osFilename{};
905 IVSIS3LikeHandleHelper *m_poS3HandleHelper = nullptr;
906 CPLStringList m_aosOptions{};
907 CPLStringList m_aosHTTPOptions{};
908 CPLHTTPRetryParameters m_oRetryParameters;
909
910 vsi_l_offset m_nCurOffset = 0;
911 size_t m_nBufferOff = 0;
912 bool m_bError = false;
913 bool m_bClosed = false;
914
915 CURLM *m_hCurlMulti = nullptr;
916 CURL *m_hCurl = nullptr;
917 const void *m_pBuffer = nullptr;
918 std::string m_osCurlErrBuf{};
919 size_t m_nChunkedBufferOff = 0;
920 size_t m_nChunkedBufferSize = 0;
921 size_t m_nWrittenInPUT = 0;
922
923 WriteFuncStruct m_sWriteFuncHeaderData{};
924
925 static size_t ReadCallBackBufferChunked(char *buffer, size_t size,
926 size_t nitems, void *instream);
927 int FinishChunkedTransfer();
928
929 bool DoEmptyPUT();
930
931 void InvalidateParentDirectory();
932
933 public:
934 VSIChunkedWriteHandle(IVSIS3LikeFSHandler *poFS, const char *pszFilename,
935 IVSIS3LikeHandleHelper *poS3HandleHelper,
936 CSLConstList papszOptions);
937 ~VSIChunkedWriteHandle() override;
938
939 int Seek(vsi_l_offset nOffset, int nWhence) override;
940 vsi_l_offset Tell() override;
941 size_t Read(void *pBuffer, size_t nBytes) override;
942 size_t Write(const void *pBuffer, size_t nBytes) override;
943
944 void ClearErr() override
945 {
946 }
947
948 int Error() override
949 {
950 return FALSE;
951 }
952
953 int Eof() override
954 {
955 return FALSE;
956 }
957
958 int Close() override;
959};
960
961/************************************************************************/
962/* VSIAppendWriteHandle */
963/************************************************************************/
964
965class VSIAppendWriteHandle CPL_NON_FINAL : public VSIVirtualHandle
966{
967 CPL_DISALLOW_COPY_ASSIGN(VSIAppendWriteHandle)
968
969 protected:
970 VSICurlFilesystemHandlerBase *m_poFS = nullptr;
971 std::string m_osFSPrefix{};
972 std::string m_osFilename{};
973 CPLHTTPRetryParameters m_oRetryParameters{};
974
975 vsi_l_offset m_nCurOffset = 0;
976 int m_nBufferOff = 0;
977 int m_nBufferSize = 0;
978 int m_nBufferOffReadCallback = 0;
979 bool m_bClosed = false;
980 GByte *m_pabyBuffer = nullptr;
981 bool m_bError = false;
982
983 static size_t ReadCallBackBuffer(char *buffer, size_t size, size_t nitems,
984 void *instream);
985 virtual bool Send(bool bIsLastBlock) = 0;
986
987 public:
988 VSIAppendWriteHandle(VSICurlFilesystemHandlerBase *poFS,
989 const char *pszFSPrefix, const char *pszFilename,
990 int nChunkSize);
991 ~VSIAppendWriteHandle() override;
992
993 int Seek(vsi_l_offset nOffset, int nWhence) override;
994 vsi_l_offset Tell() override;
995 size_t Read(void *pBuffer, size_t nBytes) override;
996 size_t Write(const void *pBuffer, size_t nBytes) override;
997
998 void ClearErr() override
999 {
1000 }
1001
1002 int Error() override
1003 {
1004 return FALSE;
1005 }
1006
1007 int Eof() override
1008 {
1009 return FALSE;
1010 }
1011
1012 int Close() override;
1013
1014 bool IsOK()
1015 {
1016 return m_pabyBuffer != nullptr;
1017 }
1018};
1019
1020/************************************************************************/
1021/* VSIDIRWithMissingDirSynthesis */
1022/************************************************************************/
1023
1024struct VSIDIRWithMissingDirSynthesis /* non final */ : public VSIDIR
1025{
1026 std::vector<std::unique_ptr<VSIDIREntry>> aoEntries{};
1027
1028 protected:
1029 ~VSIDIRWithMissingDirSynthesis() override;
1030
1031 std::vector<std::string> m_aosSubpathsStack{};
1032
1033 void SynthetizeMissingDirectories(const std::string &osCurSubdir,
1034 bool bAddEntryForThisSubdir);
1035};
1036
1037/************************************************************************/
1038/* VSIDIRS3Like */
1039/************************************************************************/
1040
1041struct VSIDIRS3Like /* non final */ : public VSIDIRWithMissingDirSynthesis
1042{
1043 const std::string m_osDirName;
1044
1045 int nRecurseDepth = 0;
1046
1047 std::string osNextMarker{};
1048 int nPos = 0;
1049
1050 std::string osBucket{};
1051 std::string osObjectKey{};
1052 VSICurlFilesystemHandlerBase *poFS = nullptr;
1053 IVSIS3LikeFSHandler *poS3FS = nullptr;
1054 std::unique_ptr<IVSIS3LikeHandleHelper> poHandleHelper{};
1055 int nMaxFiles = 0;
1056 bool bCacheEntries = true;
1057 bool m_bSynthetizeMissingDirectories = false;
1058 std::string m_osFilterPrefix{};
1059
1060 // used when listing only the file system prefix
1061 std::unique_ptr<VSIDIR, decltype(&VSICloseDir)> m_subdir{nullptr,
1062 VSICloseDir};
1063
1064 VSIDIRS3Like(const std::string &osDirName, IVSIS3LikeFSHandler *poFSIn)
1065 : m_osDirName(osDirName), poFS(poFSIn), poS3FS(poFSIn)
1066 {
1067 }
1068
1069 VSIDIRS3Like(const std::string &osDirName,
1070 VSICurlFilesystemHandlerBase *poFSIn)
1071 : m_osDirName(osDirName), poFS(poFSIn)
1072 {
1073 }
1074
1075 VSIDIRS3Like(const VSIDIRS3Like &) = delete;
1076 VSIDIRS3Like &operator=(const VSIDIRS3Like &) = delete;
1077
1078 const VSIDIREntry *NextDirEntry() override;
1079
1080 virtual bool IssueListDir() = 0;
1081 void clear();
1082};
1083
1084/************************************************************************/
1085/* CurlRequestHelper */
1086/************************************************************************/
1087
1088struct CurlRequestHelper
1089{
1090 WriteFuncStruct sWriteFuncData{};
1091 WriteFuncStruct sWriteFuncHeaderData{};
1092 char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
1093
1094 CurlRequestHelper();
1095 ~CurlRequestHelper();
1096 long perform(CURL *hCurlHandle,
1097 struct curl_slist *headers, // ownership transferred
1098 VSICurlFilesystemHandlerBase *poFS,
1099 IVSIS3LikeHandleHelper *poS3HandleHelper);
1100};
1101
1102/************************************************************************/
1103/* NetworkStatisticsLogger */
1104/************************************************************************/
1105
1106class NetworkStatisticsLogger
1107{
1108 static int gnEnabled;
1109 static NetworkStatisticsLogger gInstance;
1110
1111 NetworkStatisticsLogger() = default;
1112
1113 std::mutex m_mutex{};
1114
1115 struct Counters
1116 {
1117 GIntBig nHEAD = 0;
1118 GIntBig nGET = 0;
1119 GIntBig nPUT = 0;
1120 GIntBig nPOST = 0;
1121 GIntBig nDELETE = 0;
1122 GIntBig nGETDownloadedBytes = 0;
1123 GIntBig nPUTUploadedBytes = 0;
1124 GIntBig nPOSTDownloadedBytes = 0;
1125 GIntBig nPOSTUploadedBytes = 0;
1126 };
1127
1128 enum class ContextPathType
1129 {
1130 FILESYSTEM,
1131 FILE,
1132 ACTION,
1133 };
1134
1135 struct ContextPathItem
1136 {
1137 ContextPathType eType;
1138 std::string osName;
1139
1140 ContextPathItem(ContextPathType eTypeIn, const std::string &osNameIn)
1141 : eType(eTypeIn), osName(osNameIn)
1142 {
1143 }
1144
1145 bool operator<(const ContextPathItem &other) const
1146 {
1147 if (static_cast<int>(eType) < static_cast<int>(other.eType))
1148 return true;
1149 if (static_cast<int>(eType) > static_cast<int>(other.eType))
1150 return false;
1151 return osName < other.osName;
1152 }
1153 };
1154
1155 struct Stats
1156 {
1157 Counters counters{};
1158 std::map<ContextPathItem, Stats> children{};
1159
1160 void AsJSON(CPLJSONObject &oJSON) const;
1161 };
1162
1163 // Workaround bug in Coverity Scan
1164 // coverity[generated_default_constructor_used_in_field_initializer]
1165 Stats m_stats{};
1166 std::map<GIntBig, std::vector<ContextPathItem>>
1167 m_mapThreadIdToContextPath{};
1168
1169 static void ReadEnabled();
1170
1171 std::vector<Counters *> GetCountersForContext();
1172
1173 public:
1174 static inline bool IsEnabled()
1175 {
1176 if (gnEnabled < 0)
1177 {
1178 ReadEnabled();
1179 }
1180 return gnEnabled == TRUE;
1181 }
1182
1183 static void EnterFileSystem(const char *pszName);
1184
1185 static void LeaveFileSystem();
1186
1187 static void EnterFile(const char *pszName);
1188
1189 static void LeaveFile();
1190
1191 static void EnterAction(const char *pszName);
1192
1193 static void LeaveAction();
1194
1195 static void LogHEAD();
1196
1197 static void LogGET(size_t nDownloadedBytes);
1198
1199 static void LogPUT(size_t nUploadedBytes);
1200
1201 static void LogPOST(size_t nUploadedBytes, size_t nDownloadedBytes);
1202
1203 static void LogDELETE();
1204
1205 static void Reset();
1206
1207 static std::string GetReportAsSerializedJSON();
1208};
1209
1210struct NetworkStatisticsFileSystem
1211{
1212 inline explicit NetworkStatisticsFileSystem(const char *pszName)
1213 {
1214 NetworkStatisticsLogger::EnterFileSystem(pszName);
1215 }
1216
1217 inline ~NetworkStatisticsFileSystem()
1218 {
1219 NetworkStatisticsLogger::LeaveFileSystem();
1220 }
1221};
1222
1223struct NetworkStatisticsFile
1224{
1225 inline explicit NetworkStatisticsFile(const char *pszName)
1226 {
1227 NetworkStatisticsLogger::EnterFile(pszName);
1228 }
1229
1230 inline ~NetworkStatisticsFile()
1231 {
1232 NetworkStatisticsLogger::LeaveFile();
1233 }
1234};
1235
1236struct NetworkStatisticsAction
1237{
1238 inline explicit NetworkStatisticsAction(const char *pszName)
1239 {
1240 NetworkStatisticsLogger::EnterAction(pszName);
1241 }
1242
1243 inline ~NetworkStatisticsAction()
1244 {
1245 NetworkStatisticsLogger::LeaveAction();
1246 }
1247};
1248
1249} // namespace cpl
1250
1251int VSICURLGetDownloadChunkSize();
1252
1253void VSICURLInitWriteFuncStruct(cpl::WriteFuncStruct *psStruct, VSILFILE *fp,
1254 VSICurlReadCbkFunc pfnReadCbk,
1255 void *pReadCbkUserData);
1256size_t VSICurlHandleWriteFunc(void *buffer, size_t count, size_t nmemb,
1257 void *req);
1258void VSICURLMultiPerform(CURLM *hCurlMultiHandle, CURL *hEasyHandle = nullptr,
1259 std::atomic<bool> *pbInterrupt = nullptr);
1260void VSICURLResetHeaderAndWriterFunctions(CURL *hCurlHandle);
1261
1262int VSICurlParseUnixPermissions(const char *pszPermissions);
1263
1264// Cache of file properties (size, etc.)
1265bool VSICURLGetCachedFileProp(const char *pszURL, cpl::FileProp &oFileProp);
1266void VSICURLSetCachedFileProp(const char *pszURL, cpl::FileProp &oFileProp);
1267void VSICURLInvalidateCachedFileProp(const char *pszURL);
1268void VSICURLInvalidateCachedFilePropPrefix(const char *pszURL);
1269void VSICURLDestroyCacheFileProp();
1270
1271void VSICURLMultiCleanup(CURLM *hCurlMultiHandle);
1272
1274
1275#endif // HAVE_CURL
1276
1277#endif // CPL_VSIL_CURL_CLASS_H_INCLUDED
The CPLJSONArray class holds JSON object from CPLJSONDocument.
Definition cpl_json.h:60
String list class designed around our use of C "char**" string lists.
Definition cpl_string.h:476
Interface for downloading HTTP, FTP documents.
Interface for read and write JSON documents.
Core portability definitions for CPL.
#define CPL_NON_FINAL
Mark that a class is explicitly recognized as non-final.
Definition cpl_port.h:1094
#define EQUAL(a, b)
Alias for strcasecmp() == 0.
Definition cpl_port.h:532
#define CPL_DISALLOW_COPY_ASSIGN(ClassName)
Helper to remove the copy and assignment constructors so that the compiler will not generate the defa...
Definition cpl_port.h:1101
char ** CSLConstList
Type of a constant null-terminated list of nul terminated strings.
Definition cpl_port.h:1252
unsigned char GByte
Unsigned byte type.
Definition cpl_port.h:165
long long GIntBig
Large signed integer type (generally 64-bit integer type).
Definition cpl_port.h:195
Various convenience functions for working with strings and string lists.
#define VSIStatBufL
Type for VSIStatL()
Definition cpl_vsi.h:232
#define VSI_L_OFFSET_MAX
Maximum value for a file offset.
Definition cpl_vsi.h:138
struct VSIDIR VSIDIR
Opaque type for a directory iterator.
Definition cpl_vsi.h:433
void VSICloseDir(VSIDIR *dir)
Close a directory.
Definition cpl_vsil.cpp:599
GUIntBig vsi_l_offset
Type for a file offset.
Definition cpl_vsi.h:136
Directory entry.
Definition cpl_vsi.h:445
Virtual file handle.
Definition cpl_vsi_virtual.h:48