LCOV - code coverage report
Current view: top level - port - cpl_vsil_curl_streaming.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 715 825 86.7 %
Date: 2024-04-28 21:03:45 Functions: 99 105 94.3 %

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

Generated by: LCOV version 1.14