LCOV - code coverage report
Current view: top level - port - cpl_vsil_curl_streaming.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 722 833 86.7 %
Date: 2025-07-09 17:50:03 Functions: 103 109 94.5 %

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

Generated by: LCOV version 1.14