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 * Permission is hereby granted, free of charge, to any person obtaining a
11 * copy of this software and associated documentation files (the "Software"),
12 * to deal in the Software without restriction, including without limitation
13 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 * and/or sell copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 ****************************************************************************/
28
29#ifndef CPL_VSIL_CURL_CLASS_H_INCLUDED
30#define CPL_VSIL_CURL_CLASS_H_INCLUDED
31
32#ifdef HAVE_CURL
33
34#include "cpl_aws.h"
35#include "cpl_azure.h"
36#include "cpl_port.h"
37#include "cpl_json.h"
38#include "cpl_string.h"
39#include "cpl_vsil_curl_priv.h"
40#include "cpl_mem_cache.h"
41
42#include "cpl_curl_priv.h"
43
44#include <algorithm>
45#include <condition_variable>
46#include <set>
47#include <map>
48#include <memory>
49#include <mutex>
50#include <thread>
51#include <utility>
52
53// To avoid aliasing to CopyFile to CopyFileA on Windows
54#ifdef CopyFile
55#undef CopyFile
56#endif
57
59
60// Leave it for backward compatibility, but deprecate.
61#define HAVE_CURLINFO_REDIRECT_URL
62
63void VSICurlStreamingClearCache(void); // from cpl_vsil_curl_streaming.cpp
64
65struct curl_slist *VSICurlSetOptions(CURL *hCurlHandle, const char *pszURL,
66 const char *const *papszOptions);
67struct curl_slist *VSICurlMergeHeaders(struct curl_slist *poDest,
68 struct curl_slist *poSrcToDestroy);
69
70struct curl_slist *VSICurlSetContentTypeFromExt(struct curl_slist *polist,
71 const char *pszPath);
72
73struct curl_slist *VSICurlSetCreationHeadersFromOptions(
74 struct curl_slist *headers, CSLConstList papszOptions, const char *pszPath);
75
76namespace cpl
77{
78
79typedef enum
80{
81 EXIST_UNKNOWN = -1,
82 EXIST_NO,
83 EXIST_YES,
84} ExistStatus;
85
86class FileProp
87{
88 public:
89 unsigned int nGenerationAuthParameters = 0;
90 ExistStatus eExists = EXIST_UNKNOWN;
91 int nHTTPCode = 0;
92 vsi_l_offset fileSize = 0;
93 time_t mTime = 0;
94 time_t nExpireTimestampLocal = 0;
95 std::string osRedirectURL{};
96 bool bHasComputedFileSize = false;
97 bool bIsDirectory = false;
98 int nMode = 0; // st_mode member of struct stat
99 bool bS3LikeRedirect = false;
100 std::string ETag{};
101};
102
103struct CachedDirList
104{
105 bool bGotFileList = false;
106 unsigned int nGenerationAuthParameters = 0;
107 CPLStringList oFileList{}; /* only file name without path */
108};
109
110struct WriteFuncStruct
111{
112 char *pBuffer = nullptr;
113 size_t nSize = 0;
114 bool bIsHTTP = false;
115 bool bMultiRange = false;
116 vsi_l_offset nStartOffset = 0;
117 vsi_l_offset nEndOffset = 0;
118 int nHTTPCode = 0;
119 vsi_l_offset nContentLength = 0;
120 bool bFoundContentRange = false;
121 bool bError = false;
122 bool bInterruptDownload = false;
123 bool bDetectRangeDownloadingError = false;
124 GIntBig nTimestampDate = 0; // Corresponds to Date: header field
125
126 VSILFILE *fp = nullptr;
127 VSICurlReadCbkFunc pfnReadCbk = nullptr;
128 void *pReadCbkUserData = nullptr;
129 bool bInterrupted = false;
130};
131
132struct PutData
133{
134 const GByte *pabyData = nullptr;
135 size_t nOff = 0;
136 size_t nTotalSize = 0;
137
138 static size_t ReadCallBackBuffer(char *buffer, size_t size, size_t nitems,
139 void *instream)
140 {
141 PutData *poThis = static_cast<PutData *>(instream);
142 const size_t nSizeMax = size * nitems;
143 const size_t nSizeToWrite =
144 std::min(nSizeMax, poThis->nTotalSize - poThis->nOff);
145 memcpy(buffer, poThis->pabyData + poThis->nOff, nSizeToWrite);
146 poThis->nOff += nSizeToWrite;
147 return nSizeToWrite;
148 }
149};
150
151/************************************************************************/
152/* VSICurlFilesystemHandler */
153/************************************************************************/
154
155class VSICurlHandle;
156
157class VSICurlFilesystemHandlerBase : public VSIFilesystemHandler
158{
159 CPL_DISALLOW_COPY_ASSIGN(VSICurlFilesystemHandlerBase)
160
161 struct FilenameOffsetPair
162 {
163 std::string filename_;
164 vsi_l_offset offset_;
165
166 FilenameOffsetPair(const std::string &filename, vsi_l_offset offset)
167 : filename_(filename), offset_(offset)
168 {
169 }
170
171 bool operator==(const FilenameOffsetPair &other) const
172 {
173 return filename_ == other.filename_ && offset_ == other.offset_;
174 }
175 };
176
177 struct FilenameOffsetPairHasher
178 {
179 std::size_t operator()(const FilenameOffsetPair &k) const
180 {
181 return std::hash<std::string>()(k.filename_) ^
182 std::hash<vsi_l_offset>()(k.offset_);
183 }
184 };
185
186 using RegionCacheType = lru11::Cache<
187 FilenameOffsetPair, std::shared_ptr<std::string>, lru11::NullLock,
188 std::unordered_map<
189 FilenameOffsetPair,
190 typename std::list<lru11::KeyValuePair<
191 FilenameOffsetPair, std::shared_ptr<std::string>>>::iterator,
192 FilenameOffsetPairHasher>>;
193
194 std::unique_ptr<RegionCacheType>
195 m_poRegionCacheDoNotUseDirectly{}; // do not access directly. Use
196 // GetRegionCache();
197 RegionCacheType *GetRegionCache();
198
199 // LRU cache that just keeps in memory if this file system handler is
200 // spposed to know the file properties of a file. The actual cache is a
201 // shared one among all network file systems.
202 // The aim of that design is that invalidating /vsis3/foo results in
203 // /vsis3_streaming/foo to be invalidated as well.
204 lru11::Cache<std::string, bool> oCacheFileProp;
205
206 int nCachedFilesInDirList = 0;
207 lru11::Cache<std::string, CachedDirList> oCacheDirList;
208
209 char **ParseHTMLFileList(const char *pszFilename, int nMaxFiles,
210 char *pszData, bool *pbGotFileList);
211
212 // Data structure and map to store regions that are in progress, to
213 // avoid simultaneous downloads of the same region in different threads
214 // Cf https://github.com/OSGeo/gdal/issues/8041
215 struct RegionInDownload
216 {
217 std::mutex oMutex{};
218 std::condition_variable oCond{};
219 bool bDownloadInProgress = false;
220 int nWaiters = 0;
221 std::string osData{};
222 };
223
224 std::mutex m_oMutex{};
225 std::map<std::string, std::unique_ptr<RegionInDownload>>
226 m_oMapRegionInDownload{};
227
228 protected:
229 CPLMutex *hMutex = nullptr;
230
231 virtual VSICurlHandle *CreateFileHandle(const char *pszFilename);
232 virtual char **GetFileList(const char *pszFilename, int nMaxFiles,
233 bool *pbGotFileList);
234
235 void RegisterEmptyDir(const std::string &osDirname);
236
237 bool
238 AnalyseS3FileList(const std::string &osBaseURL, const char *pszXML,
239 CPLStringList &osFileList, int nMaxFiles,
240 const std::set<std::string> &oSetIgnoredStorageClasses,
241 bool &bIsTruncated);
242
243 void AnalyseSwiftFileList(const std::string &osBaseURL,
244 const std::string &osPrefix, const char *pszJson,
245 CPLStringList &osFileList, int nMaxFilesThisQuery,
246 int nMaxFiles, bool &bIsTruncated,
247 std::string &osNextMarker);
248
249 static const char *GetOptionsStatic();
250
251 VSICurlFilesystemHandlerBase();
252
253 public:
254 ~VSICurlFilesystemHandlerBase() override;
255
256 static bool IsAllowedFilename(const char *pszFilename);
257
258 VSIVirtualHandle *Open(const char *pszFilename, const char *pszAccess,
259 bool bSetError,
260 CSLConstList /* papszOptions */) override;
261
262 int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
263 int nFlags) override;
264 int Unlink(const char *pszFilename) override;
265 int Rename(const char *oldpath, const char *newpath) override;
266 int Mkdir(const char *pszDirname, long nMode) override;
267 int Rmdir(const char *pszDirname) override;
268 char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
269 char **SiblingFiles(const char *pszFilename) override;
270
271 int HasOptimizedReadMultiRange(const char * /* pszPath */) override
272 {
273 return true;
274 }
275
276 const char *GetActualURL(const char *pszFilename) override;
277
278 const char *GetOptions() override;
279
280 char **GetFileMetadata(const char *pszFilename, const char *pszDomain,
281 CSLConstList papszOptions) override;
282
283 char **ReadDirInternal(const char *pszDirname, int nMaxFiles,
284 bool *pbGotFileList);
285 void InvalidateDirContent(const char *pszDirname);
286
287 virtual const char *GetDebugKey() const = 0;
288
289 virtual std::string GetFSPrefix() const = 0;
290 virtual bool AllowCachedDataFor(const char *pszFilename);
291
292 virtual bool IsLocal(const char * /* pszPath */) override
293 {
294 return false;
295 }
296
297 virtual bool
298 SupportsSequentialWrite(const char * /* pszPath */,
299 bool /* bAllowLocalTempFile */) override
300 {
301 return false;
302 }
303
304 virtual bool SupportsRandomWrite(const char * /* pszPath */,
305 bool /* bAllowLocalTempFile */) override
306 {
307 return false;
308 }
309
310 std::shared_ptr<std::string> GetRegion(const char *pszURL,
311 vsi_l_offset nFileOffsetStart);
312
313 void AddRegion(const char *pszURL, vsi_l_offset nFileOffsetStart,
314 size_t nSize, const char *pData);
315
316 std::pair<bool, std::string>
317 NotifyStartDownloadRegion(const std::string &osURL,
318 vsi_l_offset startOffset, int nBlocks);
319 void NotifyStopDownloadRegion(const std::string &osURL,
320 vsi_l_offset startOffset, int nBlocks,
321 const std::string &osData);
322
323 bool GetCachedFileProp(const char *pszURL, FileProp &oFileProp);
324 void SetCachedFileProp(const char *pszURL, FileProp &oFileProp);
325 void InvalidateCachedData(const char *pszURL);
326
327 CURLM *GetCurlMultiHandleFor(const std::string &osURL);
328
329 virtual void ClearCache();
330 virtual void PartialClearCache(const char *pszFilename);
331
332 bool GetCachedDirList(const char *pszURL, CachedDirList &oCachedDirList);
333 void SetCachedDirList(const char *pszURL, CachedDirList &oCachedDirList);
334 bool ExistsInCacheDirList(const std::string &osDirname, bool *pbIsDir);
335
336 virtual std::string GetURLFromFilename(const std::string &osFilename);
337
338 std::string
339 GetStreamingFilename(const std::string &osFilename) const override = 0;
340
341 static std::set<std::string> GetS3IgnoredStorageClasses();
342};
343
344class VSICurlFilesystemHandler : public VSICurlFilesystemHandlerBase
345{
346 CPL_DISALLOW_COPY_ASSIGN(VSICurlFilesystemHandler)
347
348 public:
349 VSICurlFilesystemHandler() = default;
350
351 const char *GetDebugKey() const override
352 {
353 return "VSICURL";
354 }
355
356 std::string GetFSPrefix() const override
357 {
358 return "/vsicurl/";
359 }
360
361 std::string
362 GetStreamingFilename(const std::string &osFilename) const override;
363};
364
365/************************************************************************/
366/* VSICurlHandle */
367/************************************************************************/
368
369class VSICurlHandle : public VSIVirtualHandle
370{
371 CPL_DISALLOW_COPY_ASSIGN(VSICurlHandle)
372
373 protected:
374 VSICurlFilesystemHandlerBase *poFS = nullptr;
375
376 bool m_bCached = true;
377
378 mutable FileProp oFileProp{};
379
380 mutable std::mutex m_oMutex{};
381 std::string m_osFilename{}; // e.g "/vsicurl/http://example.com/foo"
382 char *m_pszURL = nullptr; // e.g "http://example.com/foo"
383 mutable std::string m_osQueryString{}; // e.g. an Azure SAS
384
385 char **m_papszHTTPOptions = nullptr;
386
387 vsi_l_offset lastDownloadedOffset = VSI_L_OFFSET_MAX;
388 int nBlocksToDownload = 1;
389
390 bool bStopOnInterruptUntilUninstall = false;
391 bool bInterrupted = false;
392 VSICurlReadCbkFunc pfnReadCbk = nullptr;
393 void *pReadCbkUserData = nullptr;
394
395 int m_nMaxRetry = 0;
396 double m_dfRetryDelay = 0.0;
397
398 CPLStringList m_aosHeaders{};
399
400 void DownloadRegionPostProcess(const vsi_l_offset startOffset,
401 const int nBlocks, const char *pBuffer,
402 size_t nSize);
403
404 private:
405 vsi_l_offset curOffset = 0;
406
407 bool bEOF = false;
408
409 virtual std::string DownloadRegion(vsi_l_offset startOffset, int nBlocks);
410
411 bool m_bUseHead = false;
412 bool m_bUseRedirectURLIfNoQueryStringParams = false;
413
414 // Specific to Planetary Computer signing:
415 // https://planetarycomputer.microsoft.com/docs/concepts/sas/
416 mutable bool m_bPlanetaryComputerURLSigning = false;
417 mutable std::string m_osPlanetaryComputerCollection{};
418 void ManagePlanetaryComputerSigning() const;
419
420 int ReadMultiRangeSingleGet(int nRanges, void **ppData,
421 const vsi_l_offset *panOffsets,
422 const size_t *panSizes);
423 std::string GetRedirectURLIfValid(bool &bHasExpired) const;
424
425 void UpdateRedirectInfo(CURL *hCurlHandle,
426 const WriteFuncStruct &sWriteFuncHeaderData);
427
428 // Used by AdviseRead()
429 struct AdviseReadRange
430 {
431 bool bDone = false;
432 std::mutex oMutex{};
433 std::condition_variable oCV{};
434 vsi_l_offset nStartOffset = 0;
435 size_t nSize = 0;
436 std::vector<GByte> abyData{};
437 };
438
439 std::vector<std::unique_ptr<AdviseReadRange>> m_aoAdviseReadRanges{};
440 std::thread m_oThreadAdviseRead{};
441
442 protected:
443 virtual struct curl_slist *
444 GetCurlHeaders(const std::string & /*osVerb*/,
445 const struct curl_slist * /* psExistingHeaders */)
446 {
447 return nullptr;
448 }
449
450 virtual bool AllowAutomaticRedirection()
451 {
452 return true;
453 }
454
455 virtual bool CanRestartOnError(const char *, const char *, bool)
456 {
457 return false;
458 }
459
460 virtual bool UseLimitRangeGetInsteadOfHead()
461 {
462 return false;
463 }
464
465 virtual bool IsDirectoryFromExists(const char * /*pszVerb*/,
466 int /*response_code*/)
467 {
468 return false;
469 }
470
471 virtual void ProcessGetFileSizeResult(const char * /* pszContent */)
472 {
473 }
474
475 void SetURL(const char *pszURL);
476
477 virtual bool Authenticate(const char * /* pszFilename */)
478 {
479 return false;
480 }
481
482 public:
483 VSICurlHandle(VSICurlFilesystemHandlerBase *poFS, const char *pszFilename,
484 const char *pszURLIn = nullptr);
485 ~VSICurlHandle() override;
486
487 int Seek(vsi_l_offset nOffset, int nWhence) override;
488 vsi_l_offset Tell() override;
489 size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
490 int ReadMultiRange(int nRanges, void **ppData,
491 const vsi_l_offset *panOffsets,
492 const size_t *panSizes) override;
493 size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
494 int Eof() override;
495 int Flush() override;
496 int Close() override;
497
498 bool HasPRead() const override
499 {
500 return true;
501 }
502
503 size_t PRead(void *pBuffer, size_t nSize,
504 vsi_l_offset nOffset) const override;
505
506 void AdviseRead(int nRanges, const vsi_l_offset *panOffsets,
507 const size_t *panSizes) override;
508
509 size_t GetAdviseReadTotalBytesLimit() const override;
510
511 bool IsKnownFileSize() const
512 {
513 return oFileProp.bHasComputedFileSize;
514 }
515
516 vsi_l_offset GetFileSizeOrHeaders(bool bSetError, bool bGetHeaders);
517
518 virtual vsi_l_offset GetFileSize(bool bSetError)
519 {
520 return GetFileSizeOrHeaders(bSetError, false);
521 }
522
523 bool Exists(bool bSetError);
524
525 bool IsDirectory() const
526 {
527 return oFileProp.bIsDirectory;
528 }
529
530 int GetMode() const
531 {
532 return oFileProp.nMode;
533 }
534
535 time_t GetMTime() const
536 {
537 return oFileProp.mTime;
538 }
539
540 const CPLStringList &GetHeaders()
541 {
542 return m_aosHeaders;
543 }
544
545 int InstallReadCbk(VSICurlReadCbkFunc pfnReadCbk, void *pfnUserData,
546 int bStopOnInterruptUntilUninstall);
547 int UninstallReadCbk();
548
549 const char *GetURL() const
550 {
551 return m_pszURL;
552 }
553};
554
555/************************************************************************/
556/* VSICurlFilesystemHandlerBaseWritable */
557/************************************************************************/
558
559class VSICurlFilesystemHandlerBaseWritable : public VSICurlFilesystemHandlerBase
560{
561 CPL_DISALLOW_COPY_ASSIGN(VSICurlFilesystemHandlerBaseWritable)
562
563 protected:
564 VSICurlFilesystemHandlerBaseWritable() = default;
565
566 virtual VSIVirtualHandleUniquePtr
567 CreateWriteHandle(const char *pszFilename, CSLConstList papszOptions) = 0;
568
569 public:
570 VSIVirtualHandle *Open(const char *pszFilename, const char *pszAccess,
571 bool bSetError, CSLConstList papszOptions) override;
572
573 bool SupportsSequentialWrite(const char * /* pszPath */,
574 bool /* bAllowLocalTempFile */) override
575 {
576 return true;
577 }
578
579 bool SupportsRandomWrite(const char * /* pszPath */,
580 bool /* bAllowLocalTempFile */) override;
581};
582
583/************************************************************************/
584/* IVSIS3LikeFSHandler */
585/************************************************************************/
586
587class IVSIS3LikeFSHandler : public VSICurlFilesystemHandlerBaseWritable
588{
589 CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeFSHandler)
590
591 virtual int MkdirInternal(const char *pszDirname, long nMode,
592 bool bDoStatCheck);
593
594 protected:
595 char **GetFileList(const char *pszFilename, int nMaxFiles,
596 bool *pbGotFileList) override;
597
598 virtual IVSIS3LikeHandleHelper *CreateHandleHelper(const char *pszURI,
599 bool bAllowNoObject) = 0;
600
601 virtual int CopyObject(const char *oldpath, const char *newpath,
602 CSLConstList papszMetadata);
603
604 int RmdirRecursiveInternal(const char *pszDirname, int nBatchSize);
605
606 virtual bool
607 IsAllowedHeaderForObjectCreation(const char * /* pszHeaderName */)
608 {
609 return false;
610 }
611
612 IVSIS3LikeFSHandler() = default;
613
614 public:
615 int Unlink(const char *pszFilename) override;
616 int Mkdir(const char *pszDirname, long nMode) override;
617 int Rmdir(const char *pszDirname) override;
618 int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
619 int nFlags) override;
620 int Rename(const char *oldpath, const char *newpath) override;
621
622 virtual int CopyFile(const char *pszSource, const char *pszTarget,
623 VSILFILE *fpSource, vsi_l_offset nSourceSize,
624 const char *const *papszOptions,
625 GDALProgressFunc pProgressFunc,
626 void *pProgressData) override;
627
628 virtual int DeleteObject(const char *pszFilename);
629
630 bool Sync(const char *pszSource, const char *pszTarget,
631 const char *const *papszOptions, GDALProgressFunc pProgressFunc,
632 void *pProgressData, char ***ppapszOutputs) override;
633
634 VSIDIR *OpenDir(const char *pszPath, int nRecurseDepth,
635 const char *const *papszOptions) override;
636
637 // Multipart upload
638 virtual bool SupportsParallelMultipartUpload() const
639 {
640 return false;
641 }
642
643 virtual std::string InitiateMultipartUpload(
644 const std::string &osFilename, IVSIS3LikeHandleHelper *poS3HandleHelper,
645 int nMaxRetry, double dfRetryDelay, CSLConstList papszOptions);
646 virtual std::string
647 UploadPart(const std::string &osFilename, int nPartNumber,
648 const std::string &osUploadID, vsi_l_offset nPosition,
649 const void *pabyBuffer, size_t nBufferSize,
650 IVSIS3LikeHandleHelper *poS3HandleHelper, int nMaxRetry,
651 double dfRetryDelay, CSLConstList papszOptions);
652 virtual bool CompleteMultipart(const std::string &osFilename,
653 const std::string &osUploadID,
654 const std::vector<std::string> &aosEtags,
655 vsi_l_offset nTotalSize,
656 IVSIS3LikeHandleHelper *poS3HandleHelper,
657 int nMaxRetry, double dfRetryDelay);
658 virtual bool AbortMultipart(const std::string &osFilename,
659 const std::string &osUploadID,
660 IVSIS3LikeHandleHelper *poS3HandleHelper,
661 int nMaxRetry, double dfRetryDelay);
662
663 bool AbortPendingUploads(const char *pszFilename) override;
664};
665
666/************************************************************************/
667/* IVSIS3LikeHandle */
668/************************************************************************/
669
670class IVSIS3LikeHandle : public VSICurlHandle
671{
672 CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeHandle)
673
674 protected:
675 bool UseLimitRangeGetInsteadOfHead() override
676 {
677 return true;
678 }
679
680 bool IsDirectoryFromExists(const char *pszVerb, int response_code) override
681 {
682 // A bit dirty, but on S3, a GET on a existing directory returns a 416
683 return response_code == 416 && EQUAL(pszVerb, "GET") &&
684 std::string(m_pszURL).back() == '/';
685 }
686
687 void ProcessGetFileSizeResult(const char *pszContent) override
688 {
689 oFileProp.bIsDirectory =
690 strstr(pszContent, "ListBucketResult") != nullptr;
691 }
692
693 public:
694 IVSIS3LikeHandle(VSICurlFilesystemHandlerBase *poFSIn,
695 const char *pszFilename, const char *pszURLIn)
696 : VSICurlHandle(poFSIn, pszFilename, pszURLIn)
697 {
698 }
699
700 ~IVSIS3LikeHandle() override
701 {
702 }
703};
704
705/************************************************************************/
706/* VSIS3WriteHandle */
707/************************************************************************/
708
709class VSIS3WriteHandle final : public VSIVirtualHandle
710{
711 CPL_DISALLOW_COPY_ASSIGN(VSIS3WriteHandle)
712
713 IVSIS3LikeFSHandler *m_poFS = nullptr;
714 std::string m_osFilename{};
715 IVSIS3LikeHandleHelper *m_poS3HandleHelper = nullptr;
716 bool m_bUseChunked = false;
717 CPLStringList m_aosOptions{};
718 CPLStringList m_aosHTTPOptions{};
719
720 vsi_l_offset m_nCurOffset = 0;
721 int m_nBufferOff = 0;
722 int m_nBufferSize = 0;
723 bool m_bClosed = false;
724 GByte *m_pabyBuffer = nullptr;
725 std::string m_osUploadID{};
726 int m_nPartNumber = 0;
727 std::vector<std::string> m_aosEtags{};
728 bool m_bError = false;
729
730 CURLM *m_hCurlMulti = nullptr;
731 CURL *m_hCurl = nullptr;
732 const void *m_pBuffer = nullptr;
733 std::string m_osCurlErrBuf{};
734 size_t m_nChunkedBufferOff = 0;
735 size_t m_nChunkedBufferSize = 0;
736 size_t m_nWrittenInPUT = 0;
737
738 int m_nMaxRetry = 0;
739 double m_dfRetryDelay = 0.0;
740 WriteFuncStruct m_sWriteFuncHeaderData{};
741
742 bool UploadPart();
743 bool DoSinglePartPUT();
744
745 static size_t ReadCallBackBufferChunked(char *buffer, size_t size,
746 size_t nitems, void *instream);
747 size_t WriteChunked(const void *pBuffer, size_t nSize, size_t nMemb);
748 int FinishChunkedTransfer();
749
750 void InvalidateParentDirectory();
751
752 public:
753 VSIS3WriteHandle(IVSIS3LikeFSHandler *poFS, const char *pszFilename,
754 IVSIS3LikeHandleHelper *poS3HandleHelper, bool bUseChunked,
755 CSLConstList papszOptions);
756 ~VSIS3WriteHandle() override;
757
758 int Seek(vsi_l_offset nOffset, int nWhence) override;
759 vsi_l_offset Tell() override;
760 size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
761 size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
762 int Eof() override;
763 int Close() override;
764
765 bool IsOK()
766 {
767 return m_bUseChunked || m_pabyBuffer != nullptr;
768 }
769};
770
771/************************************************************************/
772/* VSIAppendWriteHandle */
773/************************************************************************/
774
775class VSIAppendWriteHandle : public VSIVirtualHandle
776{
777 CPL_DISALLOW_COPY_ASSIGN(VSIAppendWriteHandle)
778
779 protected:
780 VSICurlFilesystemHandlerBase *m_poFS = nullptr;
781 std::string m_osFSPrefix{};
782 std::string m_osFilename{};
783
784 vsi_l_offset m_nCurOffset = 0;
785 int m_nBufferOff = 0;
786 int m_nBufferSize = 0;
787 int m_nBufferOffReadCallback = 0;
788 bool m_bClosed = false;
789 GByte *m_pabyBuffer = nullptr;
790 bool m_bError = false;
791
792 static size_t ReadCallBackBuffer(char *buffer, size_t size, size_t nitems,
793 void *instream);
794 virtual bool Send(bool bIsLastBlock) = 0;
795
796 public:
797 VSIAppendWriteHandle(VSICurlFilesystemHandlerBase *poFS,
798 const char *pszFSPrefix, const char *pszFilename,
799 int nChunkSize);
800 virtual ~VSIAppendWriteHandle();
801
802 int Seek(vsi_l_offset nOffset, int nWhence) override;
803 vsi_l_offset Tell() override;
804 size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
805 size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
806 int Eof() override;
807 int Close() override;
808
809 bool IsOK()
810 {
811 return m_pabyBuffer != nullptr;
812 }
813};
814
815/************************************************************************/
816/* VSIDIRWithMissingDirSynthesis */
817/************************************************************************/
818
819struct VSIDIRWithMissingDirSynthesis : public VSIDIR
820{
821 std::vector<std::unique_ptr<VSIDIREntry>> aoEntries{};
822
823 protected:
824 std::vector<std::string> m_aosSubpathsStack{};
825
826 void SynthetizeMissingDirectories(const std::string &osCurSubdir,
827 bool bAddEntryForThisSubdir);
828};
829
830/************************************************************************/
831/* CurlRequestHelper */
832/************************************************************************/
833
834struct CurlRequestHelper
835{
836 WriteFuncStruct sWriteFuncData{};
837 WriteFuncStruct sWriteFuncHeaderData{};
838 char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
839
840 CurlRequestHelper();
841 ~CurlRequestHelper();
842 long perform(CURL *hCurlHandle,
843 struct curl_slist *headers, // ownership transferred
844 VSICurlFilesystemHandlerBase *poFS,
845 IVSIS3LikeHandleHelper *poS3HandleHelper);
846};
847
848/************************************************************************/
849/* NetworkStatisticsLogger */
850/************************************************************************/
851
852class NetworkStatisticsLogger
853{
854 static int gnEnabled;
855 static NetworkStatisticsLogger gInstance;
856
857 NetworkStatisticsLogger() = default;
858
859 std::mutex m_mutex{};
860
861 struct Counters
862 {
863 GIntBig nHEAD = 0;
864 GIntBig nGET = 0;
865 GIntBig nPUT = 0;
866 GIntBig nPOST = 0;
867 GIntBig nDELETE = 0;
868 GIntBig nGETDownloadedBytes = 0;
869 GIntBig nPUTUploadedBytes = 0;
870 GIntBig nPOSTDownloadedBytes = 0;
871 GIntBig nPOSTUploadedBytes = 0;
872 };
873
874 enum class ContextPathType
875 {
876 FILESYSTEM,
877 FILE,
878 ACTION,
879 };
880
881 struct ContextPathItem
882 {
883 ContextPathType eType;
884 std::string osName;
885
886 ContextPathItem(ContextPathType eTypeIn, const std::string &osNameIn)
887 : eType(eTypeIn), osName(osNameIn)
888 {
889 }
890
891 bool operator<(const ContextPathItem &other) const
892 {
893 if (static_cast<int>(eType) < static_cast<int>(other.eType))
894 return true;
895 if (static_cast<int>(eType) > static_cast<int>(other.eType))
896 return false;
897 return osName < other.osName;
898 }
899 };
900
901 struct Stats
902 {
903 Counters counters{};
904 std::map<ContextPathItem, Stats> children{};
905
906 void AsJSON(CPLJSONObject &oJSON) const;
907 };
908
909 // Workaround bug in Coverity Scan
910 // coverity[generated_default_constructor_used_in_field_initializer]
911 Stats m_stats{};
912 std::map<GIntBig, std::vector<ContextPathItem>>
913 m_mapThreadIdToContextPath{};
914
915 static void ReadEnabled();
916
917 std::vector<Counters *> GetCountersForContext();
918
919 public:
920 static inline bool IsEnabled()
921 {
922 if (gnEnabled < 0)
923 {
924 ReadEnabled();
925 }
926 return gnEnabled == TRUE;
927 }
928
929 static void EnterFileSystem(const char *pszName);
930
931 static void LeaveFileSystem();
932
933 static void EnterFile(const char *pszName);
934
935 static void LeaveFile();
936
937 static void EnterAction(const char *pszName);
938
939 static void LeaveAction();
940
941 static void LogHEAD();
942
943 static void LogGET(size_t nDownloadedBytes);
944
945 static void LogPUT(size_t nUploadedBytes);
946
947 static void LogPOST(size_t nUploadedBytes, size_t nDownloadedBytes);
948
949 static void LogDELETE();
950
951 static void Reset();
952
953 static std::string GetReportAsSerializedJSON();
954};
955
956struct NetworkStatisticsFileSystem
957{
958 inline explicit NetworkStatisticsFileSystem(const char *pszName)
959 {
960 NetworkStatisticsLogger::EnterFileSystem(pszName);
961 }
962
963 inline ~NetworkStatisticsFileSystem()
964 {
965 NetworkStatisticsLogger::LeaveFileSystem();
966 }
967};
968
969struct NetworkStatisticsFile
970{
971 inline explicit NetworkStatisticsFile(const char *pszName)
972 {
973 NetworkStatisticsLogger::EnterFile(pszName);
974 }
975
976 inline ~NetworkStatisticsFile()
977 {
978 NetworkStatisticsLogger::LeaveFile();
979 }
980};
981
982struct NetworkStatisticsAction
983{
984 inline explicit NetworkStatisticsAction(const char *pszName)
985 {
986 NetworkStatisticsLogger::EnterAction(pszName);
987 }
988
989 inline ~NetworkStatisticsAction()
990 {
991 NetworkStatisticsLogger::LeaveAction();
992 }
993};
994
995} // namespace cpl
996
997int VSICURLGetDownloadChunkSize();
998
999void VSICURLInitWriteFuncStruct(cpl::WriteFuncStruct *psStruct, VSILFILE *fp,
1000 VSICurlReadCbkFunc pfnReadCbk,
1001 void *pReadCbkUserData);
1002size_t VSICurlHandleWriteFunc(void *buffer, size_t count, size_t nmemb,
1003 void *req);
1004void VSICURLMultiPerform(CURLM *hCurlMultiHandle, CURL *hEasyHandle = nullptr);
1005void VSICURLResetHeaderAndWriterFunctions(CURL *hCurlHandle);
1006
1007int VSICurlParseUnixPermissions(const char *pszPermissions);
1008
1009// Cache of file properties (size, etc.)
1010bool VSICURLGetCachedFileProp(const char *pszURL, cpl::FileProp &oFileProp);
1011void VSICURLSetCachedFileProp(const char *pszURL, cpl::FileProp &oFileProp);
1012void VSICURLInvalidateCachedFileProp(const char *pszURL);
1013void VSICURLInvalidateCachedFilePropPrefix(const char *pszURL);
1014void VSICURLDestroyCacheFileProp();
1015
1016void VSICURLMultiCleanup(CURLM *hCurlMultiHandle);
1017
1019
1020#endif // HAVE_CURL
1021
1022#endif // CPL_VSIL_CURL_CLASS_H_INCLUDED
The CPLJSONArray class holds JSON object from CPLJSONDocument.
Definition: cpl_json.h:57
String list class designed around our use of C "char**" string lists.
Definition: cpl_string.h:449
Interface for read and write JSON documents.
Core portability definitions for CPL.
#define EQUAL(a, b)
Alias for strcasecmp() == 0.
Definition: cpl_port.h:551
#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:1042
char ** CSLConstList
Type of a constant null-terminated list of nul terminated strings.
Definition: cpl_port.h:1183
unsigned char GByte
Unsigned byte type.
Definition: cpl_port.h:185
long long GIntBig
Large signed integer type (generally 64-bit integer type).
Definition: cpl_port.h:215
Various convenience functions for working with strings and string lists.
#define VSIStatBufL
Type for VSIStatL()
Definition: cpl_vsi.h:203
#define VSI_L_OFFSET_MAX
Maximum value for a file offset.
Definition: cpl_vsi.h:148
struct VSIDIR VSIDIR
Opaque type for a directory iterator.
Definition: cpl_vsi.h:402
GUIntBig vsi_l_offset
Type for a file offset.
Definition: cpl_vsi.h:146
Virtual file handle.
Definition: cpl_vsi_virtual.h:63