LCOV - code coverage report
Current view: top level - port - cpl_vsil_curl_streaming.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 723 832 86.9 %
Date: 2026-02-12 06:20:29 Functions: 97 109 89.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CPL - Common Portability Library
       4             :  * Purpose:  Implement VSI large file api for HTTP/FTP files in streaming mode
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2012-2015, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "cpl_vsi.h"
      15             : #include "cpl_vsi_virtual.h"
      16             : #include "cpl_vsil_curl_class.h"
      17             : 
      18             : #include <algorithm>
      19             : #include <cinttypes>
      20             : #include <map>
      21             : 
      22             : #include "cpl_aws.h"
      23             : #include "cpl_google_cloud.h"
      24             : #include "cpl_azure.h"
      25             : #include "cpl_alibaba_oss.h"
      26             : #include "cpl_swift.h"
      27             : #include "cpl_hash_set.h"
      28             : #include "cpl_http.h"
      29             : #include "cpl_multiproc.h"
      30             : #include "cpl_string.h"
      31             : #include "cpl_time.h"
      32             : 
      33             : #if !defined(HAVE_CURL) || defined(CPL_MULTIPROC_STUB)
      34             : 
      35             : void VSIInstallCurlStreamingFileHandler(void)
      36             : {
      37             :     // Not supported.
      38             : }
      39             : 
      40             : void VSIInstallS3StreamingFileHandler(void)
      41             : {
      42             :     // Not supported.
      43             : }
      44             : 
      45             : void VSIInstallGSStreamingFileHandler(void)
      46             : {
      47             :     // Not supported.
      48             : }
      49             : 
      50             : void VSIInstallAzureStreamingFileHandler(void)
      51             : {
      52             :     // Not supported
      53             : }
      54             : 
      55             : void VSIInstallOSSStreamingFileHandler(void)
      56             : {
      57             :     // Not supported
      58             : }
      59             : 
      60             : void VSIInstallSwiftStreamingFileHandler(void)
      61             : {
      62             :     // Not supported
      63             : }
      64             : 
      65             : #ifdef HAVE_CURL
      66             : void VSICurlStreamingClearCache(void)
      67             : {
      68             :     // Not supported
      69             : }
      70             : #endif
      71             : 
      72             : #else
      73             : 
      74             : //! @cond Doxygen_Suppress
      75             : 
      76             : #include <curl/curl.h>
      77             : 
      78             : #define ENABLE_DEBUG 0
      79             : 
      80             : #define N_MAX_REGIONS 10
      81             : 
      82             : #define BKGND_BUFFER_SIZE (1024 * 1024)
      83             : 
      84             : #define unchecked_curl_easy_setopt(handle, opt, param)                         \
      85             :     CPL_IGNORE_RET_VAL(curl_easy_setopt(handle, opt, param))
      86             : 
      87             : /************************************************************************/
      88             : /*                              RingBuffer                              */
      89             : /************************************************************************/
      90             : 
      91             : class RingBuffer
      92             : {
      93             :     CPL_DISALLOW_COPY_ASSIGN(RingBuffer)
      94             : 
      95             :     GByte *pabyBuffer = nullptr;
      96             :     size_t nCapacity = 0;
      97             :     size_t nOffset = 0;
      98             :     size_t nLength = 0;
      99             : 
     100             :   public:
     101             :     explicit RingBuffer(size_t nCapacity = BKGND_BUFFER_SIZE);
     102             :     ~RingBuffer();
     103             : 
     104         210 :     size_t GetCapacity() const
     105             :     {
     106         210 :         return nCapacity;
     107             :     }
     108             : 
     109         724 :     size_t GetSize() const
     110             :     {
     111         724 :         return nLength;
     112             :     }
     113             : 
     114             :     void Reset();
     115             :     void Write(void *pBuffer, size_t nSize);
     116             :     void Read(void *pBuffer, size_t nSize);
     117             : };
     118             : 
     119          66 : RingBuffer::RingBuffer(size_t nCapacityIn)
     120          66 :     : pabyBuffer(static_cast<GByte *>(CPLMalloc(nCapacityIn))),
     121          66 :       nCapacity(nCapacityIn)
     122             : {
     123          66 : }
     124             : 
     125         132 : RingBuffer::~RingBuffer()
     126             : {
     127          66 :     CPLFree(pabyBuffer);
     128          66 : }
     129             : 
     130         155 : void RingBuffer::Reset()
     131             : {
     132         155 :     nOffset = 0;
     133         155 :     nLength = 0;
     134         155 : }
     135             : 
     136         208 : void RingBuffer::Write(void *pBuffer, size_t nSize)
     137             : {
     138         208 :     CPLAssert(nLength + nSize <= nCapacity);
     139             : 
     140         208 :     const size_t nEndOffset = (nOffset + nLength) % nCapacity;
     141         208 :     const size_t nSz = std::min(nSize, nCapacity - nEndOffset);
     142         208 :     memcpy(pabyBuffer + nEndOffset, pBuffer, nSz);
     143         208 :     if (nSz < nSize)
     144           0 :         memcpy(pabyBuffer, static_cast<GByte *>(pBuffer) + nSz, nSize - nSz);
     145             : 
     146         208 :     nLength += nSize;
     147         208 : }
     148             : 
     149         212 : void RingBuffer::Read(void *pBuffer, size_t nSize)
     150             : {
     151         212 :     CPLAssert(nSize <= nLength);
     152             : 
     153         212 :     if (pBuffer)
     154             :     {
     155         212 :         const size_t nSz = std::min(nSize, nCapacity - nOffset);
     156         212 :         memcpy(pBuffer, pabyBuffer + nOffset, nSz);
     157         212 :         if (nSz < nSize)
     158           0 :             memcpy(static_cast<GByte *>(pBuffer) + nSz, pabyBuffer,
     159             :                    nSize - nSz);
     160             :     }
     161             : 
     162         212 :     nOffset = (nOffset + nSize) % nCapacity;
     163         212 :     nLength -= nSize;
     164         212 : }
     165             : 
     166             : /************************************************************************/
     167             : 
     168             : namespace
     169             : {
     170             : 
     171             : typedef struct
     172             : {
     173             :     char *pBuffer;
     174             :     size_t nSize;
     175             :     int bIsHTTP;
     176             :     int bIsInHeader;
     177             :     int nHTTPCode;
     178             :     int bDownloadHeaderOnly;
     179             : } WriteFuncStructStreaming;
     180             : 
     181             : }  // namespace
     182             : 
     183             : namespace cpl
     184             : {
     185             : 
     186             : /************************************************************************/
     187             : /*                      VSICurlStreamingFSHandler                       */
     188             : /************************************************************************/
     189             : 
     190             : class VSICurlStreamingHandle;
     191             : 
     192             : class VSICurlStreamingFSHandler /* non final */ : public VSIFilesystemHandler
     193             : {
     194             :     CPL_DISALLOW_COPY_ASSIGN(VSICurlStreamingFSHandler)
     195             : 
     196             :     // LRU cache that just keeps in memory if this file system handler is
     197             :     // spposed to know the file properties of a file. The actual cache is a
     198             :     // shared one among all network file systems.
     199             :     // The aim of that design is that invalidating /vsis3/foo results in
     200             :     // /vsis3_streaming/foo to be invalidated as well.
     201             :     lru11::Cache<std::string, bool> oCacheFileProp;
     202             : 
     203             :   protected:
     204             :     CPLMutex *hMutex = nullptr;
     205             : 
     206             :     virtual VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
     207             :                                                      const char *pszURL);
     208             : 
     209          11 :     virtual std::string GetNonStreamingPrefix() const
     210             :     {
     211          11 :         return "/vsicurl/";
     212             :     }
     213             : 
     214             :   public:
     215             :     VSICurlStreamingFSHandler();
     216             :     ~VSICurlStreamingFSHandler() override;
     217             : 
     218             :     VSIVirtualHandleUniquePtr Open(const char *pszFilename,
     219             :                                    const char *pszAccess, bool bSetError,
     220             :                                    CSLConstList /* papszOptions */) override;
     221             : 
     222             :     virtual int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
     223             :                      int nFlags) override;
     224             : 
     225          70 :     virtual CPLString GetFSPrefix() const
     226             :     {
     227          70 :         return "/vsicurl_streaming/";
     228             :     }
     229             : 
     230             :     std::string
     231             :     GetNonStreamingFilename(const std::string &osFilename) const override;
     232             : 
     233             :     const char *GetActualURL(const char *pszFilename) override;
     234             : 
     235           1 :     const char *GetOptions() override
     236             :     {
     237           1 :         return VSIGetFileSystemOptions("/vsicurl/");
     238             :     }
     239             : 
     240             :     void AcquireMutex();
     241             :     void ReleaseMutex();
     242             : 
     243             :     bool GetCachedFileProp(const char *pszURL, FileProp &oFileProp);
     244             :     void SetCachedFileProp(const char *pszURL, FileProp &oFileProp);
     245             : 
     246             :     virtual void ClearCache();
     247             : };
     248             : 
     249             : /************************************************************************/
     250             : /*                        VSICurlStreamingHandle                        */
     251             : /************************************************************************/
     252             : 
     253             : class VSICurlStreamingHandle : public VSIVirtualHandle
     254             : {
     255             :     CPL_DISALLOW_COPY_ASSIGN(VSICurlStreamingHandle)
     256             : 
     257             :   protected:
     258             :     VSICurlStreamingFSHandler *m_poFS = nullptr;
     259             :     CPLStringList m_aosHTTPOptions{};
     260             :     const CPLHTTPRetryParameters m_oRetryParameters;
     261             : 
     262             :   private:
     263             :     char *m_pszURL = nullptr;
     264             : 
     265             : #ifdef notdef
     266             :     unsigned int nRecomputedChecksumOfFirst1024Bytes = 0;
     267             : #endif
     268             :     vsi_l_offset curOffset = 0;
     269             :     vsi_l_offset fileSize = 0;
     270             :     bool bHasComputedFileSize = false;
     271             :     ExistStatus eExists = EXIST_UNKNOWN;
     272             :     bool bIsDirectory = false;
     273             : 
     274             :     bool bCanTrustCandidateFileSize = true;
     275             :     bool bHasCandidateFileSize = false;
     276             :     vsi_l_offset nCandidateFileSize = 0;
     277             : 
     278             :     bool bEOF = false;
     279             :     bool m_bError = false;
     280             : 
     281             :     size_t nCachedSize = 0;
     282             :     GByte *pCachedData = nullptr;
     283             : 
     284             :     volatile int bDownloadInProgress = FALSE;
     285             :     volatile int bDownloadStopped = FALSE;
     286             :     volatile int bAskDownloadEnd = FALSE;
     287             :     vsi_l_offset nRingBufferFileOffset = 0;
     288             :     CPLJoinableThread *hThread = nullptr;
     289             :     CPLMutex *hRingBufferMutex = nullptr;
     290             :     CPLCond *hCondProducer = nullptr;
     291             :     CPLCond *hCondConsumer = nullptr;
     292             :     RingBuffer oRingBuffer{};
     293             :     void StartDownload();
     294             :     void StopDownload();
     295             :     void PutRingBufferInCache();
     296             : 
     297             :     GByte *pabyHeaderData = nullptr;
     298             :     size_t nHeaderSize = 0;
     299             :     vsi_l_offset nBodySize = 0;
     300             :     int nHTTPCode = 0;
     301             :     char m_szCurlErrBuf[CURL_ERROR_SIZE + 1];
     302             :     bool m_bErrorOccurredInThread = false;
     303             : 
     304             :     void AcquireMutex();
     305             :     void ReleaseMutex();
     306             : 
     307             :     void AddRegion(vsi_l_offset nFileOffsetStart, size_t nSize, GByte *pData);
     308             : 
     309             :   protected:
     310          15 :     virtual struct curl_slist *GetCurlHeaders(const CPLString &,
     311             :                                               struct curl_slist *psHeaders)
     312             :     {
     313          15 :         return psHeaders;
     314             :     }
     315             : 
     316          11 :     virtual bool StopReceivingBytesOnError()
     317             :     {
     318          11 :         return true;
     319             :     }
     320             : 
     321           0 :     virtual bool CanRestartOnError(const char * /*pszErrorMsg*/,
     322             :                                    const char * /*pszHeaders*/,
     323             :                                    bool /*bSetError*/)
     324             :     {
     325           0 :         return false;
     326             :     }
     327             : 
     328         135 :     virtual bool InterpretRedirect()
     329             :     {
     330         135 :         return true;
     331             :     }
     332             : 
     333             :     void SetURL(const char *pszURL);
     334             : 
     335             :   public:
     336             :     VSICurlStreamingHandle(VSICurlStreamingFSHandler *poFS,
     337             :                            const char *pszFilename, const char *pszURL);
     338             :     ~VSICurlStreamingHandle() override;
     339             : 
     340             :     int Seek(vsi_l_offset nOffset, int nWhence) override;
     341             :     vsi_l_offset Tell() override;
     342             :     size_t Read(void *pBuffer, size_t nBytes) override;
     343             :     size_t Write(const void *pBuffer, size_t nBytes) override;
     344             :     void ClearErr() override;
     345             :     int Error() override;
     346             :     int Eof() override;
     347             :     int Flush() override;
     348             :     int Close() override;
     349             : 
     350             :     void DownloadInThread();
     351             :     size_t ReceivedBytes(GByte *buffer, size_t count, size_t nmemb);
     352             :     size_t ReceivedBytesHeader(GByte *buffer, size_t count, size_t nmemb);
     353             : 
     354          10 :     bool IsKnownFileSize() const
     355             :     {
     356          10 :         return bHasComputedFileSize;
     357             :     }
     358             : 
     359             :     vsi_l_offset GetFileSize();
     360             :     bool Exists(const char *pszFilename, CSLConstList papszOptions);
     361             : 
     362          19 :     bool IsDirectory() const
     363             :     {
     364          19 :         return bIsDirectory;
     365             :     }
     366             : 
     367           3 :     const char *GetURL() const
     368             :     {
     369           3 :         return m_pszURL;
     370             :     }
     371             : };
     372             : 
     373             : /************************************************************************/
     374             : /*                       VSICurlStreamingHandle()                       */
     375             : /************************************************************************/
     376             : 
     377          66 : VSICurlStreamingHandle::VSICurlStreamingHandle(VSICurlStreamingFSHandler *poFS,
     378             :                                                const char *pszFilename,
     379          66 :                                                const char *pszURL)
     380             :     : m_poFS(poFS), m_aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszFilename)),
     381          66 :       m_oRetryParameters(m_aosHTTPOptions), m_pszURL(CPLStrdup(pszURL))
     382             : {
     383          66 :     FileProp cachedFileProp;
     384          66 :     poFS->GetCachedFileProp(pszURL, cachedFileProp);
     385          66 :     eExists = cachedFileProp.eExists;
     386          66 :     fileSize = cachedFileProp.fileSize;
     387          66 :     bHasComputedFileSize = cachedFileProp.bHasComputedFileSize;
     388          66 :     bIsDirectory = cachedFileProp.bIsDirectory;
     389          66 :     poFS->SetCachedFileProp(pszURL, cachedFileProp);
     390             : 
     391          66 :     hRingBufferMutex = CPLCreateMutex();
     392          66 :     ReleaseMutex();
     393          66 :     hCondProducer = CPLCreateCond();
     394          66 :     hCondConsumer = CPLCreateCond();
     395             : 
     396          66 :     memset(m_szCurlErrBuf, 0, sizeof(m_szCurlErrBuf));
     397          66 : }
     398             : 
     399             : /************************************************************************/
     400             : /*                      ~VSICurlStreamingHandle()                       */
     401             : /************************************************************************/
     402             : 
     403          77 : VSICurlStreamingHandle::~VSICurlStreamingHandle()
     404             : {
     405          66 :     StopDownload();
     406             : 
     407          66 :     CPLFree(m_pszURL);
     408             : 
     409          66 :     CPLFree(pCachedData);
     410             : 
     411          66 :     CPLFree(pabyHeaderData);
     412             : 
     413          66 :     CPLDestroyMutex(hRingBufferMutex);
     414          66 :     CPLDestroyCond(hCondProducer);
     415          66 :     CPLDestroyCond(hCondConsumer);
     416          77 : }
     417             : 
     418             : /************************************************************************/
     419             : /*                               SetURL()                               */
     420             : /************************************************************************/
     421             : 
     422           4 : void VSICurlStreamingHandle::SetURL(const char *pszURLIn)
     423             : {
     424           4 :     CPLFree(m_pszURL);
     425           4 :     m_pszURL = CPLStrdup(pszURLIn);
     426           4 : }
     427             : 
     428             : /************************************************************************/
     429             : /*                            AcquireMutex()                            */
     430             : /************************************************************************/
     431             : 
     432        1225 : void VSICurlStreamingHandle::AcquireMutex()
     433             : {
     434        1225 :     CPLAcquireMutex(hRingBufferMutex, 1000.0);
     435        1225 : }
     436             : 
     437             : /************************************************************************/
     438             : /*                            ReleaseMutex()                            */
     439             : /************************************************************************/
     440             : 
     441        1291 : void VSICurlStreamingHandle::ReleaseMutex()
     442             : {
     443        1291 :     CPLReleaseMutex(hRingBufferMutex);
     444        1291 : }
     445             : 
     446             : /************************************************************************/
     447             : /*                                Seek()                                */
     448             : /************************************************************************/
     449             : 
     450          68 : int VSICurlStreamingHandle::Seek(vsi_l_offset nOffset, int nWhence)
     451             : {
     452          68 :     if (curOffset >= BKGND_BUFFER_SIZE)
     453             :     {
     454             :         if (ENABLE_DEBUG)
     455             :             CPLDebug("VSICURL",
     456             :                      "Invalidating cache and file size due to Seek() "
     457             :                      "beyond caching zone");
     458           0 :         CPLFree(pCachedData);
     459           0 :         pCachedData = nullptr;
     460           0 :         nCachedSize = 0;
     461           0 :         AcquireMutex();
     462           0 :         bHasComputedFileSize = false;
     463           0 :         fileSize = 0;
     464           0 :         ReleaseMutex();
     465             :     }
     466             : 
     467          68 :     if (nWhence == SEEK_SET)
     468             :     {
     469          66 :         curOffset = nOffset;
     470             :     }
     471           2 :     else if (nWhence == SEEK_CUR)
     472             :     {
     473           0 :         curOffset = curOffset + nOffset;
     474             :     }
     475             :     else
     476             :     {
     477           2 :         curOffset = GetFileSize() + nOffset;
     478             :     }
     479          68 :     bEOF = false;
     480          68 :     return 0;
     481             : }
     482             : 
     483             : /************************************************************************/
     484             : /*            VSICURLStreamingInitWriteFuncStructStreaming()            */
     485             : /************************************************************************/
     486             : 
     487             : static void
     488          18 : VSICURLStreamingInitWriteFuncStructStreaming(WriteFuncStructStreaming *psStruct)
     489             : {
     490          18 :     psStruct->pBuffer = nullptr;
     491          18 :     psStruct->nSize = 0;
     492          18 :     psStruct->bIsHTTP = FALSE;
     493          18 :     psStruct->bIsInHeader = TRUE;
     494          18 :     psStruct->nHTTPCode = 0;
     495          18 :     psStruct->bDownloadHeaderOnly = FALSE;
     496          18 : }
     497             : 
     498             : /************************************************************************/
     499             : /*              VSICurlStreamingHandleWriteFuncForHeader()              */
     500             : /************************************************************************/
     501             : 
     502          50 : static size_t VSICurlStreamingHandleWriteFuncForHeader(void *buffer,
     503             :                                                        size_t count,
     504             :                                                        size_t nmemb, void *req)
     505             : {
     506          50 :     WriteFuncStructStreaming *psStruct =
     507             :         static_cast<WriteFuncStructStreaming *>(req);
     508          50 :     const size_t nSize = count * nmemb;
     509             : 
     510             :     char *pNewBuffer = static_cast<char *>(
     511          50 :         VSIRealloc(psStruct->pBuffer, psStruct->nSize + nSize + 1));
     512          50 :     if (pNewBuffer)
     513             :     {
     514          50 :         psStruct->pBuffer = pNewBuffer;
     515          50 :         memcpy(psStruct->pBuffer + psStruct->nSize, buffer, nSize);
     516          50 :         psStruct->pBuffer[psStruct->nSize + nSize] = '\0';
     517          50 :         if (psStruct->bIsHTTP && psStruct->bIsInHeader)
     518             :         {
     519           0 :             char *pszLine = psStruct->pBuffer + psStruct->nSize;
     520           0 :             if (STARTS_WITH_CI(pszLine, "HTTP/"))
     521             :             {
     522             :                 const char *pszSpace =
     523           0 :                     strchr(const_cast<const char *>(pszLine), ' ');
     524           0 :                 if (pszSpace)
     525           0 :                     psStruct->nHTTPCode = atoi(pszSpace + 1);
     526             :             }
     527             : 
     528           0 :             if (pszLine[0] == '\r' || pszLine[0] == '\n')
     529             :             {
     530           0 :                 if (psStruct->bDownloadHeaderOnly)
     531             :                 {
     532             :                     // If moved permanently/temporarily, go on.
     533             :                     // Otherwise stop now.
     534           0 :                     if (!(psStruct->nHTTPCode == 301 ||
     535           0 :                           psStruct->nHTTPCode == 302 ||
     536           0 :                           psStruct->nHTTPCode == 303))
     537           0 :                         return 0;
     538             :                 }
     539             :                 else
     540             :                 {
     541           0 :                     psStruct->bIsInHeader = FALSE;
     542             :                 }
     543             :             }
     544             :         }
     545          50 :         psStruct->nSize += nSize;
     546          50 :         return nmemb;
     547             :     }
     548             :     else
     549             :     {
     550           0 :         return 0;
     551             :     }
     552             : }
     553             : 
     554             : /************************************************************************/
     555             : /*                            GetFileSize()                             */
     556             : /************************************************************************/
     557             : 
     558          12 : vsi_l_offset VSICurlStreamingHandle::GetFileSize()
     559             : {
     560             :     WriteFuncStructStreaming sWriteFuncData;
     561             :     WriteFuncStructStreaming sWriteFuncHeaderData;
     562             : 
     563          12 :     AcquireMutex();
     564          12 :     if (bHasComputedFileSize)
     565             :     {
     566           3 :         const vsi_l_offset nRet = fileSize;
     567           3 :         ReleaseMutex();
     568           3 :         return nRet;
     569             :     }
     570           9 :     ReleaseMutex();
     571             : 
     572           9 :     CURL *hLocalHandle = curl_easy_init();
     573             : 
     574             :     struct curl_slist *headers =
     575           9 :         VSICurlSetOptions(hLocalHandle, m_pszURL, m_aosHTTPOptions.List());
     576             : 
     577           9 :     VSICURLStreamingInitWriteFuncStructStreaming(&sWriteFuncHeaderData);
     578             : 
     579             :     // HACK for mbtiles driver: Proper fix would be to auto-detect servers that
     580             :     // don't accept HEAD http://a.tiles.mapbox.com/v3/ doesn't accept HEAD, so
     581             :     // let's start a GET and interrupt is as soon as the header is found.
     582          18 :     CPLString osVerb;
     583           9 :     if (strstr(m_pszURL, ".tiles.mapbox.com/") != nullptr)
     584             :     {
     585           0 :         unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HEADERDATA,
     586             :                                    &sWriteFuncHeaderData);
     587           0 :         unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HEADERFUNCTION,
     588             :                                    VSICurlStreamingHandleWriteFuncForHeader);
     589             : 
     590           0 :         sWriteFuncHeaderData.bIsHTTP = STARTS_WITH(m_pszURL, "http");
     591           0 :         sWriteFuncHeaderData.bDownloadHeaderOnly = TRUE;
     592           0 :         osVerb = "GET";
     593             :     }
     594             :     else
     595             :     {
     596           9 :         unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_NOBODY, 1);
     597           9 :         unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HTTPGET, 0);
     598           9 :         unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HEADER, 1);
     599           9 :         osVerb = "HEAD";
     600             :     }
     601             : 
     602           9 :     headers = GetCurlHeaders(osVerb, headers);
     603           9 :     unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HTTPHEADER, headers);
     604             : 
     605             :     // We need that otherwise OSGEO4W's libcurl issue a dummy range request
     606             :     // when doing a HEAD when recycling connections.
     607           9 :     unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_RANGE, nullptr);
     608             : 
     609             :     // Bug with older curl versions (<=7.16.4) and FTP.
     610             :     // See http://curl.haxx.se/mail/lib-2007-08/0312.html
     611           9 :     VSICURLStreamingInitWriteFuncStructStreaming(&sWriteFuncData);
     612           9 :     unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_WRITEDATA,
     613             :                                &sWriteFuncData);
     614           9 :     unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_WRITEFUNCTION,
     615             :                                VSICurlStreamingHandleWriteFuncForHeader);
     616             : 
     617           9 :     char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
     618           9 :     unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf);
     619             : 
     620           9 :     void *old_handler = CPLHTTPIgnoreSigPipe();
     621           9 :     curl_easy_perform(hLocalHandle);
     622           9 :     CPLHTTPRestoreSigPipeHandler(old_handler);
     623           9 :     if (headers != nullptr)
     624           9 :         curl_slist_free_all(headers);
     625             : 
     626           9 :     AcquireMutex();
     627             : 
     628           9 :     eExists = EXIST_UNKNOWN;
     629           9 :     bHasComputedFileSize = true;
     630             : 
     631           9 :     if (STARTS_WITH(m_pszURL, "ftp"))
     632             :     {
     633           0 :         if (sWriteFuncData.pBuffer != nullptr &&
     634           0 :             STARTS_WITH_CI(sWriteFuncData.pBuffer, "Content-Length: "))
     635             :         {
     636           0 :             const char *pszBuffer =
     637           0 :                 sWriteFuncData.pBuffer + strlen("Content-Length: ");
     638           0 :             eExists = EXIST_YES;
     639           0 :             fileSize = CPLScanUIntBig(
     640           0 :                 pszBuffer, static_cast<int>(sWriteFuncData.nSize -
     641             :                                             strlen("Content-Length: ")));
     642             :             if (ENABLE_DEBUG)
     643             :                 CPLDebug("VSICURL", "GetFileSize(%s)=" CPL_FRMT_GUIB, m_pszURL,
     644             :                          fileSize);
     645             :         }
     646             :     }
     647             : 
     648           9 :     double dfSize = 0;
     649           9 :     if (eExists != EXIST_YES)
     650             :     {
     651           9 :         curl_off_t nSizeTmp = 0;
     652           9 :         const CURLcode code = curl_easy_getinfo(
     653             :             hLocalHandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &nSizeTmp);
     654           9 :         CPL_IGNORE_RET_VAL(dfSize);
     655           9 :         dfSize = static_cast<double>(nSizeTmp);
     656           9 :         if (code == 0)
     657             :         {
     658           9 :             eExists = EXIST_YES;
     659           9 :             if (dfSize < 0)
     660           0 :                 fileSize = 0;
     661             :             else
     662           9 :                 fileSize = static_cast<GUIntBig>(dfSize);
     663             :         }
     664             :         else
     665             :         {
     666           0 :             eExists = EXIST_NO;
     667           0 :             fileSize = 0;
     668           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     669             :                      "VSICurlStreamingHandle::GetFileSize failed");
     670             :         }
     671             : 
     672           9 :         long response_code = 0;
     673           9 :         curl_easy_getinfo(hLocalHandle, CURLINFO_HTTP_CODE, &response_code);
     674           9 :         if (response_code != 200)
     675             :         {
     676           0 :             eExists = EXIST_NO;
     677           0 :             fileSize = 0;
     678             :         }
     679             : 
     680             :         // Try to guess if this is a directory. Generally if this is a
     681             :         // directory, curl will retry with an URL with slash added.
     682           9 :         char *pszEffectiveURL = nullptr;
     683           9 :         curl_easy_getinfo(hLocalHandle, CURLINFO_EFFECTIVE_URL,
     684             :                           &pszEffectiveURL);
     685           9 :         if (pszEffectiveURL != nullptr &&
     686           9 :             strncmp(m_pszURL, pszEffectiveURL, strlen(m_pszURL)) == 0 &&
     687           9 :             pszEffectiveURL[strlen(m_pszURL)] == '/')
     688             :         {
     689           0 :             eExists = EXIST_YES;
     690           0 :             fileSize = 0;
     691           0 :             bIsDirectory = true;
     692             :         }
     693             : 
     694             :         if (ENABLE_DEBUG)
     695             :             CPLDebug("VSICURL",
     696             :                      "GetFileSize(%s)=" CPL_FRMT_GUIB " response_code=%d",
     697             :                      m_pszURL, fileSize, static_cast<int>(response_code));
     698             :     }
     699             : 
     700           9 :     CPLFree(sWriteFuncData.pBuffer);
     701           9 :     CPLFree(sWriteFuncHeaderData.pBuffer);
     702             : 
     703           9 :     FileProp cachedFileProp;
     704           9 :     m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
     705           9 :     cachedFileProp.bHasComputedFileSize = true;
     706           9 :     cachedFileProp.fileSize = fileSize;
     707           9 :     cachedFileProp.eExists = eExists;
     708           9 :     cachedFileProp.bIsDirectory = bIsDirectory;
     709           9 :     if (cachedFileProp.nMode == 0)
     710           9 :         cachedFileProp.nMode = bIsDirectory ? S_IFDIR : S_IFREG;
     711           9 :     m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
     712             : 
     713           9 :     const vsi_l_offset nRet = fileSize;
     714           9 :     ReleaseMutex();
     715             : 
     716           9 :     curl_easy_cleanup(hLocalHandle);
     717             : 
     718           9 :     return nRet;
     719             : }
     720             : 
     721             : /************************************************************************/
     722             : /*                               Exists()                               */
     723             : /************************************************************************/
     724             : 
     725          63 : bool VSICurlStreamingHandle::Exists(const char *pszFilename,
     726             :                                     CSLConstList papszOptions)
     727             : {
     728          63 :     if (eExists == EXIST_UNKNOWN)
     729             :     {
     730          50 :         if (!papszOptions ||
     731           0 :             !CPLTestBool(CSLFetchNameValueDef(
     732             :                 papszOptions, "IGNORE_FILENAME_RESTRICTIONS", "NO")))
     733             :         {
     734          50 :             if (!VSICurlFilesystemHandlerBase::IsAllowedFilename(pszFilename))
     735             :             {
     736           0 :                 eExists = EXIST_NO;
     737           0 :                 fileSize = 0;
     738             : 
     739           0 :                 FileProp cachedFileProp;
     740           0 :                 m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
     741           0 :                 cachedFileProp.bHasComputedFileSize = true;
     742           0 :                 cachedFileProp.fileSize = fileSize;
     743           0 :                 cachedFileProp.eExists = eExists;
     744           0 :                 cachedFileProp.bIsDirectory = false;
     745           0 :                 cachedFileProp.nMode = S_IFREG;
     746           0 :                 m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
     747             : 
     748           0 :                 return false;
     749             :             }
     750             :         }
     751             : 
     752          50 :         char chFirstByte = '\0';
     753          50 :         int bExists = (Read(&chFirstByte, 1) == 1);
     754             : 
     755         100 :         FileProp cachedFileProp;
     756          50 :         m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
     757          50 :         cachedFileProp.eExists = eExists = bExists ? EXIST_YES : EXIST_NO;
     758          50 :         m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
     759             : 
     760          50 :         Seek(0, SEEK_SET);
     761             :     }
     762             : 
     763          63 :     return eExists == EXIST_YES;
     764             : }
     765             : 
     766             : /************************************************************************/
     767             : /*                                Tell()                                */
     768             : /************************************************************************/
     769             : 
     770           4 : vsi_l_offset VSICurlStreamingHandle::Tell()
     771             : {
     772           4 :     return curOffset;
     773             : }
     774             : 
     775             : /************************************************************************/
     776             : /*                           ReceivedBytes()                            */
     777             : /************************************************************************/
     778             : 
     779         207 : size_t VSICurlStreamingHandle::ReceivedBytes(GByte *buffer, size_t count,
     780             :                                              size_t nmemb)
     781             : {
     782         207 :     size_t nSize = count * nmemb;
     783         207 :     nBodySize += nSize;
     784             : 
     785             :     if (ENABLE_DEBUG)
     786             :         CPLDebug("VSICURL", "Receiving %d bytes...", static_cast<int>(nSize));
     787             : 
     788         207 :     if (bHasCandidateFileSize && bCanTrustCandidateFileSize &&
     789         193 :         !bHasComputedFileSize)
     790             :     {
     791          82 :         FileProp cachedFileProp;
     792          41 :         m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
     793          41 :         cachedFileProp.fileSize = fileSize = nCandidateFileSize;
     794          41 :         bHasComputedFileSize = TRUE;
     795          41 :         cachedFileProp.bHasComputedFileSize = bHasComputedFileSize;
     796          41 :         m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
     797             :         if (ENABLE_DEBUG)
     798             :             CPLDebug("VSICURL", "File size = " CPL_FRMT_GUIB, fileSize);
     799             :     }
     800             : 
     801         207 :     AcquireMutex();
     802         207 :     if (eExists == EXIST_UNKNOWN)
     803             :     {
     804           0 :         FileProp cachedFileProp;
     805           0 :         m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
     806           0 :         cachedFileProp.eExists = eExists = EXIST_YES;
     807           0 :         m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
     808             :     }
     809         207 :     else if (eExists == EXIST_NO && StopReceivingBytesOnError())
     810             :     {
     811           0 :         ReleaseMutex();
     812           0 :         return 0;
     813             :     }
     814             : 
     815             :     while (true)
     816             :     {
     817         208 :         const size_t nFree = oRingBuffer.GetCapacity() - oRingBuffer.GetSize();
     818         208 :         if (nSize <= nFree)
     819             :         {
     820         207 :             oRingBuffer.Write(buffer, nSize);
     821             : 
     822             :             // Signal to the consumer that we have added bytes to the buffer.
     823         207 :             CPLCondSignal(hCondProducer);
     824             : 
     825         207 :             if (bAskDownloadEnd)
     826             :             {
     827             :                 if (ENABLE_DEBUG)
     828             :                     CPLDebug("VSICURL", "Download interruption asked");
     829             : 
     830           2 :                 ReleaseMutex();
     831           2 :                 return 0;
     832             :             }
     833         205 :             break;
     834             :         }
     835             :         else
     836             :         {
     837           1 :             oRingBuffer.Write(buffer, nFree);
     838           1 :             buffer += nFree;
     839           1 :             nSize -= nFree;
     840             : 
     841             :             // Signal to the consumer that we have added bytes to the buffer.
     842           1 :             CPLCondSignal(hCondProducer);
     843             : 
     844             :             if (ENABLE_DEBUG)
     845             :                 CPLDebug("VSICURL",
     846             :                          "Waiting for reader to consume some bytes...");
     847             : 
     848           3 :             while (oRingBuffer.GetSize() == oRingBuffer.GetCapacity() &&
     849           1 :                    !bAskDownloadEnd)
     850             :             {
     851           1 :                 CPLCondWait(hCondConsumer, hRingBufferMutex);
     852             :             }
     853             : 
     854           1 :             if (bAskDownloadEnd)
     855             :             {
     856             :                 if (ENABLE_DEBUG)
     857             :                     CPLDebug("VSICURL", "Download interruption asked");
     858             : 
     859           0 :                 ReleaseMutex();
     860           0 :                 return 0;
     861             :             }
     862             :         }
     863           1 :     }
     864             : 
     865         205 :     ReleaseMutex();
     866             : 
     867         205 :     return nmemb;
     868             : }
     869             : 
     870             : /************************************************************************/
     871             : /*                VSICurlStreamingHandleReceivedBytes()                 */
     872             : /************************************************************************/
     873             : 
     874         207 : static size_t VSICurlStreamingHandleReceivedBytes(void *buffer, size_t count,
     875             :                                                   size_t nmemb, void *req)
     876             : {
     877         207 :     return static_cast<VSICurlStreamingHandle *>(req)->ReceivedBytes(
     878         207 :         static_cast<GByte *>(buffer), count, nmemb);
     879             : }
     880             : 
     881             : /************************************************************************/
     882             : /*             VSICurlStreamingHandleReceivedBytesHeader()              */
     883             : /************************************************************************/
     884             : 
     885             : #define HEADER_SIZE 32768
     886             : 
     887         414 : size_t VSICurlStreamingHandle::ReceivedBytesHeader(GByte *buffer, size_t count,
     888             :                                                    size_t nmemb)
     889             : {
     890         414 :     const size_t nSize = count * nmemb;
     891             :     if (ENABLE_DEBUG)
     892             :         CPLDebug("VSICURL", "Receiving %d bytes for header...",
     893             :                  static_cast<int>(nSize));
     894             : 
     895             :     // Reset buffer if we have followed link after a redirect.
     896         355 :     if (nSize >= 9 && InterpretRedirect() &&
     897         769 :         (nHTTPCode == 301 || nHTTPCode == 302 || nHTTPCode == 303) &&
     898           0 :         STARTS_WITH_CI(reinterpret_cast<char *>(buffer), "HTTP/"))
     899             :     {
     900           0 :         nHeaderSize = 0;
     901           0 :         nHTTPCode = 0;
     902             :     }
     903             : 
     904         414 :     if (nHeaderSize < HEADER_SIZE)
     905             :     {
     906         414 :         const size_t nSz = std::min(nSize, HEADER_SIZE - nHeaderSize);
     907         414 :         memcpy(pabyHeaderData + nHeaderSize, buffer, nSz);
     908         414 :         pabyHeaderData[nHeaderSize + nSz] = '\0';
     909         414 :         nHeaderSize += nSz;
     910             : 
     911             : #if DEBUG_VERBOSE
     912             :         CPLDebug("VSICURL", "Header : %s", pabyHeaderData);
     913             : #endif
     914             : 
     915         414 :         AcquireMutex();
     916             : 
     917         414 :         if (nHTTPCode == 0 &&
     918          59 :             strchr(reinterpret_cast<char *>(pabyHeaderData), '\n') != nullptr &&
     919          59 :             STARTS_WITH_CI(reinterpret_cast<char *>(pabyHeaderData), "HTTP/"))
     920             :         {
     921             :             const char *pszSpace =
     922          59 :                 strchr(const_cast<const char *>(
     923          59 :                            reinterpret_cast<char *>(pabyHeaderData)),
     924             :                        ' ');
     925          59 :             if (pszSpace)
     926          59 :                 nHTTPCode = atoi(pszSpace + 1);
     927             :             if (ENABLE_DEBUG)
     928             :                 CPLDebug("VSICURL", "HTTP code = %d", nHTTPCode);
     929             : 
     930             :             // If moved permanently/temporarily, go on.
     931         111 :             if (eExists == EXIST_UNKNOWN &&
     932          52 :                 !(InterpretRedirect() &&
     933           9 :                   (nHTTPCode == 301 || nHTTPCode == 302 || nHTTPCode == 303)))
     934             :             {
     935          52 :                 eExists = nHTTPCode == 200 ? EXIST_YES : EXIST_NO;
     936         104 :                 FileProp cachedFileProp;
     937          52 :                 m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
     938          52 :                 cachedFileProp.eExists = eExists;
     939          52 :                 m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
     940             :             }
     941             :         }
     942             : 
     943         414 :         if (!(InterpretRedirect() &&
     944         828 :               (nHTTPCode == 301 || nHTTPCode == 302 || nHTTPCode == 303)) &&
     945         414 :             !bHasComputedFileSize)
     946             :         {
     947             :             // Caution: When gzip compression is enabled, the content-length is
     948             :             // the compressed size, which we are not interested in, so we must
     949             :             // not take it into account.
     950             : 
     951         365 :             const char *pszContentLength = strstr(
     952         365 :                 reinterpret_cast<char *>(pabyHeaderData), "Content-Length: ");
     953         365 :             const char *pszEndOfLine =
     954         365 :                 pszContentLength ? strchr(pszContentLength, '\n') : nullptr;
     955         365 :             if (bCanTrustCandidateFileSize && pszEndOfLine != nullptr)
     956             :             {
     957          95 :                 const char *pszVal =
     958             :                     pszContentLength + strlen("Content-Length: ");
     959          95 :                 bHasCandidateFileSize = true;
     960          95 :                 nCandidateFileSize = CPLScanUIntBig(
     961          95 :                     pszVal, static_cast<int>(pszEndOfLine - pszVal));
     962             :                 if (ENABLE_DEBUG)
     963             :                     CPLDebug("VSICURL",
     964             :                              "Has found candidate file size = " CPL_FRMT_GUIB,
     965             :                              nCandidateFileSize);
     966             :             }
     967             : 
     968         365 :             const char *pszContentEncoding = strstr(
     969         365 :                 reinterpret_cast<char *>(pabyHeaderData), "Content-Encoding: ");
     970         365 :             pszEndOfLine =
     971         365 :                 pszContentEncoding ? strchr(pszContentEncoding, '\n') : nullptr;
     972         365 :             if (bHasCandidateFileSize && pszEndOfLine != nullptr)
     973             :             {
     974           0 :                 const char *pszVal =
     975             :                     pszContentEncoding + strlen("Content-Encoding: ");
     976           0 :                 if (STARTS_WITH(pszVal, "gzip"))
     977             :                 {
     978             :                     if (ENABLE_DEBUG)
     979             :                         CPLDebug("VSICURL", "GZip compression enabled --> "
     980             :                                             "cannot trust candidate file size");
     981           0 :                     bCanTrustCandidateFileSize = false;
     982             :                 }
     983             :             }
     984             :         }
     985             : 
     986         414 :         ReleaseMutex();
     987             :     }
     988             : 
     989         414 :     return nmemb;
     990             : }
     991             : 
     992             : /************************************************************************/
     993             : /*             VSICurlStreamingHandleReceivedBytesHeader()              */
     994             : /************************************************************************/
     995             : 
     996         414 : static size_t VSICurlStreamingHandleReceivedBytesHeader(void *buffer,
     997             :                                                         size_t count,
     998             :                                                         size_t nmemb, void *req)
     999             : {
    1000         414 :     return static_cast<VSICurlStreamingHandle *>(req)->ReceivedBytesHeader(
    1001         414 :         static_cast<GByte *>(buffer), count, nmemb);
    1002             : }
    1003             : 
    1004             : /************************************************************************/
    1005             : /*                          DownloadInThread()                          */
    1006             : /************************************************************************/
    1007             : 
    1008          61 : void VSICurlStreamingHandle::DownloadInThread()
    1009             : {
    1010          61 :     CURL *hCurlHandle = curl_easy_init();
    1011             : 
    1012             :     struct curl_slist *headers =
    1013          61 :         VSICurlSetOptions(hCurlHandle, m_pszURL, m_aosHTTPOptions.List());
    1014          61 :     headers = GetCurlHeaders("GET", headers);
    1015          61 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
    1016             : 
    1017             :     static bool bHasCheckVersion = false;
    1018             :     static bool bSupportGZip = false;
    1019          60 :     if (!bHasCheckVersion)
    1020             :     {
    1021           3 :         bSupportGZip = strstr(curl_version(), "zlib/") != nullptr;
    1022           3 :         bHasCheckVersion = true;
    1023             :     }
    1024          60 :     if (bSupportGZip && CPLTestBool(CPLGetConfigOption("CPL_CURL_GZIP", "YES")))
    1025             :     {
    1026          61 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_ENCODING, "gzip");
    1027             :     }
    1028             : 
    1029          61 :     if (pabyHeaderData == nullptr)
    1030          53 :         pabyHeaderData = static_cast<GByte *>(CPLMalloc(HEADER_SIZE + 1));
    1031          61 :     nHeaderSize = 0;
    1032          61 :     nBodySize = 0;
    1033          61 :     nHTTPCode = 0;
    1034             : 
    1035          61 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA, this);
    1036          61 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION,
    1037             :                                VSICurlStreamingHandleReceivedBytesHeader);
    1038             : 
    1039          61 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, this);
    1040          61 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
    1041             :                                VSICurlStreamingHandleReceivedBytes);
    1042             : 
    1043          61 :     m_szCurlErrBuf[0] = '\0';
    1044          61 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER,
    1045             :                                m_szCurlErrBuf);
    1046             : 
    1047          61 :     void *old_handler = CPLHTTPIgnoreSigPipe();
    1048          61 :     CURLcode eRet = curl_easy_perform(hCurlHandle);
    1049          61 :     CPLHTTPRestoreSigPipeHandler(old_handler);
    1050          61 :     if (headers != nullptr)
    1051          43 :         curl_slist_free_all(headers);
    1052             : 
    1053          61 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, nullptr);
    1054          61 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION, nullptr);
    1055          61 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA, nullptr);
    1056          61 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION, nullptr);
    1057             : 
    1058          61 :     AcquireMutex();
    1059          61 :     m_bErrorOccurredInThread = eRet != CURLE_OK;
    1060          61 :     if (m_bErrorOccurredInThread)
    1061             :     {
    1062             :         // For autotest purposes only !
    1063           9 :         const char *pszSimulatedCurlError = CPLGetConfigOption(
    1064             :             "CPL_VSIL_CURL_STREMAING_SIMULATED_CURL_ERROR", nullptr);
    1065           9 :         if (pszSimulatedCurlError)
    1066           5 :             snprintf(m_szCurlErrBuf, sizeof(m_szCurlErrBuf), "%s",
    1067             :                      pszSimulatedCurlError);
    1068             :     }
    1069             : 
    1070          61 :     if (!bAskDownloadEnd && eRet == CURLE_OK && !bHasComputedFileSize)
    1071             :     {
    1072          22 :         FileProp cachedFileProp;
    1073          11 :         m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
    1074          11 :         fileSize = nBodySize;
    1075          11 :         cachedFileProp.fileSize = fileSize;
    1076          11 :         bHasComputedFileSize = true;
    1077          11 :         cachedFileProp.bHasComputedFileSize = bHasComputedFileSize;
    1078          11 :         m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
    1079             :         if (ENABLE_DEBUG)
    1080             :             CPLDebug("VSICURL", "File size = " CPL_FRMT_GUIB, fileSize);
    1081             :     }
    1082             : 
    1083          61 :     bDownloadInProgress = FALSE;
    1084          61 :     bDownloadStopped = TRUE;
    1085             : 
    1086             :     // Signal to the consumer that the download has ended.
    1087          61 :     CPLCondSignal(hCondProducer);
    1088          61 :     ReleaseMutex();
    1089             : 
    1090          61 :     curl_easy_cleanup(hCurlHandle);
    1091          61 : }
    1092             : 
    1093          61 : static void VSICurlDownloadInThread(void *pArg)
    1094             : {
    1095          61 :     static_cast<VSICurlStreamingHandle *>(pArg)->DownloadInThread();
    1096          61 : }
    1097             : 
    1098             : /************************************************************************/
    1099             : /*                           StartDownload()                            */
    1100             : /************************************************************************/
    1101             : 
    1102          89 : void VSICurlStreamingHandle::StartDownload()
    1103             : {
    1104          89 :     if (bDownloadInProgress || bDownloadStopped)
    1105          28 :         return;
    1106             : 
    1107          61 :     CPLDebug("VSICURL", "Start download for %s", m_pszURL);
    1108             : 
    1109          61 :     oRingBuffer.Reset();
    1110          61 :     bDownloadInProgress = TRUE;
    1111          61 :     nRingBufferFileOffset = 0;
    1112          61 :     m_bErrorOccurredInThread = false;
    1113          61 :     hThread = CPLCreateJoinableThread(VSICurlDownloadInThread, this);
    1114             : }
    1115             : 
    1116             : /************************************************************************/
    1117             : /*                            StopDownload()                            */
    1118             : /************************************************************************/
    1119             : 
    1120          94 : void VSICurlStreamingHandle::StopDownload()
    1121             : {
    1122          94 :     if (hThread)
    1123             :     {
    1124          61 :         CPLDebug("VSICURL", "Stop download for %s", m_pszURL);
    1125             : 
    1126          61 :         AcquireMutex();
    1127             :         // Signal to the producer that we ask for download interruption.
    1128          61 :         bAskDownloadEnd = TRUE;
    1129          61 :         CPLCondSignal(hCondConsumer);
    1130             : 
    1131             :         // Wait for the producer to have finished.
    1132          72 :         while (bDownloadInProgress)
    1133          11 :             CPLCondWait(hCondProducer, hRingBufferMutex);
    1134             : 
    1135          61 :         bAskDownloadEnd = FALSE;
    1136             : 
    1137          61 :         ReleaseMutex();
    1138             : 
    1139          61 :         CPLJoinThread(hThread);
    1140          61 :         hThread = nullptr;
    1141             :     }
    1142             : 
    1143          94 :     oRingBuffer.Reset();
    1144          94 :     bDownloadStopped = FALSE;
    1145          94 :     m_bErrorOccurredInThread = false;
    1146          94 :     nRingBufferFileOffset = 0;
    1147          94 :     bEOF = false;
    1148          94 : }
    1149             : 
    1150             : /************************************************************************/
    1151             : /*                        PutRingBufferInCache()                        */
    1152             : /************************************************************************/
    1153             : 
    1154          44 : void VSICurlStreamingHandle::PutRingBufferInCache()
    1155             : {
    1156          44 :     if (nRingBufferFileOffset >= BKGND_BUFFER_SIZE)
    1157           0 :         return;
    1158             : 
    1159          44 :     AcquireMutex();
    1160             : 
    1161             :     // Cache any remaining bytes available in the ring buffer.
    1162          44 :     size_t nBufSize = oRingBuffer.GetSize();
    1163          44 :     if (nBufSize > 0)
    1164             :     {
    1165          30 :         if (nRingBufferFileOffset + nBufSize > BKGND_BUFFER_SIZE)
    1166           1 :             nBufSize =
    1167           1 :                 static_cast<size_t>(BKGND_BUFFER_SIZE - nRingBufferFileOffset);
    1168          30 :         GByte *pabyTmp = static_cast<GByte *>(CPLMalloc(nBufSize));
    1169          30 :         oRingBuffer.Read(pabyTmp, nBufSize);
    1170             : 
    1171             :         // Signal to the producer that we have ingested some bytes.
    1172          30 :         CPLCondSignal(hCondConsumer);
    1173             : 
    1174          30 :         AddRegion(nRingBufferFileOffset, nBufSize, pabyTmp);
    1175          30 :         nRingBufferFileOffset += nBufSize;
    1176          30 :         CPLFree(pabyTmp);
    1177             :     }
    1178             : 
    1179          44 :     ReleaseMutex();
    1180             : }
    1181             : 
    1182             : /************************************************************************/
    1183             : /*                                Read()                                */
    1184             : /************************************************************************/
    1185             : 
    1186         122 : size_t VSICurlStreamingHandle::Read(void *const pBuffer, size_t const nBytes)
    1187             : {
    1188         122 :     const size_t nBufferRequestSize = nBytes;
    1189         122 :     const vsi_l_offset curOffsetOri = curOffset;
    1190         122 :     const vsi_l_offset nRingBufferFileOffsetOri = nRingBufferFileOffset;
    1191         122 :     if (nBufferRequestSize == 0)
    1192           0 :         return 0;
    1193             : 
    1194         244 :     CPLHTTPRetryContext oRetryContext(m_oRetryParameters);
    1195             : 
    1196         130 : retry:
    1197         130 :     GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
    1198         130 :     size_t nRemaining = nBufferRequestSize;
    1199             : 
    1200         130 :     AcquireMutex();
    1201             :     // fileSize might be set wrongly to 0, such as
    1202             :     // /vsicurl_streaming/https://query.data.world/s/jgsghstpphjhicstradhy5kpjwrnfy
    1203         130 :     const bool bHasComputedFileSizeLocal = bHasComputedFileSize && fileSize > 0;
    1204         130 :     const vsi_l_offset fileSizeLocal = fileSize;
    1205         130 :     ReleaseMutex();
    1206             : 
    1207         130 :     if (bHasComputedFileSizeLocal && curOffset >= fileSizeLocal)
    1208             :     {
    1209           0 :         CPLDebug("VSICURL",
    1210             :                  "Read attempt beyond end of file (%" PRIu64 " >= %" PRIu64 ")",
    1211           0 :                  static_cast<uint64_t>(curOffset),
    1212             :                  static_cast<uint64_t>(fileSizeLocal));
    1213           0 :         bEOF = true;
    1214             :     }
    1215         130 :     if (bEOF)
    1216           0 :         return 0;
    1217             : 
    1218         130 :     if (curOffset < nRingBufferFileOffset)
    1219          44 :         PutRingBufferInCache();
    1220             : 
    1221             :     if (ENABLE_DEBUG)
    1222             :         CPLDebug("VSICURL", "Read [" CPL_FRMT_GUIB ", " CPL_FRMT_GUIB "[ in %s",
    1223             :                  curOffset, curOffset + nBufferRequestSize, m_pszURL);
    1224             : 
    1225             :     // Can we use the cache?
    1226         130 :     if (pCachedData != nullptr && curOffset < nCachedSize)
    1227             :     {
    1228             :         const size_t nSz =
    1229          47 :             std::min(nRemaining, static_cast<size_t>(nCachedSize - curOffset));
    1230             :         if (ENABLE_DEBUG)
    1231             :             CPLDebug("VSICURL", "Using cache for [%d, %d[ in %s",
    1232             :                      static_cast<int>(curOffset),
    1233             :                      static_cast<int>(curOffset + nSz), m_pszURL);
    1234          47 :         memcpy(pabyBuffer, pCachedData + curOffset, nSz);
    1235          47 :         pabyBuffer += nSz;
    1236          47 :         curOffset += nSz;
    1237          47 :         nRemaining -= nSz;
    1238             :     }
    1239             : 
    1240             :     // Is the request partially covered by the cache and going beyond file size?
    1241         130 :     if (pCachedData != nullptr && bHasComputedFileSizeLocal &&
    1242          63 :         curOffset <= nCachedSize && curOffset + nRemaining > fileSizeLocal &&
    1243          43 :         fileSize == nCachedSize)
    1244             :     {
    1245          28 :         size_t nSz = static_cast<size_t>(nCachedSize - curOffset);
    1246             :         if (ENABLE_DEBUG && nSz != 0)
    1247             :             CPLDebug("VSICURL", "Using cache for [%d, %d[ in %s",
    1248             :                      static_cast<int>(curOffset),
    1249             :                      static_cast<int>(curOffset + nSz), m_pszURL);
    1250          28 :         memcpy(pabyBuffer, pCachedData + curOffset, nSz);
    1251          28 :         pabyBuffer += nSz;
    1252          28 :         curOffset += nSz;
    1253          28 :         nRemaining -= nSz;
    1254          28 :         bEOF = true;
    1255             :     }
    1256             : 
    1257         130 :     bool bErrorOccurred = false;
    1258             : 
    1259             :     // Has a Seek() being done since the last Read()?
    1260         130 :     if (!bEOF && nRemaining > 0 && curOffset != nRingBufferFileOffset)
    1261             :     {
    1262             :         // Backward seek: Need to restart the download from the beginning.
    1263           3 :         if (curOffset < nRingBufferFileOffset)
    1264           0 :             StopDownload();
    1265             : 
    1266           3 :         StartDownload();
    1267             : 
    1268           3 :         const vsi_l_offset SKIP_BUFFER_SIZE = 32768;
    1269           3 :         GByte *pabyTmp = static_cast<GByte *>(CPLMalloc(SKIP_BUFFER_SIZE));
    1270             : 
    1271           3 :         CPLAssert(curOffset >= nRingBufferFileOffset);
    1272           3 :         vsi_l_offset nBytesToSkip = curOffset - nRingBufferFileOffset;
    1273           9 :         while (nBytesToSkip > 0)
    1274             :         {
    1275           8 :             vsi_l_offset nBytesToRead = nBytesToSkip;
    1276             : 
    1277           8 :             AcquireMutex();
    1278           8 :             if (nBytesToRead > oRingBuffer.GetSize())
    1279           7 :                 nBytesToRead = oRingBuffer.GetSize();
    1280           8 :             if (nBytesToRead > SKIP_BUFFER_SIZE)
    1281           0 :                 nBytesToRead = SKIP_BUFFER_SIZE;
    1282           8 :             oRingBuffer.Read(pabyTmp, static_cast<size_t>(nBytesToRead));
    1283             : 
    1284             :             // Signal to the producer that we have ingested some bytes.
    1285           8 :             CPLCondSignal(hCondConsumer);
    1286           8 :             ReleaseMutex();
    1287             : 
    1288           8 :             if (nBytesToRead)
    1289           3 :                 AddRegion(nRingBufferFileOffset,
    1290             :                           static_cast<size_t>(nBytesToRead), pabyTmp);
    1291             : 
    1292           8 :             nBytesToSkip -= nBytesToRead;
    1293           8 :             nRingBufferFileOffset += nBytesToRead;
    1294             : 
    1295           8 :             if (nBytesToRead == 0 && nBytesToSkip != 0)
    1296             :             {
    1297             :                 if (ENABLE_DEBUG)
    1298             :                     CPLDebug("VSICURL",
    1299             :                              "Waiting for writer to produce some bytes...");
    1300             : 
    1301           5 :                 AcquireMutex();
    1302          10 :                 while (oRingBuffer.GetSize() == 0 && bDownloadInProgress)
    1303           5 :                     CPLCondWait(hCondProducer, hRingBufferMutex);
    1304           5 :                 const int bBufferEmpty = (oRingBuffer.GetSize() == 0);
    1305           5 :                 bErrorOccurred = m_bErrorOccurredInThread;
    1306           5 :                 ReleaseMutex();
    1307             : 
    1308           5 :                 if (bBufferEmpty && !bDownloadInProgress)
    1309           2 :                     break;
    1310             :             }
    1311             :         }
    1312             : 
    1313           3 :         CPLFree(pabyTmp);
    1314             : 
    1315           3 :         if (nBytesToSkip != 0 && !bErrorOccurred)
    1316             :         {
    1317           0 :             bEOF = true;
    1318           0 :             return 0;
    1319             :         }
    1320             :     }
    1321             : 
    1322         130 :     if (!bEOF && nRemaining > 0 && !bErrorOccurred)
    1323             :     {
    1324          86 :         StartDownload();
    1325          86 :         CPLAssert(curOffset == nRingBufferFileOffset);
    1326             :     }
    1327             : 
    1328             :     // Fill the destination buffer from the ring buffer.
    1329         274 :     while (!bEOF && nRemaining > 0 && !bErrorOccurred)
    1330             :     {
    1331         174 :         AcquireMutex();
    1332         174 :         size_t nToRead = oRingBuffer.GetSize();
    1333         174 :         if (nToRead > nRemaining)
    1334          53 :             nToRead = nRemaining;
    1335         174 :         oRingBuffer.Read(pabyBuffer, nToRead);
    1336             : 
    1337             :         // Signal to the producer that we have ingested some bytes.
    1338         174 :         CPLCondSignal(hCondConsumer);
    1339         174 :         ReleaseMutex();
    1340             : 
    1341         174 :         if (nToRead)
    1342          85 :             AddRegion(curOffset, nToRead, pabyBuffer);
    1343             : 
    1344         174 :         nRemaining -= nToRead;
    1345         174 :         pabyBuffer += nToRead;
    1346         174 :         curOffset += nToRead;
    1347         174 :         nRingBufferFileOffset += nToRead;
    1348             : 
    1349         174 :         if (nToRead == 0 && nRemaining != 0)
    1350             :         {
    1351             :             if (ENABLE_DEBUG)
    1352             :                 CPLDebug("VSICURL",
    1353             :                          "Waiting for writer to produce some bytes...");
    1354             : 
    1355          89 :             AcquireMutex();
    1356         177 :             while (oRingBuffer.GetSize() == 0 && bDownloadInProgress)
    1357          88 :                 CPLCondWait(hCondProducer, hRingBufferMutex);
    1358          89 :             const bool bBufferEmpty = oRingBuffer.GetSize() == 0;
    1359          89 :             bErrorOccurred = m_bErrorOccurredInThread;
    1360          89 :             ReleaseMutex();
    1361             : 
    1362          89 :             if (bBufferEmpty && !bDownloadInProgress)
    1363          30 :                 break;
    1364             :         }
    1365             :     }
    1366             : 
    1367             :     if (ENABLE_DEBUG)
    1368             :         CPLDebug("VSICURL", "Read(%d) = %d",
    1369             :                  static_cast<int>(nBufferRequestSize),
    1370             :                  static_cast<int>(nBufferRequestSize - nRemaining));
    1371         130 :     size_t nRet = nBufferRequestSize - nRemaining;
    1372         130 :     if (nRet < nBytes)
    1373          61 :         bEOF = true;
    1374             : 
    1375             :     // Give a chance to specialized filesystem to deal with errors to redirect
    1376             :     // elsewhere.
    1377          94 :     if (curOffsetOri == 0 && nRingBufferFileOffsetOri == 0 &&
    1378         248 :         !StopReceivingBytesOnError() && eExists == EXIST_NO &&
    1379          24 :         nRemaining < nBufferRequestSize)
    1380             :     {
    1381          24 :         const size_t nErrorBufferMaxSize = 4096;
    1382             :         std::unique_ptr<GByte, VSIFreeReleaser> pabyErrorBuffer(
    1383          24 :             static_cast<GByte *>(CPLMalloc(nErrorBufferMaxSize + 1)));
    1384          24 :         size_t nRead = nBufferRequestSize - nRemaining;
    1385          24 :         size_t nErrorBufferSize = std::min(nErrorBufferMaxSize, nRead);
    1386          24 :         memcpy(pabyErrorBuffer.get(), pBuffer, nErrorBufferSize);
    1387          24 :         if (nRead < nErrorBufferMaxSize)
    1388          24 :             nErrorBufferSize += Read(pabyErrorBuffer.get() + nRead,
    1389          24 :                                      nErrorBufferMaxSize - nRead);
    1390          24 :         (pabyErrorBuffer.get())[nErrorBufferSize] = 0;
    1391          24 :         StopDownload();
    1392          24 :         if (CanRestartOnError(reinterpret_cast<char *>(pabyErrorBuffer.get()),
    1393          24 :                               reinterpret_cast<char *>(pabyHeaderData), true))
    1394             :         {
    1395           4 :             curOffset = 0;
    1396             : 
    1397           4 :             AcquireMutex();
    1398           4 :             eExists = EXIST_UNKNOWN;
    1399           4 :             bHasComputedFileSize = false;
    1400           4 :             fileSize = 0;
    1401           4 :             ReleaseMutex();
    1402           4 :             nCachedSize = 0;
    1403             : 
    1404           4 :             FileProp cachedFileProp;
    1405           4 :             m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
    1406           4 :             cachedFileProp.bHasComputedFileSize = false;
    1407           4 :             cachedFileProp.fileSize = 0;
    1408           4 :             cachedFileProp.eExists = EXIST_UNKNOWN;
    1409           4 :             m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
    1410             : 
    1411           4 :             goto retry;
    1412             :         }
    1413             :         else
    1414             :         {
    1415          20 :             CPLDebug("VSICURL", "Error buffer: %s",
    1416          20 :                      reinterpret_cast<char *>(pabyErrorBuffer.get()));
    1417          20 :             nRet = 0;
    1418             :         }
    1419             :     }
    1420             : 
    1421         126 :     if (bErrorOccurred)
    1422             :     {
    1423             :         // Look if we should attempt a retry
    1424           7 :         AcquireMutex();
    1425          14 :         const bool bRetry = oRetryContext.CanRetry(static_cast<int>(nHTTPCode),
    1426           7 :                                                    nullptr, m_szCurlErrBuf);
    1427           7 :         ReleaseMutex();
    1428           7 :         if (bRetry)
    1429             :         {
    1430           4 :             StopDownload();
    1431             : 
    1432           4 :             CPLError(CE_Warning, CPLE_AppDefined,
    1433             :                      "HTTP error code: %d - %s. "
    1434             :                      "Retrying again in %.1f secs",
    1435           4 :                      static_cast<int>(nHTTPCode), m_pszURL,
    1436             :                      oRetryContext.GetCurrentDelay());
    1437           4 :             CPLSleep(oRetryContext.GetCurrentDelay());
    1438           4 :             curOffset = curOffsetOri;
    1439           4 :             goto retry;
    1440             :         }
    1441             :     }
    1442             : 
    1443         122 :     if (bErrorOccurred)
    1444           3 :         m_bError = true;
    1445             : 
    1446         122 :     return nRet;
    1447             : }
    1448             : 
    1449             : /************************************************************************/
    1450             : /*                             AddRegion()                              */
    1451             : /************************************************************************/
    1452             : 
    1453         118 : void VSICurlStreamingHandle::AddRegion(vsi_l_offset nFileOffsetStart,
    1454             :                                        size_t nSize, GByte *pData)
    1455             : {
    1456         118 :     if (nFileOffsetStart >= BKGND_BUFFER_SIZE)
    1457           2 :         return;
    1458             : 
    1459         116 :     if (pCachedData == nullptr)
    1460          51 :         pCachedData = static_cast<GByte *>(CPLMalloc(BKGND_BUFFER_SIZE));
    1461             : 
    1462         116 :     if (nFileOffsetStart <= nCachedSize &&
    1463         116 :         nFileOffsetStart + nSize > nCachedSize)
    1464             :     {
    1465             :         const size_t nSz = std::min(
    1466         113 :             nSize, static_cast<size_t>(BKGND_BUFFER_SIZE - nFileOffsetStart));
    1467             :         if (ENABLE_DEBUG)
    1468             :             CPLDebug("VSICURL", "Writing [%d, %d[ in cache for %s",
    1469             :                      static_cast<int>(nFileOffsetStart),
    1470             :                      static_cast<int>(nFileOffsetStart + nSz), m_pszURL);
    1471         113 :         memcpy(pCachedData + nFileOffsetStart, pData, nSz);
    1472         113 :         nCachedSize = static_cast<size_t>(nFileOffsetStart + nSz);
    1473             :     }
    1474             : }
    1475             : 
    1476             : /************************************************************************/
    1477             : /*                               Write()                                */
    1478             : /************************************************************************/
    1479             : 
    1480           0 : size_t VSICurlStreamingHandle::Write(const void * /* pBuffer */,
    1481             :                                      size_t /* nBytes */)
    1482             : {
    1483           0 :     return 0;
    1484             : }
    1485             : 
    1486             : /************************************************************************/
    1487             : /*                                Eof()                                 */
    1488             : /************************************************************************/
    1489             : 
    1490           4 : int VSICurlStreamingHandle::Eof()
    1491             : {
    1492           4 :     return bEOF;
    1493             : }
    1494             : 
    1495             : /************************************************************************/
    1496             : /*                               Error()                                */
    1497             : /************************************************************************/
    1498             : 
    1499          11 : int VSICurlStreamingHandle::Error()
    1500             : 
    1501             : {
    1502          11 :     return m_bError;
    1503             : }
    1504             : 
    1505             : /************************************************************************/
    1506             : /*                              ClearErr()                              */
    1507             : /************************************************************************/
    1508             : 
    1509           0 : void VSICurlStreamingHandle::ClearErr()
    1510             : {
    1511           0 :     bEOF = false;
    1512           0 :     m_bError = false;
    1513           0 : }
    1514             : 
    1515             : /************************************************************************/
    1516             : /*                               Flush()                                */
    1517             : /************************************************************************/
    1518             : 
    1519           0 : int VSICurlStreamingHandle::Flush()
    1520             : {
    1521           0 :     return 0;
    1522             : }
    1523             : 
    1524             : /************************************************************************/
    1525             : /*                               Close()                                */
    1526             : /************************************************************************/
    1527             : 
    1528          31 : int VSICurlStreamingHandle::Close()
    1529             : {
    1530          31 :     return 0;
    1531             : }
    1532             : 
    1533             : /************************************************************************/
    1534             : /*                     VSICurlStreamingFSHandler()                      */
    1535             : /************************************************************************/
    1536             : 
    1537       10734 : VSICurlStreamingFSHandler::VSICurlStreamingFSHandler()
    1538       10734 :     : oCacheFileProp{100 * 1024}
    1539             : {
    1540       10734 :     hMutex = CPLCreateMutex();
    1541       10734 :     CPLReleaseMutex(hMutex);
    1542       10734 : }
    1543             : 
    1544             : /************************************************************************/
    1545             : /*                     ~VSICurlStreamingFSHandler()                     */
    1546             : /************************************************************************/
    1547             : 
    1548        6756 : VSICurlStreamingFSHandler::~VSICurlStreamingFSHandler()
    1549             : {
    1550        6756 :     VSICurlStreamingFSHandler::ClearCache();
    1551             : 
    1552        6756 :     CPLDestroyMutex(hMutex);
    1553        6756 :     hMutex = nullptr;
    1554        6756 : }
    1555             : 
    1556             : /************************************************************************/
    1557             : /*                             ClearCache()                             */
    1558             : /************************************************************************/
    1559             : 
    1560        8922 : void VSICurlStreamingFSHandler::ClearCache()
    1561             : {
    1562       17844 :     CPLMutexHolder oHolder(&hMutex);
    1563             : 
    1564             :     {
    1565          61 :         const auto lambda = [](const lru11::KeyValuePair<std::string, bool> &kv)
    1566          61 :         { VSICURLInvalidateCachedFileProp(kv.key.c_str()); };
    1567        8922 :         oCacheFileProp.cwalk(lambda);
    1568        8922 :         oCacheFileProp.clear();
    1569             :     }
    1570        8922 : }
    1571             : 
    1572             : /************************************************************************/
    1573             : /*                            AcquireMutex()                            */
    1574             : /************************************************************************/
    1575             : 
    1576           0 : void VSICurlStreamingFSHandler::AcquireMutex()
    1577             : {
    1578           0 :     CPLAcquireMutex(hMutex, 1000.0);
    1579           0 : }
    1580             : 
    1581             : /************************************************************************/
    1582             : /*                            ReleaseMutex()                            */
    1583             : /************************************************************************/
    1584             : 
    1585           0 : void VSICurlStreamingFSHandler::ReleaseMutex()
    1586             : {
    1587           0 :     CPLReleaseMutex(hMutex);
    1588           0 : }
    1589             : 
    1590             : /************************************************************************/
    1591             : /*                          CreateFileHandle()                          */
    1592             : /************************************************************************/
    1593             : 
    1594             : VSICurlStreamingHandle *
    1595          11 : VSICurlStreamingFSHandler::CreateFileHandle(const char *pszFilename,
    1596             :                                             const char *pszURL)
    1597             : {
    1598          11 :     return new VSICurlStreamingHandle(this, pszFilename, pszURL);
    1599             : }
    1600             : 
    1601             : /************************************************************************/
    1602             : /*                                Open()                                */
    1603             : /************************************************************************/
    1604             : 
    1605             : VSIVirtualHandleUniquePtr
    1606          60 : VSICurlStreamingFSHandler::Open(const char *pszFilename, const char *pszAccess,
    1607             :                                 bool /* bSetError */, CSLConstList papszOptions)
    1608             : {
    1609          60 :     if (!STARTS_WITH_CI(pszFilename, GetFSPrefix()))
    1610           2 :         return nullptr;
    1611             : 
    1612          58 :     if (strchr(pszAccess, 'w') != nullptr || strchr(pszAccess, '+') != nullptr)
    1613             :     {
    1614           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1615             :                  "Only read-only mode is supported for %s",
    1616           0 :                  GetFSPrefix().c_str());
    1617           0 :         return nullptr;
    1618             :     }
    1619             : 
    1620             :     auto poHandle = std::unique_ptr<VSICurlStreamingHandle>(
    1621         116 :         CreateFileHandle(pszFilename, pszFilename + GetFSPrefix().size()));
    1622             :     // If we didn't get a filelist, check that the file really exists.
    1623          58 :     if (poHandle == nullptr || !poHandle->Exists(pszFilename, papszOptions))
    1624             :     {
    1625          27 :         return nullptr;
    1626             :     }
    1627             : 
    1628          31 :     if (CPLTestBool(CPLGetConfigOption("VSI_CACHE", "FALSE")))
    1629             :         return VSIVirtualHandleUniquePtr(
    1630           0 :             VSICreateCachedFile(poHandle.release()));
    1631             : 
    1632          31 :     return VSIVirtualHandleUniquePtr(poHandle.release());
    1633             : }
    1634             : 
    1635             : /************************************************************************/
    1636             : /*                         GetCachedFileProp()                          */
    1637             : /************************************************************************/
    1638             : 
    1639         233 : bool VSICurlStreamingFSHandler::GetCachedFileProp(const char *pszURL,
    1640             :                                                   FileProp &oFileProp)
    1641             : {
    1642         466 :     CPLMutexHolder oHolder(&hMutex);
    1643             :     bool inCache;
    1644         233 :     if (oCacheFileProp.tryGet(std::string(pszURL), inCache))
    1645             :     {
    1646         170 :         if (VSICURLGetCachedFileProp(pszURL, oFileProp))
    1647             :         {
    1648         170 :             return true;
    1649             :         }
    1650           0 :         oCacheFileProp.remove(std::string(pszURL));
    1651             :     }
    1652          63 :     return false;
    1653             : }
    1654             : 
    1655             : /************************************************************************/
    1656             : /*                         SetCachedFileProp()                          */
    1657             : /************************************************************************/
    1658             : 
    1659         233 : void VSICurlStreamingFSHandler::SetCachedFileProp(const char *pszURL,
    1660             :                                                   FileProp &oFileProp)
    1661             : {
    1662         466 :     CPLMutexHolder oHolder(&hMutex);
    1663         233 :     oCacheFileProp.insert(std::string(pszURL), true);
    1664         233 :     VSICURLSetCachedFileProp(pszURL, oFileProp);
    1665         233 : }
    1666             : 
    1667             : /************************************************************************/
    1668             : /*                                Stat()                                */
    1669             : /************************************************************************/
    1670             : 
    1671          12 : int VSICurlStreamingFSHandler::Stat(const char *pszFilename,
    1672             :                                     VSIStatBufL *pStatBuf, int nFlags)
    1673             : {
    1674          12 :     if (!STARTS_WITH_CI(pszFilename, GetFSPrefix()))
    1675           2 :         return -1;
    1676             : 
    1677          10 :     if ((nFlags & VSI_STAT_CACHE_ONLY) != 0)
    1678             :     {
    1679             :         const std::string osVSICURLFilename =
    1680           0 :             std::string("/vsicurl/") + (pszFilename + GetFSPrefix().size());
    1681           0 :         return VSIStatExL(osVSICURLFilename.c_str(), pStatBuf, nFlags);
    1682             :     }
    1683             : 
    1684          10 :     memset(pStatBuf, 0, sizeof(VSIStatBufL));
    1685             : 
    1686             :     VSICurlStreamingHandle *poHandle =
    1687          10 :         CreateFileHandle(pszFilename, pszFilename + GetFSPrefix().size());
    1688          10 :     if (poHandle == nullptr)
    1689             :     {
    1690           0 :         return -1;
    1691             :     }
    1692          19 :     if (poHandle->IsKnownFileSize() ||
    1693           9 :         ((nFlags & VSI_STAT_SIZE_FLAG) && !poHandle->IsDirectory() &&
    1694           9 :          CPLTestBool(CPLGetConfigOption("CPL_VSIL_CURL_SLOW_GET_SIZE", "YES"))))
    1695             :     {
    1696          10 :         pStatBuf->st_size = poHandle->GetFileSize();
    1697             :     }
    1698             : 
    1699          10 :     int nRet = (poHandle->Exists(pszFilename, nullptr)) ? 0 : -1;
    1700          10 :     pStatBuf->st_mode = poHandle->IsDirectory() ? S_IFDIR : S_IFREG;
    1701             : 
    1702          10 :     delete poHandle;
    1703          10 :     return nRet;
    1704             : }
    1705             : 
    1706             : /************************************************************************/
    1707             : /*                            GetActualURL()                            */
    1708             : /************************************************************************/
    1709             : 
    1710           3 : const char *VSICurlStreamingFSHandler::GetActualURL(const char *pszFilename)
    1711             : {
    1712           3 :     if (!STARTS_WITH_CI(pszFilename, GetFSPrefix()))
    1713           0 :         return pszFilename;
    1714             :     auto poHandle = std::unique_ptr<VSICurlStreamingHandle>(
    1715           6 :         CreateFileHandle(pszFilename, pszFilename + GetFSPrefix().size()));
    1716           3 :     if (poHandle == nullptr)
    1717           0 :         return pszFilename;
    1718           3 :     return CPLSPrintf("%s", poHandle->GetURL());
    1719             : }
    1720             : 
    1721             : /************************************************************************/
    1722             : /*                      GetNonStreamingFilename()                       */
    1723             : /************************************************************************/
    1724             : 
    1725          66 : std::string VSICurlStreamingFSHandler::GetNonStreamingFilename(
    1726             :     const std::string &osFilename) const
    1727             : {
    1728          66 :     if (STARTS_WITH(osFilename.c_str(), GetFSPrefix().c_str()))
    1729         132 :         return GetNonStreamingPrefix() +
    1730         198 :                osFilename.substr(GetFSPrefix().size());
    1731           0 :     return osFilename;
    1732             : }
    1733             : 
    1734             : /************************************************************************/
    1735             : /*                     IVSIS3LikeStreamingFSHandler                     */
    1736             : /************************************************************************/
    1737             : 
    1738             : class IVSIS3LikeStreamingFSHandler : public VSICurlStreamingFSHandler
    1739             : {
    1740             :     CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeStreamingFSHandler)
    1741             : 
    1742             :   public:
    1743        8945 :     IVSIS3LikeStreamingFSHandler() = default;
    1744             : 
    1745             :     char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
    1746             : 
    1747           5 :     const char *GetOptions() override
    1748             :     {
    1749           5 :         return VSIGetFileSystemOptions(GetNonStreamingPrefix().c_str());
    1750             :     }
    1751             : };
    1752             : 
    1753           1 : char **IVSIS3LikeStreamingFSHandler::ReadDirEx(const char *pszDirname,
    1754             :                                                int nMaxFiles)
    1755             : {
    1756           1 :     if (STARTS_WITH(pszDirname, GetFSPrefix()))
    1757             :     {
    1758           1 :         return VSIReadDirEx(
    1759           2 :             (GetNonStreamingPrefix() + (pszDirname + GetFSPrefix().size()))
    1760             :                 .c_str(),
    1761           1 :             nMaxFiles);
    1762             :     }
    1763           0 :     return nullptr;
    1764             : }
    1765             : 
    1766             : /************************************************************************/
    1767             : /*                       VSIS3StreamingFSHandler                        */
    1768             : /************************************************************************/
    1769             : 
    1770             : class VSIS3StreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
    1771             : {
    1772             :     CPL_DISALLOW_COPY_ASSIGN(VSIS3StreamingFSHandler)
    1773             : 
    1774             :   protected:
    1775         228 :     CPLString GetFSPrefix() const override
    1776             :     {
    1777         228 :         return "/vsis3_streaming/";
    1778             :     }
    1779             : 
    1780          33 :     std::string GetNonStreamingPrefix() const override
    1781             :     {
    1782          33 :         return "/vsis3/";
    1783             :     }
    1784             : 
    1785             :     VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
    1786             :                                              const char *pszURL) override;
    1787             : 
    1788             :   public:
    1789        1789 :     VSIS3StreamingFSHandler() = default;
    1790        1126 :     ~VSIS3StreamingFSHandler() override = default;
    1791             : 
    1792         361 :     void ClearCache() override
    1793             :     {
    1794         361 :         IVSIS3LikeStreamingFSHandler::ClearCache();
    1795         361 :         VSIS3UpdateParams::ClearCache();
    1796         361 :     }
    1797             : };
    1798             : 
    1799             : /************************************************************************/
    1800             : /*                       VSIS3LikeStreamingHandle                       */
    1801             : /************************************************************************/
    1802             : 
    1803             : class VSIS3LikeStreamingHandle final : public VSICurlStreamingHandle
    1804             : {
    1805             :     CPL_DISALLOW_COPY_ASSIGN(VSIS3LikeStreamingHandle)
    1806             : 
    1807             :     IVSIS3LikeHandleHelper *m_poS3HandleHelper = nullptr;
    1808             : 
    1809             :   protected:
    1810             :     struct curl_slist *GetCurlHeaders(const CPLString &osVerb,
    1811             :                                       struct curl_slist *psHeaders) override;
    1812             : 
    1813          71 :     bool StopReceivingBytesOnError() override
    1814             :     {
    1815          71 :         return false;
    1816             :     }
    1817             : 
    1818             :     bool CanRestartOnError(const char *pszErrorMsg, const char *pszHeaders,
    1819             :                            bool bSetError) override;
    1820             : 
    1821         686 :     bool InterpretRedirect() override
    1822             :     {
    1823         686 :         return false;
    1824             :     }
    1825             : 
    1826             :   public:
    1827             :     VSIS3LikeStreamingHandle(IVSIS3LikeStreamingFSHandler *poFS,
    1828             :                              const char *pszFilename,
    1829             :                              IVSIS3LikeHandleHelper *poS3HandleHelper);
    1830             :     ~VSIS3LikeStreamingHandle() override;
    1831             : };
    1832             : 
    1833             : /************************************************************************/
    1834             : /*                          CreateFileHandle()                          */
    1835             : /************************************************************************/
    1836             : 
    1837             : VSICurlStreamingHandle *
    1838          32 : VSIS3StreamingFSHandler::CreateFileHandle(const char *pszFilename,
    1839             :                                           const char *pszURL)
    1840             : {
    1841             :     VSIS3HandleHelper *poS3HandleHelper =
    1842          32 :         VSIS3HandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str(), false);
    1843          32 :     if (poS3HandleHelper)
    1844             :     {
    1845             :         return new VSIS3LikeStreamingHandle(this, pszFilename,
    1846          31 :                                             poS3HandleHelper);
    1847             :     }
    1848           1 :     return nullptr;
    1849             : }
    1850             : 
    1851             : /************************************************************************/
    1852             : /*                      VSIS3LikeStreamingHandle()                      */
    1853             : /************************************************************************/
    1854             : 
    1855          55 : VSIS3LikeStreamingHandle::VSIS3LikeStreamingHandle(
    1856             :     IVSIS3LikeStreamingFSHandler *poFS, const char *pszFilename,
    1857          55 :     IVSIS3LikeHandleHelper *poS3HandleHelper)
    1858             :     : VSICurlStreamingHandle(poFS, pszFilename,
    1859         110 :                              poS3HandleHelper->GetURL().c_str()),
    1860          55 :       m_poS3HandleHelper(poS3HandleHelper)
    1861             : {
    1862          55 : }
    1863             : 
    1864             : /************************************************************************/
    1865             : /*                     ~VSIS3LikeStreamingHandle()                      */
    1866             : /************************************************************************/
    1867             : 
    1868         110 : VSIS3LikeStreamingHandle::~VSIS3LikeStreamingHandle()
    1869             : {
    1870          55 :     delete m_poS3HandleHelper;
    1871         110 : }
    1872             : 
    1873             : /************************************************************************/
    1874             : /*                           GetCurlHeaders()                           */
    1875             : /************************************************************************/
    1876             : 
    1877             : struct curl_slist *
    1878          55 : VSIS3LikeStreamingHandle::GetCurlHeaders(const CPLString &osVerb,
    1879             :                                          struct curl_slist *psHeaders)
    1880             : {
    1881          55 :     return m_poS3HandleHelper->GetCurlHeaders(osVerb, psHeaders);
    1882             : }
    1883             : 
    1884             : /************************************************************************/
    1885             : /*                         CanRestartOnError()                          */
    1886             : /************************************************************************/
    1887             : 
    1888          24 : bool VSIS3LikeStreamingHandle::CanRestartOnError(const char *pszErrorMsg,
    1889             :                                                  const char *pszHeaders,
    1890             :                                                  bool bSetError)
    1891             : {
    1892          24 :     if (m_poS3HandleHelper->CanRestartOnError(pszErrorMsg, pszHeaders,
    1893          24 :                                               bSetError))
    1894             :     {
    1895           4 :         SetURL(m_poS3HandleHelper->GetURL().c_str());
    1896           4 :         return true;
    1897             :     }
    1898          20 :     return false;
    1899             : }
    1900             : 
    1901             : /************************************************************************/
    1902             : /*                       VSIGSStreamingFSHandler                        */
    1903             : /************************************************************************/
    1904             : 
    1905             : class VSIGSStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
    1906             : {
    1907             :   protected:
    1908          67 :     CPLString GetFSPrefix() const override
    1909             :     {
    1910          67 :         return "/vsigs_streaming/";
    1911             :     }
    1912             : 
    1913          10 :     std::string GetNonStreamingPrefix() const override
    1914             :     {
    1915          10 :         return "/vsigs/";
    1916             :     }
    1917             : 
    1918             :     VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
    1919             :                                              const char *pszURL) override;
    1920             : 
    1921             :   public:
    1922        1789 :     VSIGSStreamingFSHandler()
    1923        1789 :     {
    1924        1789 :     }
    1925             : 
    1926        1126 :     ~VSIGSStreamingFSHandler() override
    1927        1126 :     {
    1928        1126 :     }
    1929             : };
    1930             : 
    1931             : /************************************************************************/
    1932             : /*                          CreateFileHandle()                          */
    1933             : /************************************************************************/
    1934             : 
    1935             : VSICurlStreamingHandle *
    1936          10 : VSIGSStreamingFSHandler::CreateFileHandle(const char *pszFilename,
    1937             :                                           const char *pszURL)
    1938             : {
    1939             :     VSIGSHandleHelper *poGCHandleHelper =
    1940          10 :         VSIGSHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str());
    1941          10 :     if (poGCHandleHelper)
    1942             :     {
    1943             :         return new VSIS3LikeStreamingHandle(this, pszFilename,
    1944           9 :                                             poGCHandleHelper);
    1945             :     }
    1946           1 :     return nullptr;
    1947             : }
    1948             : 
    1949             : /************************************************************************/
    1950             : /*                      VSIAzureStreamingFSHandler                      */
    1951             : /************************************************************************/
    1952             : 
    1953             : class VSIAzureStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
    1954             : {
    1955             :   protected:
    1956          39 :     CPLString GetFSPrefix() const override
    1957             :     {
    1958          39 :         return "/vsiaz_streaming/";
    1959             :     }
    1960             : 
    1961           6 :     std::string GetNonStreamingPrefix() const override
    1962             :     {
    1963           6 :         return "/vsiaz/";
    1964             :     }
    1965             : 
    1966             :     VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
    1967             :                                              const char *pszURL) override;
    1968             : 
    1969             :   public:
    1970        1789 :     VSIAzureStreamingFSHandler()
    1971        1789 :     {
    1972        1789 :     }
    1973             : 
    1974        1126 :     ~VSIAzureStreamingFSHandler() override
    1975        1126 :     {
    1976        1126 :     }
    1977             : };
    1978             : 
    1979             : /************************************************************************/
    1980             : /*                          CreateFileHandle()                          */
    1981             : /************************************************************************/
    1982             : 
    1983             : VSICurlStreamingHandle *
    1984           6 : VSIAzureStreamingFSHandler::CreateFileHandle(const char *pszFilename,
    1985             :                                              const char *pszURL)
    1986             : {
    1987             :     VSIAzureBlobHandleHelper *poHandleHelper =
    1988           6 :         VSIAzureBlobHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str());
    1989           6 :     if (poHandleHelper)
    1990             :     {
    1991           5 :         return new VSIS3LikeStreamingHandle(this, pszFilename, poHandleHelper);
    1992             :     }
    1993           1 :     return nullptr;
    1994             : }
    1995             : 
    1996             : /************************************************************************/
    1997             : /*                       VSIOSSStreamingFSHandler                       */
    1998             : /************************************************************************/
    1999             : 
    2000             : class VSIOSSStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
    2001             : {
    2002             :     CPL_DISALLOW_COPY_ASSIGN(VSIOSSStreamingFSHandler)
    2003             : 
    2004             :   protected:
    2005          60 :     CPLString GetFSPrefix() const override
    2006             :     {
    2007          60 :         return "/vsioss_streaming/";
    2008             :     }
    2009             : 
    2010           9 :     std::string GetNonStreamingPrefix() const override
    2011             :     {
    2012           9 :         return "/vsioss/";
    2013             :     }
    2014             : 
    2015             :     VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
    2016             :                                              const char *pszURL) override;
    2017             : 
    2018             :   public:
    2019        1789 :     VSIOSSStreamingFSHandler() = default;
    2020        1126 :     ~VSIOSSStreamingFSHandler() override = default;
    2021             : 
    2022         361 :     void ClearCache() override
    2023             :     {
    2024         361 :         IVSIS3LikeStreamingFSHandler::ClearCache();
    2025         361 :         VSIOSSUpdateParams::ClearCache();
    2026         361 :     }
    2027             : };
    2028             : 
    2029             : /************************************************************************/
    2030             : /*                          CreateFileHandle()                          */
    2031             : /************************************************************************/
    2032             : 
    2033             : VSICurlStreamingHandle *
    2034           9 : VSIOSSStreamingFSHandler::CreateFileHandle(const char *pszFilename,
    2035             :                                            const char *pszURL)
    2036             : {
    2037             :     VSIOSSHandleHelper *poOSSHandleHelper =
    2038           9 :         VSIOSSHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str(), false);
    2039           9 :     if (poOSSHandleHelper)
    2040             :     {
    2041             :         return new VSIS3LikeStreamingHandle(this, pszFilename,
    2042           8 :                                             poOSSHandleHelper);
    2043             :     }
    2044           1 :     return nullptr;
    2045             : }
    2046             : 
    2047             : /************************************************************************/
    2048             : /*                      VSISwiftStreamingFSHandler                      */
    2049             : /************************************************************************/
    2050             : 
    2051             : class VSISwiftStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
    2052             : {
    2053             :   protected:
    2054          18 :     CPLString GetFSPrefix() const override
    2055             :     {
    2056          18 :         return "/vsiswift_streaming/";
    2057             :     }
    2058             : 
    2059           3 :     std::string GetNonStreamingPrefix() const override
    2060             :     {
    2061           3 :         return "/vsiswift/";
    2062             :     }
    2063             : 
    2064             :     VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
    2065             :                                              const char *pszURL) override;
    2066             : 
    2067             :   public:
    2068        1789 :     VSISwiftStreamingFSHandler()
    2069        1789 :     {
    2070        1789 :     }
    2071             : 
    2072        1126 :     ~VSISwiftStreamingFSHandler() override
    2073        1126 :     {
    2074        1126 :     }
    2075             : };
    2076             : 
    2077             : /************************************************************************/
    2078             : /*                          CreateFileHandle()                          */
    2079             : /************************************************************************/
    2080             : 
    2081             : VSICurlStreamingHandle *
    2082           3 : VSISwiftStreamingFSHandler::CreateFileHandle(const char *pszFilename,
    2083             :                                              const char *pszURL)
    2084             : {
    2085             :     VSISwiftHandleHelper *poHandleHelper =
    2086           3 :         VSISwiftHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str());
    2087           3 :     if (poHandleHelper)
    2088             :     {
    2089           2 :         return new VSIS3LikeStreamingHandle(this, pszFilename, poHandleHelper);
    2090             :     }
    2091           1 :     return nullptr;
    2092             : }
    2093             : 
    2094             : //! @endcond
    2095             : 
    2096             : } /* namespace cpl */
    2097             : 
    2098             : /************************************************************************/
    2099             : /*                 VSIInstallCurlStreamingFileHandler()                 */
    2100             : /************************************************************************/
    2101             : 
    2102             : /*!
    2103             :  \brief Install /vsicurl_streaming/ HTTP/FTP file system handler (requires
    2104             :  libcurl).
    2105             : 
    2106             :   \verbatim embed:rst
    2107             :  See :ref:`/vsicurl_streaming/ documentation <vsicurl_streaming>`
    2108             :  \endverbatim
    2109             : 
    2110             :  */
    2111        1789 : void VSIInstallCurlStreamingFileHandler(void)
    2112             : {
    2113        1789 :     VSIFileManager::InstallHandler(
    2114             :         "/vsicurl_streaming/",
    2115        3578 :         std::make_shared<cpl::VSICurlStreamingFSHandler>());
    2116        1789 : }
    2117             : 
    2118             : /************************************************************************/
    2119             : /*                  VSIInstallS3StreamingFileHandler()                  */
    2120             : /************************************************************************/
    2121             : 
    2122             : /*!
    2123             :  \brief Install /vsis3_streaming/ Amazon S3 file system handler (requires
    2124             :  libcurl).
    2125             : 
    2126             :   \verbatim embed:rst
    2127             :  See :ref:`/vsis3_streaming/ documentation <vsis3_streaming>`
    2128             :  \endverbatim
    2129             : 
    2130             :  */
    2131        1789 : void VSIInstallS3StreamingFileHandler(void)
    2132             : {
    2133        1789 :     VSIFileManager::InstallHandler(
    2134        3578 :         "/vsis3_streaming/", std::make_shared<cpl::VSIS3StreamingFSHandler>());
    2135        1789 : }
    2136             : 
    2137             : /************************************************************************/
    2138             : /*                  VSIInstallGSStreamingFileHandler()                  */
    2139             : /************************************************************************/
    2140             : 
    2141             : /*!
    2142             :  \brief Install /vsigs_streaming/ Google Cloud Storage file system handler
    2143             :  (requires libcurl)
    2144             : 
    2145             :   \verbatim embed:rst
    2146             :  See :ref:`/vsigs_streaming/ documentation <vsigs_streaming>`
    2147             :  \endverbatim
    2148             : 
    2149             :  */
    2150             : 
    2151        1789 : void VSIInstallGSStreamingFileHandler(void)
    2152             : {
    2153        1789 :     VSIFileManager::InstallHandler(
    2154        3578 :         "/vsigs_streaming/", std::make_shared<cpl::VSIGSStreamingFSHandler>());
    2155        1789 : }
    2156             : 
    2157             : /************************************************************************/
    2158             : /*                VSIInstallAzureStreamingFileHandler()                 */
    2159             : /************************************************************************/
    2160             : 
    2161             : /*!
    2162             :  \brief Install /vsiaz_streaming/ Microsoft Azure Blob file system handler
    2163             :  (requires libcurl)
    2164             : 
    2165             :   \verbatim embed:rst
    2166             :  See :ref:`/vsiaz_streaming/ documentation <vsiaz_streaming>`
    2167             :  \endverbatim
    2168             : 
    2169             :  */
    2170             : 
    2171        1789 : void VSIInstallAzureStreamingFileHandler(void)
    2172             : {
    2173        1789 :     VSIFileManager::InstallHandler(
    2174             :         "/vsiaz_streaming/",
    2175        3578 :         std::make_shared<cpl::VSIAzureStreamingFSHandler>());
    2176        1789 : }
    2177             : 
    2178             : /************************************************************************/
    2179             : /*                 VSIInstallOSSStreamingFileHandler()                  */
    2180             : /************************************************************************/
    2181             : 
    2182             : /*!
    2183             :  \brief Install /vsiaz_streaming/ Alibaba Cloud Object Storage Service (OSS)
    2184             :  (requires libcurl)
    2185             : 
    2186             :   \verbatim embed:rst
    2187             :  See :ref:`/vsioss_streaming/ documentation <vsioss_streaming>`
    2188             :  \endverbatim
    2189             : 
    2190             :  */
    2191             : 
    2192        1789 : void VSIInstallOSSStreamingFileHandler(void)
    2193             : {
    2194        1789 :     VSIFileManager::InstallHandler(
    2195             :         "/vsioss_streaming/",
    2196        3578 :         std::make_shared<cpl::VSIOSSStreamingFSHandler>());
    2197        1789 : }
    2198             : 
    2199             : /************************************************************************/
    2200             : /*                VSIInstallSwiftStreamingFileHandler()                 */
    2201             : /************************************************************************/
    2202             : 
    2203             : /*!
    2204             :  \brief Install /vsiswift_streaming/ OpenStack Swif Object Storage (Swift) file
    2205             :  system handler (requires libcurl)
    2206             : 
    2207             :   \verbatim embed:rst
    2208             :  See :ref:`/vsiswift_streaming/ documentation <vsiswift_streaming>`
    2209             :  \endverbatim
    2210             : 
    2211             :  */
    2212             : 
    2213        1789 : void VSIInstallSwiftStreamingFileHandler(void)
    2214             : {
    2215        1789 :     VSIFileManager::InstallHandler(
    2216             :         "/vsiswift_streaming/",
    2217        3578 :         std::make_shared<cpl::VSISwiftStreamingFSHandler>());
    2218        1789 : }
    2219             : 
    2220             : //! @cond Doxygen_Suppress
    2221             : 
    2222             : /************************************************************************/
    2223             : /*                     VSICurlStreamingClearCache()                     */
    2224             : /************************************************************************/
    2225             : 
    2226         361 : void VSICurlStreamingClearCache(void)
    2227             : {
    2228             :     // FIXME ? Currently we have different filesystem instances for
    2229             :     // vsicurl/, /vsis3/, /vsigs/ . So each one has its own cache of regions.
    2230             :     // File properties cache are now shared
    2231         722 :     const CPLStringList aosPrefixes(VSIFileManager::GetPrefixes());
    2232       11191 :     for (const char *pszPrefix : aosPrefixes)
    2233             :     {
    2234           0 :         auto poFSHandler = dynamic_cast<cpl::VSICurlStreamingFSHandler *>(
    2235       10830 :             VSIFileManager::GetHandler(pszPrefix));
    2236             : 
    2237       10830 :         if (poFSHandler)
    2238        2166 :             poFSHandler->ClearCache();
    2239             :     }
    2240         361 : }
    2241             : 
    2242             : //! @endcond
    2243             : 
    2244             : #undef ENABLE_DEBUG
    2245             : 
    2246             : #endif  // !defined(HAVE_CURL) || defined(CPL_MULTIPROC_STUB)

Generated by: LCOV version 1.14