Line data Source code
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 :
58 : //! @cond Doxygen_Suppress
59 :
60 : // Leave it for backward compatibility, but deprecate.
61 : #define HAVE_CURLINFO_REDIRECT_URL
62 :
63 : void VSICurlStreamingClearCache(void); // from cpl_vsil_curl_streaming.cpp
64 :
65 : struct curl_slist *VSICurlSetOptions(CURL *hCurlHandle, const char *pszURL,
66 : const char *const *papszOptions);
67 : struct curl_slist *VSICurlMergeHeaders(struct curl_slist *poDest,
68 : struct curl_slist *poSrcToDestroy);
69 :
70 : struct curl_slist *VSICurlSetContentTypeFromExt(struct curl_slist *polist,
71 : const char *pszPath);
72 :
73 : struct curl_slist *VSICurlSetCreationHeadersFromOptions(
74 : struct curl_slist *headers, CSLConstList papszOptions, const char *pszPath);
75 :
76 : namespace cpl
77 : {
78 :
79 : typedef enum
80 : {
81 : EXIST_UNKNOWN = -1,
82 : EXIST_NO,
83 : EXIST_YES,
84 : } ExistStatus;
85 :
86 : class 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 :
103 : struct CachedDirList
104 : {
105 : bool bGotFileList = false;
106 : unsigned int nGenerationAuthParameters = 0;
107 : CPLStringList oFileList{}; /* only file name without path */
108 : };
109 :
110 : struct 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 :
132 : struct PutData
133 : {
134 : const GByte *pabyData = nullptr;
135 : size_t nOff = 0;
136 : size_t nTotalSize = 0;
137 :
138 22 : static size_t ReadCallBackBuffer(char *buffer, size_t size, size_t nitems,
139 : void *instream)
140 : {
141 22 : PutData *poThis = static_cast<PutData *>(instream);
142 22 : const size_t nSizeMax = size * nitems;
143 : const size_t nSizeToWrite =
144 22 : std::min(nSizeMax, poThis->nTotalSize - poThis->nOff);
145 22 : memcpy(buffer, poThis->pabyData + poThis->nOff, nSizeToWrite);
146 22 : poThis->nOff += nSizeToWrite;
147 22 : return nSizeToWrite;
148 : }
149 : };
150 :
151 : /************************************************************************/
152 : /* VSICurlFilesystemHandler */
153 : /************************************************************************/
154 :
155 : class VSICurlHandle;
156 :
157 : class 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 45878 : FilenameOffsetPair(const std::string &filename, vsi_l_offset offset)
167 45878 : : filename_(filename), offset_(offset)
168 : {
169 45878 : }
170 :
171 45275 : bool operator==(const FilenameOffsetPair &other) const
172 : {
173 45275 : return filename_ == other.filename_ && offset_ == other.offset_;
174 : }
175 : };
176 :
177 : struct FilenameOffsetPairHasher
178 : {
179 46224 : std::size_t operator()(const FilenameOffsetPair &k) const
180 : {
181 46224 : return std::hash<std::string>()(k.filename_) ^
182 46224 : 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 12 : int HasOptimizedReadMultiRange(const char * /* pszPath */) override
272 : {
273 12 : 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 1 : virtual bool IsLocal(const char * /* pszPath */) override
293 : {
294 1 : return false;
295 : }
296 :
297 : virtual bool
298 2 : SupportsSequentialWrite(const char * /* pszPath */,
299 : bool /* bAllowLocalTempFile */) override
300 : {
301 2 : return false;
302 : }
303 :
304 1 : virtual bool SupportsRandomWrite(const char * /* pszPath */,
305 : bool /* bAllowLocalTempFile */) override
306 : {
307 1 : 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 :
344 : class VSICurlFilesystemHandler : public VSICurlFilesystemHandlerBase
345 : {
346 : CPL_DISALLOW_COPY_ASSIGN(VSICurlFilesystemHandler)
347 :
348 : public:
349 1227 : VSICurlFilesystemHandler() = default;
350 :
351 401 : const char *GetDebugKey() const override
352 : {
353 401 : return "VSICURL";
354 : }
355 :
356 46430 : std::string GetFSPrefix() const override
357 : {
358 46430 : return "/vsicurl/";
359 : }
360 :
361 : std::string
362 : GetStreamingFilename(const std::string &osFilename) const override;
363 : };
364 :
365 : /************************************************************************/
366 : /* VSICurlHandle */
367 : /************************************************************************/
368 :
369 : class 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 290 : GetCurlHeaders(const std::string & /*osVerb*/,
445 : const struct curl_slist * /* psExistingHeaders */)
446 : {
447 290 : return nullptr;
448 : }
449 :
450 359 : virtual bool AllowAutomaticRedirection()
451 : {
452 359 : return true;
453 : }
454 :
455 1 : virtual bool CanRestartOnError(const char *, const char *, bool)
456 : {
457 1 : return false;
458 : }
459 :
460 410 : virtual bool UseLimitRangeGetInsteadOfHead()
461 : {
462 410 : return false;
463 : }
464 :
465 136 : virtual bool IsDirectoryFromExists(const char * /*pszVerb*/,
466 : int /*response_code*/)
467 : {
468 136 : return false;
469 : }
470 :
471 56 : virtual void ProcessGetFileSizeResult(const char * /* pszContent */)
472 : {
473 56 : }
474 :
475 : void SetURL(const char *pszURL);
476 :
477 0 : virtual bool Authenticate(const char * /* pszFilename */)
478 : {
479 0 : 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 12 : bool HasPRead() const override
499 : {
500 12 : 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 446 : bool IsKnownFileSize() const
512 : {
513 446 : return oFileProp.bHasComputedFileSize;
514 : }
515 :
516 : vsi_l_offset GetFileSizeOrHeaders(bool bSetError, bool bGetHeaders);
517 :
518 738 : virtual vsi_l_offset GetFileSize(bool bSetError)
519 : {
520 738 : return GetFileSizeOrHeaders(bSetError, false);
521 : }
522 :
523 : bool Exists(bool bSetError);
524 :
525 561 : bool IsDirectory() const
526 : {
527 561 : return oFileProp.bIsDirectory;
528 : }
529 :
530 446 : int GetMode() const
531 : {
532 446 : return oFileProp.nMode;
533 : }
534 :
535 446 : time_t GetMTime() const
536 : {
537 446 : return oFileProp.mTime;
538 : }
539 :
540 4 : const CPLStringList &GetHeaders()
541 : {
542 4 : return m_aosHeaders;
543 : }
544 :
545 : int InstallReadCbk(VSICurlReadCbkFunc pfnReadCbk, void *pfnUserData,
546 : int bStopOnInterruptUntilUninstall);
547 : int UninstallReadCbk();
548 :
549 5 : const char *GetURL() const
550 : {
551 5 : return m_pszURL;
552 : }
553 : };
554 :
555 : /************************************************************************/
556 : /* VSICurlFilesystemHandlerBaseWritable */
557 : /************************************************************************/
558 :
559 : class VSICurlFilesystemHandlerBaseWritable : public VSICurlFilesystemHandlerBase
560 : {
561 : CPL_DISALLOW_COPY_ASSIGN(VSICurlFilesystemHandlerBaseWritable)
562 :
563 : protected:
564 8589 : 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 1 : bool SupportsSequentialWrite(const char * /* pszPath */,
574 : bool /* bAllowLocalTempFile */) override
575 : {
576 1 : return true;
577 : }
578 :
579 : bool SupportsRandomWrite(const char * /* pszPath */,
580 : bool /* bAllowLocalTempFile */) override;
581 : };
582 :
583 : /************************************************************************/
584 : /* IVSIS3LikeFSHandler */
585 : /************************************************************************/
586 :
587 : class 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 0 : IsAllowedHeaderForObjectCreation(const char * /* pszHeaderName */)
608 : {
609 0 : return false;
610 : }
611 :
612 7362 : 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 0 : virtual bool SupportsParallelMultipartUpload() const
639 : {
640 0 : 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 :
670 : class IVSIS3LikeHandle : public VSICurlHandle
671 : {
672 : CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeHandle)
673 :
674 : protected:
675 281 : bool UseLimitRangeGetInsteadOfHead() override
676 : {
677 281 : return true;
678 : }
679 :
680 103 : 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 107 : return response_code == 416 && EQUAL(pszVerb, "GET") &&
684 107 : std::string(m_pszURL).back() == '/';
685 : }
686 :
687 48 : void ProcessGetFileSizeResult(const char *pszContent) override
688 : {
689 48 : oFileProp.bIsDirectory =
690 48 : strstr(pszContent, "ListBucketResult") != nullptr;
691 48 : }
692 :
693 : public:
694 241 : IVSIS3LikeHandle(VSICurlFilesystemHandlerBase *poFSIn,
695 : const char *pszFilename, const char *pszURLIn)
696 241 : : VSICurlHandle(poFSIn, pszFilename, pszURLIn)
697 : {
698 241 : }
699 :
700 241 : ~IVSIS3LikeHandle() override
701 241 : {
702 241 : }
703 : };
704 :
705 : /************************************************************************/
706 : /* VSIS3WriteHandle */
707 : /************************************************************************/
708 :
709 : class 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 48 : bool IsOK()
766 : {
767 48 : return m_bUseChunked || m_pabyBuffer != nullptr;
768 : }
769 : };
770 :
771 : /************************************************************************/
772 : /* VSIAppendWriteHandle */
773 : /************************************************************************/
774 :
775 : class 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 18 : bool IsOK()
810 : {
811 18 : return m_pabyBuffer != nullptr;
812 : }
813 : };
814 :
815 : /************************************************************************/
816 : /* VSIDIRWithMissingDirSynthesis */
817 : /************************************************************************/
818 :
819 : struct 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 :
834 : struct 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 :
852 : class NetworkStatisticsLogger
853 : {
854 : static int gnEnabled;
855 : static NetworkStatisticsLogger gInstance;
856 :
857 1241 : 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 3 : ContextPathItem(ContextPathType eTypeIn, const std::string &osNameIn)
887 3 : : eType(eTypeIn), osName(osNameIn)
888 : {
889 3 : }
890 :
891 0 : bool operator<(const ContextPathItem &other) const
892 : {
893 0 : if (static_cast<int>(eType) < static_cast<int>(other.eType))
894 0 : return true;
895 0 : if (static_cast<int>(eType) > static_cast<int>(other.eType))
896 0 : return false;
897 0 : 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 277580 : static inline bool IsEnabled()
921 : {
922 277580 : if (gnEnabled < 0)
923 : {
924 6 : ReadEnabled();
925 : }
926 277580 : 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 :
956 : struct NetworkStatisticsFileSystem
957 : {
958 46504 : inline explicit NetworkStatisticsFileSystem(const char *pszName)
959 : {
960 46504 : NetworkStatisticsLogger::EnterFileSystem(pszName);
961 46504 : }
962 :
963 46504 : inline ~NetworkStatisticsFileSystem()
964 : {
965 46504 : NetworkStatisticsLogger::LeaveFileSystem();
966 46504 : }
967 : };
968 :
969 : struct NetworkStatisticsFile
970 : {
971 45304 : inline explicit NetworkStatisticsFile(const char *pszName)
972 : {
973 45304 : NetworkStatisticsLogger::EnterFile(pszName);
974 45304 : }
975 :
976 45304 : inline ~NetworkStatisticsFile()
977 : {
978 45304 : NetworkStatisticsLogger::LeaveFile();
979 45304 : }
980 : };
981 :
982 : struct NetworkStatisticsAction
983 : {
984 46504 : inline explicit NetworkStatisticsAction(const char *pszName)
985 : {
986 46504 : NetworkStatisticsLogger::EnterAction(pszName);
987 46504 : }
988 :
989 46504 : inline ~NetworkStatisticsAction()
990 : {
991 46504 : NetworkStatisticsLogger::LeaveAction();
992 46504 : }
993 : };
994 :
995 : } // namespace cpl
996 :
997 : int VSICURLGetDownloadChunkSize();
998 :
999 : void VSICURLInitWriteFuncStruct(cpl::WriteFuncStruct *psStruct, VSILFILE *fp,
1000 : VSICurlReadCbkFunc pfnReadCbk,
1001 : void *pReadCbkUserData);
1002 : size_t VSICurlHandleWriteFunc(void *buffer, size_t count, size_t nmemb,
1003 : void *req);
1004 : void VSICURLMultiPerform(CURLM *hCurlMultiHandle, CURL *hEasyHandle = nullptr);
1005 : void VSICURLResetHeaderAndWriterFunctions(CURL *hCurlHandle);
1006 :
1007 : int VSICurlParseUnixPermissions(const char *pszPermissions);
1008 :
1009 : // Cache of file properties (size, etc.)
1010 : bool VSICURLGetCachedFileProp(const char *pszURL, cpl::FileProp &oFileProp);
1011 : void VSICURLSetCachedFileProp(const char *pszURL, cpl::FileProp &oFileProp);
1012 : void VSICURLInvalidateCachedFileProp(const char *pszURL);
1013 : void VSICURLInvalidateCachedFilePropPrefix(const char *pszURL);
1014 : void VSICURLDestroyCacheFileProp();
1015 :
1016 : void VSICURLMultiCleanup(CURLM *hCurlMultiHandle);
1017 :
1018 : //! @endcond
1019 :
1020 : #endif // HAVE_CURL
1021 :
1022 : #endif // CPL_VSIL_CURL_CLASS_H_INCLUDED
|