• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15127350238

20 May 2025 02:16AM UTC coverage: 70.928% (-0.01%) from 70.939%
15127350238

Pull #12385

github

web-flow
Merge ebdb1426d into fe854d2a4
Pull Request #12385: BuildVRT: Support pixel functions

59 of 64 new or added lines in 4 files covered. (92.19%)

148 existing lines in 48 files now uncovered.

567589 of 800237 relevant lines covered (70.93%)

235402.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.03
/port/cpl_vsil_curl_streaming.cpp
1
/******************************************************************************
2
 *
3
 * Project:  CPL - Common Portability Library
4
 * Purpose:  Implement VSI large file api for HTTP/FTP files in streaming mode
5
 * Author:   Even Rouault <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2012-2015, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12

13
#include "cpl_port.h"
14
#include "cpl_vsi.h"
15
#include "cpl_vsi_virtual.h"
16
#include "cpl_vsil_curl_class.h"
17

18
#include <algorithm>
19
#include <map>
20

21
#include "cpl_aws.h"
22
#include "cpl_google_cloud.h"
23
#include "cpl_azure.h"
24
#include "cpl_alibaba_oss.h"
25
#include "cpl_swift.h"
26
#include "cpl_hash_set.h"
27
#include "cpl_http.h"
28
#include "cpl_multiproc.h"
29
#include "cpl_string.h"
30
#include "cpl_time.h"
31

32
#if !defined(HAVE_CURL) || defined(CPL_MULTIPROC_STUB)
33

34
void VSIInstallCurlStreamingFileHandler(void)
35
{
36
    // Not supported.
37
}
38

39
void VSIInstallS3StreamingFileHandler(void)
40
{
41
    // Not supported.
42
}
43

44
void VSIInstallGSStreamingFileHandler(void)
45
{
46
    // Not supported.
47
}
48

49
void VSIInstallAzureStreamingFileHandler(void)
50
{
51
    // Not supported
52
}
53

54
void VSIInstallOSSStreamingFileHandler(void)
55
{
56
    // Not supported
57
}
58

59
void VSIInstallSwiftStreamingFileHandler(void)
60
{
61
    // Not supported
62
}
63

64
#ifdef HAVE_CURL
65
void VSICurlStreamingClearCache(void)
66
{
67
    // Not supported
68
}
69
#endif
70

71
#else
72

73
//! @cond Doxygen_Suppress
74

75
#include <curl/curl.h>
76

77
#define ENABLE_DEBUG 0
78

79
#define N_MAX_REGIONS 10
80

81
#define BKGND_BUFFER_SIZE (1024 * 1024)
82

83
#define unchecked_curl_easy_setopt(handle, opt, param)                         \
84
    CPL_IGNORE_RET_VAL(curl_easy_setopt(handle, opt, param))
85

86
/************************************************************************/
87
/*                               RingBuffer                             */
88
/************************************************************************/
89

90
class RingBuffer
91
{
92
    CPL_DISALLOW_COPY_ASSIGN(RingBuffer)
93

94
    GByte *pabyBuffer = nullptr;
95
    size_t nCapacity = 0;
96
    size_t nOffset = 0;
97
    size_t nLength = 0;
98

99
  public:
100
    explicit RingBuffer(size_t nCapacity = BKGND_BUFFER_SIZE);
101
    ~RingBuffer();
102

103
    size_t GetCapacity() const
199✔
104
    {
105
        return nCapacity;
199✔
106
    }
107

108
    size_t GetSize() const
861✔
109
    {
110
        return nLength;
861✔
111
    }
112

113
    void Reset();
114
    void Write(void *pBuffer, size_t nSize);
115
    void Read(void *pBuffer, size_t nSize);
116
};
117

118
RingBuffer::RingBuffer(size_t nCapacityIn)
65✔
119
    : pabyBuffer(static_cast<GByte *>(CPLMalloc(nCapacityIn))),
65✔
120
      nCapacity(nCapacityIn)
65✔
121
{
122
}
65✔
123

124
RingBuffer::~RingBuffer()
130✔
125
{
126
    CPLFree(pabyBuffer);
65✔
127
}
65✔
128

129
void RingBuffer::Reset()
153✔
130
{
131
    nOffset = 0;
153✔
132
    nLength = 0;
153✔
133
}
153✔
134

135
void RingBuffer::Write(void *pBuffer, size_t nSize)
197✔
136
{
137
    CPLAssert(nLength + nSize <= nCapacity);
197✔
138

139
    const size_t nEndOffset = (nOffset + nLength) % nCapacity;
197✔
140
    const size_t nSz = std::min(nSize, nCapacity - nEndOffset);
197✔
141
    memcpy(pabyBuffer + nEndOffset, pBuffer, nSz);
197✔
142
    if (nSz < nSize)
197✔
UNCOV
143
        memcpy(pabyBuffer, static_cast<GByte *>(pBuffer) + nSz, nSize - nSz);
×
144

145
    nLength += nSize;
197✔
146
}
197✔
147

148
void RingBuffer::Read(void *pBuffer, size_t nSize)
263✔
149
{
150
    CPLAssert(nSize <= nLength);
263✔
151

152
    if (pBuffer)
263✔
153
    {
154
        const size_t nSz = std::min(nSize, nCapacity - nOffset);
263✔
155
        memcpy(pBuffer, pabyBuffer + nOffset, nSz);
262✔
156
        if (nSz < nSize)
262✔
157
            memcpy(static_cast<GByte *>(pBuffer) + nSz, pabyBuffer,
×
158
                   nSize - nSz);
159
    }
160

161
    nOffset = (nOffset + nSize) % nCapacity;
262✔
162
    nLength -= nSize;
262✔
163
}
262✔
164

165
/************************************************************************/
166

167
namespace
168
{
169

170
typedef struct
171
{
172
    char *pBuffer;
173
    size_t nSize;
174
    int bIsHTTP;
175
    int bIsInHeader;
176
    int nHTTPCode;
177
    int bDownloadHeaderOnly;
178
} WriteFuncStructStreaming;
179

180
}  // namespace
181

182
namespace cpl
183
{
184

185
/************************************************************************/
186
/*                       VSICurlStreamingFSHandler                      */
187
/************************************************************************/
188

189
class VSICurlStreamingHandle;
190

191
class VSICurlStreamingFSHandler : public VSIFilesystemHandler
192
{
193
    CPL_DISALLOW_COPY_ASSIGN(VSICurlStreamingFSHandler)
194

195
    // LRU cache that just keeps in memory if this file system handler is
196
    // spposed to know the file properties of a file. The actual cache is a
197
    // shared one among all network file systems.
198
    // The aim of that design is that invalidating /vsis3/foo results in
199
    // /vsis3_streaming/foo to be invalidated as well.
200
    lru11::Cache<std::string, bool> oCacheFileProp;
201

202
  protected:
203
    CPLMutex *hMutex = nullptr;
204

205
    virtual VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
206
                                                     const char *pszURL);
207

208
    virtual std::string GetNonStreamingPrefix() const
10✔
209
    {
210
        return "/vsicurl/";
10✔
211
    }
212

213
  public:
214
    VSICurlStreamingFSHandler();
215
    virtual ~VSICurlStreamingFSHandler();
216

217
    virtual VSIVirtualHandle *Open(const char *pszFilename,
218
                                   const char *pszAccess, bool bSetError,
219
                                   CSLConstList /* papszOptions */) override;
220
    virtual int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
221
                     int nFlags) override;
222

223
    virtual CPLString GetFSPrefix() const
64✔
224
    {
225
        return "/vsicurl_streaming/";
64✔
226
    }
227

228
    std::string
229
    GetNonStreamingFilename(const std::string &osFilename) const override;
230

231
    const char *GetActualURL(const char *pszFilename) override;
232

233
    const char *GetOptions() override
1✔
234
    {
235
        return VSIGetFileSystemOptions("/vsicurl/");
1✔
236
    }
237

238
    void AcquireMutex();
239
    void ReleaseMutex();
240

241
    bool GetCachedFileProp(const char *pszURL, FileProp &oFileProp);
242
    void SetCachedFileProp(const char *pszURL, FileProp &oFileProp);
243

244
    virtual void ClearCache();
245
};
246

247
/************************************************************************/
248
/*                        VSICurlStreamingHandle                        */
249
/************************************************************************/
250

251
class VSICurlStreamingHandle : public VSIVirtualHandle
252
{
253
    CPL_DISALLOW_COPY_ASSIGN(VSICurlStreamingHandle)
254

255
  protected:
256
    VSICurlStreamingFSHandler *m_poFS = nullptr;
257
    CPLStringList m_aosHTTPOptions{};
258
    const CPLHTTPRetryParameters m_oRetryParameters;
259

260
  private:
261
    char *m_pszURL = nullptr;
262

263
#ifdef notdef
264
    unsigned int nRecomputedChecksumOfFirst1024Bytes = 0;
265
#endif
266
    vsi_l_offset curOffset = 0;
267
    vsi_l_offset fileSize = 0;
268
    bool bHasComputedFileSize = false;
269
    ExistStatus eExists = EXIST_UNKNOWN;
270
    bool bIsDirectory = false;
271

272
    bool bCanTrustCandidateFileSize = true;
273
    bool bHasCandidateFileSize = false;
274
    vsi_l_offset nCandidateFileSize = 0;
275

276
    bool bEOF = false;
277
    bool m_bError = false;
278

279
    size_t nCachedSize = 0;
280
    GByte *pCachedData = nullptr;
281

282
    volatile int bDownloadInProgress = FALSE;
283
    volatile int bDownloadStopped = FALSE;
284
    volatile int bAskDownloadEnd = FALSE;
285
    vsi_l_offset nRingBufferFileOffset = 0;
286
    CPLJoinableThread *hThread = nullptr;
287
    CPLMutex *hRingBufferMutex = nullptr;
288
    CPLCond *hCondProducer = nullptr;
289
    CPLCond *hCondConsumer = nullptr;
290
    RingBuffer oRingBuffer{};
291
    void StartDownload();
292
    void StopDownload();
293
    void PutRingBufferInCache();
294

295
    GByte *pabyHeaderData = nullptr;
296
    size_t nHeaderSize = 0;
297
    vsi_l_offset nBodySize = 0;
298
    int nHTTPCode = 0;
299
    char m_szCurlErrBuf[CURL_ERROR_SIZE + 1];
300
    bool m_bErrorOccurredInThread = false;
301

302
    void AcquireMutex();
303
    void ReleaseMutex();
304

305
    void AddRegion(vsi_l_offset nFileOffsetStart, size_t nSize, GByte *pData);
306

307
  protected:
308
    virtual struct curl_slist *
309
    GetCurlHeaders(const CPLString &,
15✔
310
                   const struct curl_slist * /* psExistingHeaders */)
311
    {
312
        return nullptr;
15✔
313
    }
314

315
    virtual bool StopReceivingBytesOnError()
9✔
316
    {
317
        return true;
9✔
318
    }
319

320
    virtual bool CanRestartOnError(const char * /*pszErrorMsg*/,
×
321
                                   const char * /*pszHeaders*/,
322
                                   bool /*bSetError*/)
323
    {
324
        return false;
×
325
    }
326

327
    virtual bool InterpretRedirect()
125✔
328
    {
329
        return true;
125✔
330
    }
331

332
    void SetURL(const char *pszURL);
333

334
  public:
335
    VSICurlStreamingHandle(VSICurlStreamingFSHandler *poFS,
336
                           const char *pszFilename, const char *pszURL);
337
    ~VSICurlStreamingHandle() override;
338

339
    int Seek(vsi_l_offset nOffset, int nWhence) override;
340
    vsi_l_offset Tell() override;
341
    size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
342
    size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
343
    void ClearErr() override;
344
    int Error() override;
345
    int Eof() override;
346
    int Flush() override;
347
    int Close() override;
348

349
    void DownloadInThread();
350
    size_t ReceivedBytes(GByte *buffer, size_t count, size_t nmemb);
351
    size_t ReceivedBytesHeader(GByte *buffer, size_t count, size_t nmemb);
352

353
    bool IsKnownFileSize() const
10✔
354
    {
355
        return bHasComputedFileSize;
10✔
356
    }
357

358
    vsi_l_offset GetFileSize();
359
    bool Exists(const char *pszFilename, CSLConstList papszOptions);
360

361
    bool IsDirectory() const
19✔
362
    {
363
        return bIsDirectory;
19✔
364
    }
365

366
    const char *GetURL() const
3✔
367
    {
368
        return m_pszURL;
3✔
369
    }
370
};
371

372
/************************************************************************/
373
/*                       VSICurlStreamingHandle()                       */
374
/************************************************************************/
375

376
VSICurlStreamingHandle::VSICurlStreamingHandle(VSICurlStreamingFSHandler *poFS,
65✔
377
                                               const char *pszFilename,
378
                                               const char *pszURL)
65✔
379
    : m_poFS(poFS), m_aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszFilename)),
380
      m_oRetryParameters(m_aosHTTPOptions), m_pszURL(CPLStrdup(pszURL))
65✔
381
{
382
    FileProp cachedFileProp;
65✔
383
    poFS->GetCachedFileProp(pszURL, cachedFileProp);
65✔
384
    eExists = cachedFileProp.eExists;
65✔
385
    fileSize = cachedFileProp.fileSize;
65✔
386
    bHasComputedFileSize = cachedFileProp.bHasComputedFileSize;
65✔
387
    bIsDirectory = cachedFileProp.bIsDirectory;
65✔
388
    poFS->SetCachedFileProp(pszURL, cachedFileProp);
65✔
389

390
    hRingBufferMutex = CPLCreateMutex();
65✔
391
    ReleaseMutex();
65✔
392
    hCondProducer = CPLCreateCond();
65✔
393
    hCondConsumer = CPLCreateCond();
65✔
394

395
    memset(m_szCurlErrBuf, 0, sizeof(m_szCurlErrBuf));
65✔
396
}
65✔
397

398
/************************************************************************/
399
/*                       ~VSICurlStreamingHandle()                      */
400
/************************************************************************/
401

402
VSICurlStreamingHandle::~VSICurlStreamingHandle()
75✔
403
{
404
    StopDownload();
65✔
405

406
    CPLFree(m_pszURL);
65✔
407

408
    CPLFree(pCachedData);
65✔
409

410
    CPLFree(pabyHeaderData);
65✔
411

412
    CPLDestroyMutex(hRingBufferMutex);
65✔
413
    CPLDestroyCond(hCondProducer);
65✔
414
    CPLDestroyCond(hCondConsumer);
65✔
415
}
75✔
416

417
/************************************************************************/
418
/*                            SetURL()                                  */
419
/************************************************************************/
420

421
void VSICurlStreamingHandle::SetURL(const char *pszURLIn)
4✔
422
{
423
    CPLFree(m_pszURL);
4✔
424
    m_pszURL = CPLStrdup(pszURLIn);
4✔
425
}
4✔
426

427
/************************************************************************/
428
/*                         AcquireMutex()                               */
429
/************************************************************************/
430

431
void VSICurlStreamingHandle::AcquireMutex()
1,288✔
432
{
433
    CPLAcquireMutex(hRingBufferMutex, 1000.0);
1,288✔
434
}
1,288✔
435

436
/************************************************************************/
437
/*                          ReleaseMutex()                              */
438
/************************************************************************/
439

440
void VSICurlStreamingHandle::ReleaseMutex()
1,353✔
441
{
442
    CPLReleaseMutex(hRingBufferMutex);
1,353✔
443
}
1,353✔
444

445
/************************************************************************/
446
/*                                Seek()                                */
447
/************************************************************************/
448

449
int VSICurlStreamingHandle::Seek(vsi_l_offset nOffset, int nWhence)
66✔
450
{
451
    if (curOffset >= BKGND_BUFFER_SIZE)
66✔
452
    {
453
        if (ENABLE_DEBUG)
454
            CPLDebug("VSICURL",
455
                     "Invalidating cache and file size due to Seek() "
456
                     "beyond caching zone");
457
        CPLFree(pCachedData);
×
458
        pCachedData = nullptr;
×
459
        nCachedSize = 0;
×
460
        AcquireMutex();
×
461
        bHasComputedFileSize = false;
×
462
        fileSize = 0;
×
463
        ReleaseMutex();
×
464
    }
465

466
    if (nWhence == SEEK_SET)
66✔
467
    {
468
        curOffset = nOffset;
65✔
469
    }
470
    else if (nWhence == SEEK_CUR)
1✔
471
    {
472
        curOffset = curOffset + nOffset;
×
473
    }
474
    else
475
    {
476
        curOffset = GetFileSize() + nOffset;
1✔
477
    }
478
    bEOF = false;
66✔
479
    return 0;
66✔
480
}
481

482
/************************************************************************/
483
/*                  VSICURLStreamingInitWriteFuncStructStreaming() */
484
/************************************************************************/
485

486
static void
487
VSICURLStreamingInitWriteFuncStructStreaming(WriteFuncStructStreaming *psStruct)
20✔
488
{
489
    psStruct->pBuffer = nullptr;
20✔
490
    psStruct->nSize = 0;
20✔
491
    psStruct->bIsHTTP = FALSE;
20✔
492
    psStruct->bIsInHeader = TRUE;
20✔
493
    psStruct->nHTTPCode = 0;
20✔
494
    psStruct->bDownloadHeaderOnly = FALSE;
20✔
495
}
20✔
496

497
/************************************************************************/
498
/*                 VSICurlStreamingHandleWriteFuncForHeader()           */
499
/************************************************************************/
500

501
static size_t VSICurlStreamingHandleWriteFuncForHeader(void *buffer,
55✔
502
                                                       size_t count,
503
                                                       size_t nmemb, void *req)
504
{
505
    WriteFuncStructStreaming *psStruct =
55✔
506
        static_cast<WriteFuncStructStreaming *>(req);
507
    const size_t nSize = count * nmemb;
55✔
508

509
    char *pNewBuffer = static_cast<char *>(
510
        VSIRealloc(psStruct->pBuffer, psStruct->nSize + nSize + 1));
55✔
511
    if (pNewBuffer)
55✔
512
    {
513
        psStruct->pBuffer = pNewBuffer;
55✔
514
        memcpy(psStruct->pBuffer + psStruct->nSize, buffer, nSize);
55✔
515
        psStruct->pBuffer[psStruct->nSize + nSize] = '\0';
55✔
516
        if (psStruct->bIsHTTP && psStruct->bIsInHeader)
55✔
517
        {
518
            char *pszLine = psStruct->pBuffer + psStruct->nSize;
×
519
            if (STARTS_WITH_CI(pszLine, "HTTP/"))
×
520
            {
521
                const char *pszSpace =
522
                    strchr(const_cast<const char *>(pszLine), ' ');
×
523
                if (pszSpace)
×
524
                    psStruct->nHTTPCode = atoi(pszSpace + 1);
×
525
            }
526

527
            if (pszLine[0] == '\r' || pszLine[0] == '\n')
×
528
            {
529
                if (psStruct->bDownloadHeaderOnly)
×
530
                {
531
                    // If moved permanently/temporarily, go on.
532
                    // Otherwise stop now.
533
                    if (!(psStruct->nHTTPCode == 301 ||
×
534
                          psStruct->nHTTPCode == 302 ||
×
535
                          psStruct->nHTTPCode == 303))
×
536
                        return 0;
×
537
                }
538
                else
539
                {
540
                    psStruct->bIsInHeader = FALSE;
×
541
                }
542
            }
543
        }
544
        psStruct->nSize += nSize;
55✔
545
        return nmemb;
55✔
546
    }
547
    else
548
    {
549
        return 0;
×
550
    }
551
}
552

553
/************************************************************************/
554
/*                           GetFileSize()                              */
555
/************************************************************************/
556

557
vsi_l_offset VSICurlStreamingHandle::GetFileSize()
11✔
558
{
559
    WriteFuncStructStreaming sWriteFuncData;
560
    WriteFuncStructStreaming sWriteFuncHeaderData;
561

562
    AcquireMutex();
11✔
563
    if (bHasComputedFileSize)
11✔
564
    {
565
        const vsi_l_offset nRet = fileSize;
1✔
566
        ReleaseMutex();
1✔
567
        return nRet;
1✔
568
    }
569
    ReleaseMutex();
10✔
570

571
    CURL *hLocalHandle = curl_easy_init();
10✔
572

573
    struct curl_slist *headers =
574
        VSICurlSetOptions(hLocalHandle, m_pszURL, m_aosHTTPOptions.List());
10✔
575

576
    VSICURLStreamingInitWriteFuncStructStreaming(&sWriteFuncHeaderData);
10✔
577

578
    // HACK for mbtiles driver: Proper fix would be to auto-detect servers that
579
    // don't accept HEAD http://a.tiles.mapbox.com/v3/ doesn't accept HEAD, so
580
    // let's start a GET and interrupt is as soon as the header is found.
581
    CPLString osVerb;
20✔
582
    if (strstr(m_pszURL, ".tiles.mapbox.com/") != nullptr)
10✔
583
    {
584
        unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HEADERDATA,
×
585
                                   &sWriteFuncHeaderData);
586
        unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HEADERFUNCTION,
×
587
                                   VSICurlStreamingHandleWriteFuncForHeader);
588

589
        sWriteFuncHeaderData.bIsHTTP = STARTS_WITH(m_pszURL, "http");
×
590
        sWriteFuncHeaderData.bDownloadHeaderOnly = TRUE;
×
591
        osVerb = "GET";
×
592
    }
593
    else
594
    {
595
        unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_NOBODY, 1);
10✔
596
        unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HTTPGET, 0);
10✔
597
        unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HEADER, 1);
10✔
598
        osVerb = "HEAD";
10✔
599
    }
600

601
    headers = VSICurlMergeHeaders(headers, GetCurlHeaders(osVerb, headers));
10✔
602
    unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HTTPHEADER, headers);
10✔
603

604
    // We need that otherwise OSGEO4W's libcurl issue a dummy range request
605
    // when doing a HEAD when recycling connections.
606
    unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_RANGE, nullptr);
10✔
607

608
    // Bug with older curl versions (<=7.16.4) and FTP.
609
    // See http://curl.haxx.se/mail/lib-2007-08/0312.html
610
    VSICURLStreamingInitWriteFuncStructStreaming(&sWriteFuncData);
10✔
611
    unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_WRITEDATA,
10✔
612
                               &sWriteFuncData);
613
    unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_WRITEFUNCTION,
10✔
614
                               VSICurlStreamingHandleWriteFuncForHeader);
615

616
    char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
10✔
617
    unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf);
10✔
618

619
    void *old_handler = CPLHTTPIgnoreSigPipe();
10✔
620
    curl_easy_perform(hLocalHandle);
10✔
621
    CPLHTTPRestoreSigPipeHandler(old_handler);
10✔
622
    if (headers != nullptr)
10✔
623
        curl_slist_free_all(headers);
9✔
624

625
    AcquireMutex();
10✔
626

627
    eExists = EXIST_UNKNOWN;
10✔
628
    bHasComputedFileSize = true;
10✔
629

630
    if (STARTS_WITH(m_pszURL, "ftp"))
10✔
631
    {
632
        if (sWriteFuncData.pBuffer != nullptr &&
×
633
            STARTS_WITH_CI(sWriteFuncData.pBuffer, "Content-Length: "))
×
634
        {
635
            const char *pszBuffer =
×
636
                sWriteFuncData.pBuffer + strlen("Content-Length: ");
×
637
            eExists = EXIST_YES;
×
638
            fileSize = CPLScanUIntBig(
×
639
                pszBuffer, static_cast<int>(sWriteFuncData.nSize -
×
640
                                            strlen("Content-Length: ")));
641
            if (ENABLE_DEBUG)
642
                CPLDebug("VSICURL", "GetFileSize(%s)=" CPL_FRMT_GUIB, m_pszURL,
643
                         fileSize);
644
        }
645
    }
646

647
    double dfSize = 0;
10✔
648
    if (eExists != EXIST_YES)
10✔
649
    {
650
        curl_off_t nSizeTmp = 0;
10✔
651
        const CURLcode code = curl_easy_getinfo(
10✔
652
            hLocalHandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &nSizeTmp);
653
        CPL_IGNORE_RET_VAL(dfSize);
10✔
654
        dfSize = static_cast<double>(nSizeTmp);
10✔
655
        if (code == 0)
10✔
656
        {
657
            eExists = EXIST_YES;
10✔
658
            if (dfSize < 0)
10✔
659
                fileSize = 0;
×
660
            else
661
                fileSize = static_cast<GUIntBig>(dfSize);
10✔
662
        }
663
        else
664
        {
665
            eExists = EXIST_NO;
×
666
            fileSize = 0;
×
667
            CPLError(CE_Failure, CPLE_AppDefined,
×
668
                     "VSICurlStreamingHandle::GetFileSize failed");
669
        }
670

671
        long response_code = 0;
10✔
672
        curl_easy_getinfo(hLocalHandle, CURLINFO_HTTP_CODE, &response_code);
10✔
673
        if (response_code != 200)
10✔
674
        {
675
            eExists = EXIST_NO;
×
676
            fileSize = 0;
×
677
        }
678

679
        // Try to guess if this is a directory. Generally if this is a
680
        // directory, curl will retry with an URL with slash added.
681
        char *pszEffectiveURL = nullptr;
10✔
682
        curl_easy_getinfo(hLocalHandle, CURLINFO_EFFECTIVE_URL,
10✔
683
                          &pszEffectiveURL);
684
        if (pszEffectiveURL != nullptr &&
10✔
685
            strncmp(m_pszURL, pszEffectiveURL, strlen(m_pszURL)) == 0 &&
10✔
686
            pszEffectiveURL[strlen(m_pszURL)] == '/')
10✔
687
        {
688
            eExists = EXIST_YES;
×
689
            fileSize = 0;
×
690
            bIsDirectory = true;
×
691
        }
692

693
        if (ENABLE_DEBUG)
694
            CPLDebug("VSICURL",
695
                     "GetFileSize(%s)=" CPL_FRMT_GUIB " response_code=%d",
696
                     m_pszURL, fileSize, static_cast<int>(response_code));
697
    }
698

699
    CPLFree(sWriteFuncData.pBuffer);
10✔
700
    CPLFree(sWriteFuncHeaderData.pBuffer);
10✔
701

702
    FileProp cachedFileProp;
10✔
703
    m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
10✔
704
    cachedFileProp.bHasComputedFileSize = true;
10✔
705
    cachedFileProp.fileSize = fileSize;
10✔
706
    cachedFileProp.eExists = eExists;
10✔
707
    cachedFileProp.bIsDirectory = bIsDirectory;
10✔
708
    if (cachedFileProp.nMode == 0)
10✔
709
        cachedFileProp.nMode = bIsDirectory ? S_IFDIR : S_IFREG;
10✔
710
    m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
10✔
711

712
    const vsi_l_offset nRet = fileSize;
10✔
713
    ReleaseMutex();
10✔
714

715
    curl_easy_cleanup(hLocalHandle);
10✔
716

717
    return nRet;
10✔
718
}
719

720
/************************************************************************/
721
/*                                 Exists()                             */
722
/************************************************************************/
723

724
bool VSICurlStreamingHandle::Exists(const char *pszFilename,
62✔
725
                                    CSLConstList papszOptions)
726
{
727
    if (eExists == EXIST_UNKNOWN)
62✔
728
    {
729
        if (!papszOptions ||
49✔
730
            !CPLTestBool(CSLFetchNameValueDef(
×
731
                papszOptions, "IGNORE_FILENAME_RESTRICTIONS", "NO")))
732
        {
733
            if (!VSICurlFilesystemHandlerBase::IsAllowedFilename(pszFilename))
49✔
734
            {
735
                eExists = EXIST_NO;
×
736
                fileSize = 0;
×
737

738
                FileProp cachedFileProp;
×
739
                m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
×
740
                cachedFileProp.bHasComputedFileSize = true;
×
741
                cachedFileProp.fileSize = fileSize;
×
742
                cachedFileProp.eExists = eExists;
×
743
                cachedFileProp.bIsDirectory = false;
×
744
                cachedFileProp.nMode = S_IFREG;
×
745
                m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
×
746

747
                return false;
×
748
            }
749
        }
750

751
        char chFirstByte = '\0';
49✔
752
        int bExists = (Read(&chFirstByte, 1, 1) == 1);
49✔
753

754
        FileProp cachedFileProp;
98✔
755
        m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
49✔
756
        cachedFileProp.eExists = eExists = bExists ? EXIST_YES : EXIST_NO;
49✔
757
        m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
49✔
758

759
        Seek(0, SEEK_SET);
49✔
760
    }
761

762
    return eExists == EXIST_YES;
62✔
763
}
764

765
/************************************************************************/
766
/*                                  Tell()                              */
767
/************************************************************************/
768

769
vsi_l_offset VSICurlStreamingHandle::Tell()
3✔
770
{
771
    return curOffset;
3✔
772
}
773

774
/************************************************************************/
775
/*                         ReceivedBytes()                              */
776
/************************************************************************/
777

778
size_t VSICurlStreamingHandle::ReceivedBytes(GByte *buffer, size_t count,
196✔
779
                                             size_t nmemb)
780
{
781
    size_t nSize = count * nmemb;
196✔
782
    nBodySize += nSize;
196✔
783

784
    if (ENABLE_DEBUG)
785
        CPLDebug("VSICURL", "Receiving %d bytes...", static_cast<int>(nSize));
786

787
    if (bHasCandidateFileSize && bCanTrustCandidateFileSize &&
196✔
788
        !bHasComputedFileSize)
182✔
789
    {
790
        FileProp cachedFileProp;
364✔
791
        m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
182✔
792
        cachedFileProp.fileSize = fileSize = nCandidateFileSize;
182✔
793
        bHasCandidateFileSize = TRUE;
182✔
794
        cachedFileProp.bHasComputedFileSize = bHasComputedFileSize;
182✔
795
        m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
182✔
796
        if (ENABLE_DEBUG)
797
            CPLDebug("VSICURL", "File size = " CPL_FRMT_GUIB, fileSize);
798
    }
799

800
    AcquireMutex();
196✔
801
    if (eExists == EXIST_UNKNOWN)
196✔
802
    {
803
        FileProp cachedFileProp;
×
804
        m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
×
805
        cachedFileProp.eExists = eExists = EXIST_YES;
×
806
        m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
×
807
    }
808
    else if (eExists == EXIST_NO && StopReceivingBytesOnError())
196✔
809
    {
810
        ReleaseMutex();
×
811
        return 0;
×
812
    }
813

814
    while (true)
815
    {
816
        const size_t nFree = oRingBuffer.GetCapacity() - oRingBuffer.GetSize();
197✔
817
        if (nSize <= nFree)
197✔
818
        {
819
            oRingBuffer.Write(buffer, nSize);
196✔
820

821
            // Signal to the consumer that we have added bytes to the buffer.
822
            CPLCondSignal(hCondProducer);
196✔
823

824
            if (bAskDownloadEnd)
196✔
825
            {
826
                if (ENABLE_DEBUG)
827
                    CPLDebug("VSICURL", "Download interruption asked");
828

829
                ReleaseMutex();
1✔
830
                return 0;
1✔
831
            }
832
            break;
195✔
833
        }
834
        else
835
        {
836
            oRingBuffer.Write(buffer, nFree);
1✔
837
            buffer += nFree;
1✔
838
            nSize -= nFree;
1✔
839

840
            // Signal to the consumer that we have added bytes to the buffer.
841
            CPLCondSignal(hCondProducer);
1✔
842

843
            if (ENABLE_DEBUG)
844
                CPLDebug("VSICURL",
845
                         "Waiting for reader to consume some bytes...");
846

847
            while (oRingBuffer.GetSize() == oRingBuffer.GetCapacity() &&
3✔
848
                   !bAskDownloadEnd)
1✔
849
            {
850
                CPLCondWait(hCondConsumer, hRingBufferMutex);
1✔
851
            }
852

853
            if (bAskDownloadEnd)
1✔
854
            {
855
                if (ENABLE_DEBUG)
856
                    CPLDebug("VSICURL", "Download interruption asked");
857

858
                ReleaseMutex();
×
859
                return 0;
×
860
            }
861
        }
862
    }
1✔
863

864
    ReleaseMutex();
195✔
865

866
    return nmemb;
195✔
867
}
868

869
/************************************************************************/
870
/*                 VSICurlStreamingHandleReceivedBytes()                */
871
/************************************************************************/
872

873
static size_t VSICurlStreamingHandleReceivedBytes(void *buffer, size_t count,
196✔
874
                                                  size_t nmemb, void *req)
875
{
876
    return static_cast<VSICurlStreamingHandle *>(req)->ReceivedBytes(
196✔
877
        static_cast<GByte *>(buffer), count, nmemb);
196✔
878
}
879

880
/************************************************************************/
881
/*              VSICurlStreamingHandleReceivedBytesHeader()             */
882
/************************************************************************/
883

884
#define HEADER_SIZE 32768
885

886
size_t VSICurlStreamingHandle::ReceivedBytesHeader(GByte *buffer, size_t count,
409✔
887
                                                   size_t nmemb)
888
{
889
    const size_t nSize = count * nmemb;
409✔
890
    if (ENABLE_DEBUG)
891
        CPLDebug("VSICURL", "Receiving %d bytes for header...",
892
                 static_cast<int>(nSize));
893

894
    // Reset buffer if we have followed link after a redirect.
895
    if (nSize >= 9 && InterpretRedirect() &&
351✔
896
        (nHTTPCode == 301 || nHTTPCode == 302 || nHTTPCode == 303) &&
760✔
897
        STARTS_WITH_CI(reinterpret_cast<char *>(buffer), "HTTP/"))
×
898
    {
899
        nHeaderSize = 0;
×
900
        nHTTPCode = 0;
×
901
    }
902

903
    if (nHeaderSize < HEADER_SIZE)
409✔
904
    {
905
        const size_t nSz = std::min(nSize, HEADER_SIZE - nHeaderSize);
409✔
906
        memcpy(pabyHeaderData + nHeaderSize, buffer, nSz);
409✔
907
        pabyHeaderData[nHeaderSize + nSz] = '\0';
409✔
908
        nHeaderSize += nSz;
409✔
909

910
#if DEBUG_VERBOSE
911
        CPLDebug("VSICURL", "Header : %s", pabyHeaderData);
912
#endif
913

914
        AcquireMutex();
409✔
915

916
        if (nHTTPCode == 0 &&
409✔
917
            strchr(reinterpret_cast<char *>(pabyHeaderData), '\n') != nullptr &&
58✔
918
            STARTS_WITH_CI(reinterpret_cast<char *>(pabyHeaderData), "HTTP/"))
58✔
919
        {
920
            const char *pszSpace =
921
                strchr(const_cast<const char *>(
58✔
922
                           reinterpret_cast<char *>(pabyHeaderData)),
58✔
923
                       ' ');
924
            if (pszSpace)
58✔
925
                nHTTPCode = atoi(pszSpace + 1);
58✔
926
            if (ENABLE_DEBUG)
927
                CPLDebug("VSICURL", "HTTP code = %d", nHTTPCode);
928

929
            // If moved permanently/temporarily, go on.
930
            if (eExists == EXIST_UNKNOWN &&
109✔
931
                !(InterpretRedirect() &&
51✔
932
                  (nHTTPCode == 301 || nHTTPCode == 302 || nHTTPCode == 303)))
8✔
933
            {
934
                eExists = nHTTPCode == 200 ? EXIST_YES : EXIST_NO;
51✔
935
                FileProp cachedFileProp;
102✔
936
                m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
51✔
937
                cachedFileProp.eExists = eExists;
51✔
938
                m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
51✔
939
            }
940
        }
941

942
        if (!(InterpretRedirect() &&
409✔
943
              (nHTTPCode == 301 || nHTTPCode == 302 || nHTTPCode == 303)) &&
818✔
944
            !bHasComputedFileSize)
409✔
945
        {
946
            // Caution: When gzip compression is enabled, the content-length is
947
            // the compressed size, which we are not interested in, so we must
948
            // not take it into account.
949

950
            const char *pszContentLength = strstr(
380✔
951
                reinterpret_cast<char *>(pabyHeaderData), "Content-Length: ");
380✔
952
            const char *pszEndOfLine =
380✔
953
                pszContentLength ? strchr(pszContentLength, '\n') : nullptr;
380✔
954
            if (bCanTrustCandidateFileSize && pszEndOfLine != nullptr)
380✔
955
            {
956
                const char *pszVal =
101✔
957
                    pszContentLength + strlen("Content-Length: ");
958
                bHasCandidateFileSize = true;
101✔
959
                nCandidateFileSize = CPLScanUIntBig(
101✔
960
                    pszVal, static_cast<int>(pszEndOfLine - pszVal));
101✔
961
                if (ENABLE_DEBUG)
962
                    CPLDebug("VSICURL",
963
                             "Has found candidate file size = " CPL_FRMT_GUIB,
964
                             nCandidateFileSize);
965
            }
966

967
            const char *pszContentEncoding = strstr(
380✔
968
                reinterpret_cast<char *>(pabyHeaderData), "Content-Encoding: ");
380✔
969
            pszEndOfLine =
380✔
970
                pszContentEncoding ? strchr(pszContentEncoding, '\n') : nullptr;
380✔
971
            if (bHasCandidateFileSize && pszEndOfLine != nullptr)
380✔
972
            {
973
                const char *pszVal =
×
974
                    pszContentEncoding + strlen("Content-Encoding: ");
975
                if (STARTS_WITH(pszVal, "gzip"))
×
976
                {
977
                    if (ENABLE_DEBUG)
978
                        CPLDebug("VSICURL", "GZip compression enabled --> "
979
                                            "cannot trust candidate file size");
980
                    bCanTrustCandidateFileSize = false;
×
981
                }
982
            }
983
        }
984

985
        ReleaseMutex();
409✔
986
    }
987

988
    return nmemb;
409✔
989
}
990

991
/************************************************************************/
992
/*                 VSICurlStreamingHandleReceivedBytesHeader()          */
993
/************************************************************************/
994

995
static size_t VSICurlStreamingHandleReceivedBytesHeader(void *buffer,
409✔
996
                                                        size_t count,
997
                                                        size_t nmemb, void *req)
998
{
999
    return static_cast<VSICurlStreamingHandle *>(req)->ReceivedBytesHeader(
409✔
1000
        static_cast<GByte *>(buffer), count, nmemb);
409✔
1001
}
1002

1003
/************************************************************************/
1004
/*                       DownloadInThread()                             */
1005
/************************************************************************/
1006

1007
void VSICurlStreamingHandle::DownloadInThread()
60✔
1008
{
1009
    CURL *hCurlHandle = curl_easy_init();
60✔
1010

1011
    struct curl_slist *headers =
1012
        VSICurlSetOptions(hCurlHandle, m_pszURL, m_aosHTTPOptions.List());
60✔
1013
    headers = VSICurlMergeHeaders(headers, GetCurlHeaders("GET", headers));
60✔
1014
    unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
60✔
1015

1016
    static bool bHasCheckVersion = false;
1017
    static bool bSupportGZip = false;
1018
    if (!bHasCheckVersion)
60✔
1019
    {
1020
        bSupportGZip = strstr(curl_version(), "zlib/") != nullptr;
3✔
1021
        bHasCheckVersion = true;
3✔
1022
    }
1023
    if (bSupportGZip && CPLTestBool(CPLGetConfigOption("CPL_CURL_GZIP", "YES")))
60✔
1024
    {
1025
        unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_ENCODING, "gzip");
60✔
1026
    }
1027

1028
    if (pabyHeaderData == nullptr)
60✔
1029
        pabyHeaderData = static_cast<GByte *>(CPLMalloc(HEADER_SIZE + 1));
52✔
1030
    nHeaderSize = 0;
60✔
1031
    nBodySize = 0;
60✔
1032
    nHTTPCode = 0;
60✔
1033

1034
    unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA, this);
60✔
1035
    unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION,
60✔
1036
                               VSICurlStreamingHandleReceivedBytesHeader);
1037

1038
    unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, this);
60✔
1039
    unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
60✔
1040
                               VSICurlStreamingHandleReceivedBytes);
1041

1042
    m_szCurlErrBuf[0] = '\0';
60✔
1043
    unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER,
60✔
1044
                               m_szCurlErrBuf);
1045

1046
    void *old_handler = CPLHTTPIgnoreSigPipe();
60✔
1047
    CURLcode eRet = curl_easy_perform(hCurlHandle);
60✔
1048
    CPLHTTPRestoreSigPipeHandler(old_handler);
60✔
1049
    if (headers != nullptr)
60✔
1050
        curl_slist_free_all(headers);
47✔
1051

1052
    unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, nullptr);
60✔
1053
    unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION, nullptr);
60✔
1054
    unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA, nullptr);
60✔
1055
    unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION, nullptr);
60✔
1056

1057
    AcquireMutex();
60✔
1058
    m_bErrorOccurredInThread = eRet != CURLE_OK;
60✔
1059
    if (m_bErrorOccurredInThread)
60✔
1060
    {
1061
        // For autotest purposes only !
1062
        const char *pszSimulatedCurlError = CPLGetConfigOption(
8✔
1063
            "CPL_VSIL_CURL_STREMAING_SIMULATED_CURL_ERROR", nullptr);
1064
        if (pszSimulatedCurlError)
8✔
1065
            snprintf(m_szCurlErrBuf, sizeof(m_szCurlErrBuf), "%s",
5✔
1066
                     pszSimulatedCurlError);
1067
    }
1068

1069
    if (!bAskDownloadEnd && eRet == CURLE_OK && !bHasComputedFileSize)
60✔
1070
    {
1071
        FileProp cachedFileProp;
94✔
1072
        m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
47✔
1073
        fileSize = nBodySize;
47✔
1074
        cachedFileProp.fileSize = fileSize;
47✔
1075
        bHasComputedFileSize = true;
47✔
1076
        cachedFileProp.bHasComputedFileSize = bHasComputedFileSize;
47✔
1077
        m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
47✔
1078
        if (ENABLE_DEBUG)
1079
            CPLDebug("VSICURL", "File size = " CPL_FRMT_GUIB, fileSize);
1080
    }
1081

1082
    bDownloadInProgress = FALSE;
60✔
1083
    bDownloadStopped = TRUE;
60✔
1084

1085
    // Signal to the consumer that the download has ended.
1086
    CPLCondSignal(hCondProducer);
60✔
1087
    ReleaseMutex();
60✔
1088

1089
    curl_easy_cleanup(hCurlHandle);
60✔
1090
}
60✔
1091

1092
static void VSICurlDownloadInThread(void *pArg)
60✔
1093
{
1094
    static_cast<VSICurlStreamingHandle *>(pArg)->DownloadInThread();
60✔
1095
}
60✔
1096

1097
/************************************************************************/
1098
/*                            StartDownload()                            */
1099
/************************************************************************/
1100

1101
void VSICurlStreamingHandle::StartDownload()
106✔
1102
{
1103
    if (bDownloadInProgress || bDownloadStopped)
106✔
1104
        return;
46✔
1105

1106
    CPLDebug("VSICURL", "Start download for %s", m_pszURL);
60✔
1107

1108
    oRingBuffer.Reset();
60✔
1109
    bDownloadInProgress = TRUE;
60✔
1110
    nRingBufferFileOffset = 0;
60✔
1111
    m_bErrorOccurredInThread = false;
60✔
1112
    hThread = CPLCreateJoinableThread(VSICurlDownloadInThread, this);
60✔
1113
}
1114

1115
/************************************************************************/
1116
/*                            StopDownload()                            */
1117
/************************************************************************/
1118

1119
void VSICurlStreamingHandle::StopDownload()
93✔
1120
{
1121
    if (hThread)
93✔
1122
    {
1123
        CPLDebug("VSICURL", "Stop download for %s", m_pszURL);
60✔
1124

1125
        AcquireMutex();
60✔
1126
        // Signal to the producer that we ask for download interruption.
1127
        bAskDownloadEnd = TRUE;
60✔
1128
        CPLCondSignal(hCondConsumer);
60✔
1129

1130
        // Wait for the producer to have finished.
1131
        while (bDownloadInProgress)
64✔
1132
            CPLCondWait(hCondProducer, hRingBufferMutex);
4✔
1133

1134
        bAskDownloadEnd = FALSE;
60✔
1135

1136
        ReleaseMutex();
60✔
1137

1138
        CPLJoinThread(hThread);
60✔
1139
        hThread = nullptr;
60✔
1140
    }
1141

1142
    oRingBuffer.Reset();
93✔
1143
    bDownloadStopped = FALSE;
93✔
1144
    m_bErrorOccurredInThread = false;
93✔
1145
    nRingBufferFileOffset = 0;
93✔
1146
    bEOF = false;
93✔
1147
}
93✔
1148

1149
/************************************************************************/
1150
/*                        PutRingBufferInCache()                        */
1151
/************************************************************************/
1152

1153
void VSICurlStreamingHandle::PutRingBufferInCache()
42✔
1154
{
1155
    if (nRingBufferFileOffset >= BKGND_BUFFER_SIZE)
42✔
1156
        return;
×
1157

1158
    AcquireMutex();
42✔
1159

1160
    // Cache any remaining bytes available in the ring buffer.
1161
    size_t nBufSize = oRingBuffer.GetSize();
42✔
1162
    if (nBufSize > 0)
42✔
1163
    {
1164
        if (nRingBufferFileOffset + nBufSize > BKGND_BUFFER_SIZE)
28✔
1165
            nBufSize =
1✔
1166
                static_cast<size_t>(BKGND_BUFFER_SIZE - nRingBufferFileOffset);
1✔
1167
        GByte *pabyTmp = static_cast<GByte *>(CPLMalloc(nBufSize));
28✔
1168
        oRingBuffer.Read(pabyTmp, nBufSize);
28✔
1169

1170
        // Signal to the producer that we have ingested some bytes.
1171
        CPLCondSignal(hCondConsumer);
28✔
1172

1173
        AddRegion(nRingBufferFileOffset, nBufSize, pabyTmp);
28✔
1174
        nRingBufferFileOffset += nBufSize;
28✔
1175
        CPLFree(pabyTmp);
28✔
1176
    }
1177

1178
    ReleaseMutex();
42✔
1179
}
1180

1181
/************************************************************************/
1182
/*                                Read()                                */
1183
/************************************************************************/
1184

1185
size_t VSICurlStreamingHandle::Read(void *const pBuffer, size_t const nSize,
120✔
1186
                                    size_t const nMemb)
1187
{
1188
    const size_t nBufferRequestSize = nSize * nMemb;
120✔
1189
    const vsi_l_offset curOffsetOri = curOffset;
120✔
1190
    const vsi_l_offset nRingBufferFileOffsetOri = nRingBufferFileOffset;
120✔
1191
    if (nBufferRequestSize == 0)
120✔
1192
        return 0;
×
1193

1194
    CPLHTTPRetryContext oRetryContext(m_oRetryParameters);
240✔
1195

1196
retry:
128✔
1197
    GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
128✔
1198
    size_t nRemaining = nBufferRequestSize;
128✔
1199

1200
    AcquireMutex();
128✔
1201
    // fileSize might be set wrongly to 0, such as
1202
    // /vsicurl_streaming/https://query.data.world/s/jgsghstpphjhicstradhy5kpjwrnfy
1203
    const bool bHasComputedFileSizeLocal = bHasComputedFileSize && fileSize > 0;
128✔
1204
    const vsi_l_offset fileSizeLocal = fileSize;
128✔
1205
    ReleaseMutex();
128✔
1206

1207
    if (bHasComputedFileSizeLocal && curOffset >= fileSizeLocal)
128✔
1208
    {
1209
        CPLDebug("VSICURL", "Read attempt beyond end of file");
×
1210
        bEOF = true;
×
1211
    }
1212
    if (bEOF)
128✔
1213
        return 0;
×
1214

1215
    if (curOffset < nRingBufferFileOffset)
128✔
1216
        PutRingBufferInCache();
42✔
1217

1218
    if (ENABLE_DEBUG)
1219
        CPLDebug("VSICURL", "Read [" CPL_FRMT_GUIB ", " CPL_FRMT_GUIB "[ in %s",
1220
                 curOffset, curOffset + nBufferRequestSize, m_pszURL);
1221

1222
    // Can we use the cache?
1223
    if (pCachedData != nullptr && curOffset < nCachedSize)
128✔
1224
    {
1225
        const size_t nSz =
1226
            std::min(nRemaining, static_cast<size_t>(nCachedSize - curOffset));
46✔
1227
        if (ENABLE_DEBUG)
1228
            CPLDebug("VSICURL", "Using cache for [%d, %d[ in %s",
1229
                     static_cast<int>(curOffset),
1230
                     static_cast<int>(curOffset + nSz), m_pszURL);
1231
        memcpy(pabyBuffer, pCachedData + curOffset, nSz);
46✔
1232
        pabyBuffer += nSz;
46✔
1233
        curOffset += nSz;
46✔
1234
        nRemaining -= nSz;
46✔
1235
    }
1236

1237
    // Is the request partially covered by the cache and going beyond file size?
1238
    if (pCachedData != nullptr && bHasComputedFileSizeLocal &&
128✔
1239
        curOffset <= nCachedSize && curOffset + nRemaining > fileSizeLocal &&
21✔
1240
        fileSize == nCachedSize)
15✔
1241
    {
1242
        size_t nSz = static_cast<size_t>(nCachedSize - curOffset);
13✔
1243
        if (ENABLE_DEBUG && nSz != 0)
1244
            CPLDebug("VSICURL", "Using cache for [%d, %d[ in %s",
1245
                     static_cast<int>(curOffset),
1246
                     static_cast<int>(curOffset + nSz), m_pszURL);
1247
        memcpy(pabyBuffer, pCachedData + curOffset, nSz);
13✔
1248
        pabyBuffer += nSz;
13✔
1249
        curOffset += nSz;
13✔
1250
        nRemaining -= nSz;
13✔
1251
        bEOF = true;
13✔
1252
    }
1253

1254
    bool bErrorOccurred = false;
128✔
1255

1256
    // Has a Seek() being done since the last Read()?
1257
    if (!bEOF && nRemaining > 0 && curOffset != nRingBufferFileOffset)
128✔
1258
    {
1259
        // Backward seek: Need to restart the download from the beginning.
1260
        if (curOffset < nRingBufferFileOffset)
4✔
1261
            StopDownload();
×
1262

1263
        StartDownload();
4✔
1264

1265
        const vsi_l_offset SKIP_BUFFER_SIZE = 32768;
4✔
1266
        GByte *pabyTmp = static_cast<GByte *>(CPLMalloc(SKIP_BUFFER_SIZE));
4✔
1267

1268
        CPLAssert(curOffset >= nRingBufferFileOffset);
4✔
1269
        vsi_l_offset nBytesToSkip = curOffset - nRingBufferFileOffset;
4✔
1270
        while (nBytesToSkip > 0)
12✔
1271
        {
1272
            vsi_l_offset nBytesToRead = nBytesToSkip;
10✔
1273

1274
            AcquireMutex();
10✔
1275
            if (nBytesToRead > oRingBuffer.GetSize())
10✔
1276
                nBytesToRead = oRingBuffer.GetSize();
8✔
1277
            if (nBytesToRead > SKIP_BUFFER_SIZE)
10✔
1278
                nBytesToRead = SKIP_BUFFER_SIZE;
×
1279
            oRingBuffer.Read(pabyTmp, static_cast<size_t>(nBytesToRead));
10✔
1280

1281
            // Signal to the producer that we have ingested some bytes.
1282
            CPLCondSignal(hCondConsumer);
10✔
1283
            ReleaseMutex();
10✔
1284

1285
            if (nBytesToRead)
10✔
1286
                AddRegion(nRingBufferFileOffset,
4✔
1287
                          static_cast<size_t>(nBytesToRead), pabyTmp);
1288

1289
            nBytesToSkip -= nBytesToRead;
10✔
1290
            nRingBufferFileOffset += nBytesToRead;
10✔
1291

1292
            if (nBytesToRead == 0 && nBytesToSkip != 0)
10✔
1293
            {
1294
                if (ENABLE_DEBUG)
1295
                    CPLDebug("VSICURL",
1296
                             "Waiting for writer to produce some bytes...");
1297

1298
                AcquireMutex();
6✔
1299
                while (oRingBuffer.GetSize() == 0 && bDownloadInProgress)
12✔
1300
                    CPLCondWait(hCondProducer, hRingBufferMutex);
6✔
1301
                const int bBufferEmpty = (oRingBuffer.GetSize() == 0);
6✔
1302
                bErrorOccurred = m_bErrorOccurredInThread;
6✔
1303
                ReleaseMutex();
6✔
1304

1305
                if (bBufferEmpty && !bDownloadInProgress)
6✔
1306
                    break;
2✔
1307
            }
1308
        }
1309

1310
        CPLFree(pabyTmp);
4✔
1311

1312
        if (nBytesToSkip != 0 && !bErrorOccurred)
4✔
1313
        {
1314
            bEOF = true;
×
1315
            return 0;
×
1316
        }
1317
    }
1318

1319
    if (!bEOF && nRemaining > 0 && !bErrorOccurred)
128✔
1320
    {
1321
        StartDownload();
102✔
1322
        CPLAssert(curOffset == nRingBufferFileOffset);
102✔
1323
    }
1324

1325
    // Fill the destination buffer from the ring buffer.
1326
    while (!bEOF && nRemaining > 0 && !bErrorOccurred)
307✔
1327
    {
1328
        AcquireMutex();
225✔
1329
        size_t nToRead = oRingBuffer.GetSize();
225✔
1330
        if (nToRead > nRemaining)
224✔
1331
            nToRead = nRemaining;
52✔
1332
        oRingBuffer.Read(pabyBuffer, nToRead);
224✔
1333

1334
        // Signal to the producer that we have ingested some bytes.
1335
        CPLCondSignal(hCondConsumer);
224✔
1336
        ReleaseMutex();
224✔
1337

1338
        if (nToRead)
224✔
1339
            AddRegion(curOffset, nToRead, pabyBuffer);
104✔
1340

1341
        nRemaining -= nToRead;
224✔
1342
        pabyBuffer += nToRead;
224✔
1343
        curOffset += nToRead;
224✔
1344
        nRingBufferFileOffset += nToRead;
224✔
1345

1346
        if (nToRead == 0 && nRemaining != 0)
224✔
1347
        {
1348
            if (ENABLE_DEBUG)
1349
                CPLDebug("VSICURL",
1350
                         "Waiting for writer to produce some bytes...");
1351

1352
            AcquireMutex();
120✔
1353
            while (oRingBuffer.GetSize() == 0 && bDownloadInProgress)
239✔
1354
                CPLCondWait(hCondProducer, hRingBufferMutex);
118✔
1355
            const bool bBufferEmpty = oRingBuffer.GetSize() == 0;
121✔
1356
            bErrorOccurred = m_bErrorOccurredInThread;
121✔
1357
            ReleaseMutex();
121✔
1358

1359
            if (bBufferEmpty && !bDownloadInProgress)
121✔
1360
                break;
46✔
1361
        }
1362
    }
1363

1364
    if (ENABLE_DEBUG)
1365
        CPLDebug("VSICURL", "Read(%d) = %d",
1366
                 static_cast<int>(nBufferRequestSize),
1367
                 static_cast<int>(nBufferRequestSize - nRemaining));
1368
    size_t nRet = (nBufferRequestSize - nRemaining) / nSize;
128✔
1369
    if (nRet < nMemb)
128✔
1370
        bEOF = true;
61✔
1371

1372
    // Give a chance to specialized filesystem to deal with errors to redirect
1373
    // elsewhere.
1374
    if (curOffsetOri == 0 && nRingBufferFileOffsetOri == 0 &&
92✔
1375
        !StopReceivingBytesOnError() && eExists == EXIST_NO &&
244✔
1376
        nRemaining < nBufferRequestSize)
24✔
1377
    {
1378
        const size_t nErrorBufferMaxSize = 4096;
24✔
1379
        GByte *pabyErrorBuffer =
1380
            static_cast<GByte *>(CPLMalloc(nErrorBufferMaxSize + 1));
24✔
1381
        size_t nRead = nBufferRequestSize - nRemaining;
24✔
1382
        size_t nErrorBufferSize = std::min(nErrorBufferMaxSize, nRead);
24✔
1383
        memcpy(pabyErrorBuffer, pBuffer, nErrorBufferSize);
24✔
1384
        if (nRead < nErrorBufferMaxSize)
24✔
1385
            nErrorBufferSize +=
24✔
1386
                Read(pabyErrorBuffer + nRead, 1, nErrorBufferMaxSize - nRead);
24✔
1387
        pabyErrorBuffer[nErrorBufferSize] = 0;
24✔
1388
        StopDownload();
24✔
1389
        if (CanRestartOnError(reinterpret_cast<char *>(pabyErrorBuffer),
24✔
1390
                              reinterpret_cast<char *>(pabyHeaderData), true))
24✔
1391
        {
1392
            curOffset = 0;
4✔
1393

1394
            AcquireMutex();
4✔
1395
            eExists = EXIST_UNKNOWN;
4✔
1396
            bHasComputedFileSize = false;
4✔
1397
            fileSize = 0;
4✔
1398
            ReleaseMutex();
4✔
1399
            nCachedSize = 0;
4✔
1400

1401
            FileProp cachedFileProp;
4✔
1402
            m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
4✔
1403
            cachedFileProp.bHasComputedFileSize = false;
4✔
1404
            cachedFileProp.fileSize = 0;
4✔
1405
            cachedFileProp.eExists = EXIST_UNKNOWN;
4✔
1406
            m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
4✔
1407

1408
            goto retry;
4✔
1409
        }
1410
        else
1411
        {
1412
            CPLDebug("VSICURL", "Error buffer: %s",
20✔
1413
                     reinterpret_cast<char *>(pabyErrorBuffer));
1414
            nRet = 0;
20✔
1415
        }
1416

1417
        CPLFree(pabyErrorBuffer);
20✔
1418
    }
1419

1420
    if (bErrorOccurred)
124✔
1421
    {
1422
        // Look if we should attempt a retry
1423
        AcquireMutex();
7✔
1424
        const bool bRetry = oRetryContext.CanRetry(static_cast<int>(nHTTPCode),
14✔
1425
                                                   nullptr, m_szCurlErrBuf);
7✔
1426
        ReleaseMutex();
7✔
1427
        if (bRetry)
7✔
1428
        {
1429
            StopDownload();
4✔
1430

1431
            CPLError(CE_Warning, CPLE_AppDefined,
4✔
1432
                     "HTTP error code: %d - %s. "
1433
                     "Retrying again in %.1f secs",
1434
                     static_cast<int>(nHTTPCode), m_pszURL,
4✔
1435
                     oRetryContext.GetCurrentDelay());
1436
            CPLSleep(oRetryContext.GetCurrentDelay());
4✔
1437
            curOffset = curOffsetOri;
4✔
1438
            goto retry;
4✔
1439
        }
1440
    }
1441

1442
    if (bErrorOccurred)
120✔
1443
        m_bError = true;
3✔
1444

1445
    return nRet;
120✔
1446
}
1447

1448
/************************************************************************/
1449
/*                          AddRegion()                                 */
1450
/************************************************************************/
1451

1452
void VSICurlStreamingHandle::AddRegion(vsi_l_offset nFileOffsetStart,
136✔
1453
                                       size_t nSize, GByte *pData)
1454
{
1455
    if (nFileOffsetStart >= BKGND_BUFFER_SIZE)
136✔
1456
        return;
21✔
1457

1458
    if (pCachedData == nullptr)
115✔
1459
        pCachedData = static_cast<GByte *>(CPLMalloc(BKGND_BUFFER_SIZE));
50✔
1460

1461
    if (nFileOffsetStart <= nCachedSize &&
115✔
1462
        nFileOffsetStart + nSize > nCachedSize)
115✔
1463
    {
1464
        const size_t nSz = std::min(
1465
            nSize, static_cast<size_t>(BKGND_BUFFER_SIZE - nFileOffsetStart));
111✔
1466
        if (ENABLE_DEBUG)
1467
            CPLDebug("VSICURL", "Writing [%d, %d[ in cache for %s",
1468
                     static_cast<int>(nFileOffsetStart),
1469
                     static_cast<int>(nFileOffsetStart + nSz), m_pszURL);
1470
        memcpy(pCachedData + nFileOffsetStart, pData, nSz);
111✔
1471
        nCachedSize = static_cast<size_t>(nFileOffsetStart + nSz);
111✔
1472
    }
1473
}
1474

1475
/************************************************************************/
1476
/*                               Write()                                */
1477
/************************************************************************/
1478

1479
size_t VSICurlStreamingHandle::Write(const void * /* pBuffer */,
×
1480
                                     size_t /* nSize */, size_t /* nMemb */)
1481
{
1482
    return 0;
×
1483
}
1484

1485
/************************************************************************/
1486
/*                                 Eof()                                */
1487
/************************************************************************/
1488

1489
int VSICurlStreamingHandle::Eof()
4✔
1490
{
1491
    return bEOF;
4✔
1492
}
1493

1494
/************************************************************************/
1495
/*                               Error()                                */
1496
/************************************************************************/
1497

1498
int VSICurlStreamingHandle::Error()
11✔
1499

1500
{
1501
    return m_bError;
11✔
1502
}
1503

1504
/************************************************************************/
1505
/*                             ClearErr()                               */
1506
/************************************************************************/
1507

1508
void VSICurlStreamingHandle::ClearErr()
×
1509
{
1510
    bEOF = false;
×
1511
    m_bError = false;
×
1512
}
×
1513

1514
/************************************************************************/
1515
/*                                 Flush()                              */
1516
/************************************************************************/
1517

1518
int VSICurlStreamingHandle::Flush()
×
1519
{
1520
    return 0;
×
1521
}
1522

1523
/************************************************************************/
1524
/*                                  Close()                             */
1525
/************************************************************************/
1526

1527
int VSICurlStreamingHandle::Close()
30✔
1528
{
1529
    return 0;
30✔
1530
}
1531

1532
/************************************************************************/
1533
/*                      VSICurlStreamingFSHandler()                     */
1534
/************************************************************************/
1535

1536
VSICurlStreamingFSHandler::VSICurlStreamingFSHandler()
9,708✔
1537
    : oCacheFileProp{100 * 1024}
9,708✔
1538
{
1539
    hMutex = CPLCreateMutex();
9,708✔
1540
    CPLReleaseMutex(hMutex);
9,708✔
1541
}
9,708✔
1542

1543
/************************************************************************/
1544
/*                      ~VSICurlStreamingFSHandler()                    */
1545
/************************************************************************/
1546

1547
VSICurlStreamingFSHandler::~VSICurlStreamingFSHandler()
7,875✔
1548
{
1549
    VSICurlStreamingFSHandler::ClearCache();
6,750✔
1550

1551
    CPLDestroyMutex(hMutex);
6,750✔
1552
    hMutex = nullptr;
6,750✔
1553
}
7,875✔
1554

1555
/************************************************************************/
1556
/*                            ClearCache()                              */
1557
/************************************************************************/
1558

1559
void VSICurlStreamingFSHandler::ClearCache()
8,676✔
1560
{
1561
    CPLMutexHolder oHolder(&hMutex);
17,352✔
1562

1563
    {
1564
        const auto lambda = [](const lru11::KeyValuePair<std::string, bool> &kv)
60✔
1565
        { VSICURLInvalidateCachedFileProp(kv.key.c_str()); };
60✔
1566
        oCacheFileProp.cwalk(lambda);
8,676✔
1567
        oCacheFileProp.clear();
8,676✔
1568
    }
1569
}
8,676✔
1570

1571
/************************************************************************/
1572
/*                         AcquireMutex()                               */
1573
/************************************************************************/
1574

1575
void VSICurlStreamingFSHandler::AcquireMutex()
×
1576
{
1577
    CPLAcquireMutex(hMutex, 1000.0);
×
1578
}
×
1579

1580
/************************************************************************/
1581
/*                         ReleaseMutex()                               */
1582
/************************************************************************/
1583

1584
void VSICurlStreamingFSHandler::ReleaseMutex()
×
1585
{
1586
    CPLReleaseMutex(hMutex);
×
1587
}
×
1588

1589
/************************************************************************/
1590
/*                          CreateFileHandle()                          */
1591
/************************************************************************/
1592

1593
VSICurlStreamingHandle *
1594
VSICurlStreamingFSHandler::CreateFileHandle(const char *pszFilename,
10✔
1595
                                            const char *pszURL)
1596
{
1597
    return new VSICurlStreamingHandle(this, pszFilename, pszURL);
10✔
1598
}
1599

1600
/************************************************************************/
1601
/*                                Open()                                */
1602
/************************************************************************/
1603

1604
VSIVirtualHandle *VSICurlStreamingFSHandler::Open(const char *pszFilename,
59✔
1605
                                                  const char *pszAccess,
1606
                                                  bool /* bSetError */,
1607
                                                  CSLConstList papszOptions)
1608
{
1609
    if (!STARTS_WITH_CI(pszFilename, GetFSPrefix()))
59✔
1610
        return nullptr;
2✔
1611

1612
    if (strchr(pszAccess, 'w') != nullptr || strchr(pszAccess, '+') != nullptr)
57✔
1613
    {
1614
        CPLError(CE_Failure, CPLE_AppDefined,
×
1615
                 "Only read-only mode is supported for %s",
1616
                 GetFSPrefix().c_str());
×
1617
        return nullptr;
×
1618
    }
1619

1620
    VSICurlStreamingHandle *poHandle =
1621
        CreateFileHandle(pszFilename, pszFilename + GetFSPrefix().size());
57✔
1622
    // If we didn't get a filelist, check that the file really exists.
1623
    if (poHandle == nullptr || !poHandle->Exists(pszFilename, papszOptions))
57✔
1624
    {
1625
        delete poHandle;
27✔
1626
        return nullptr;
27✔
1627
    }
1628

1629
    if (CPLTestBool(CPLGetConfigOption("VSI_CACHE", "FALSE")))
30✔
1630
        return VSICreateCachedFile(poHandle);
×
1631

1632
    return poHandle;
30✔
1633
}
1634

1635
/************************************************************************/
1636
/*                         GetCachedFileProp()                          */
1637
/************************************************************************/
1638

1639
bool VSICurlStreamingFSHandler::GetCachedFileProp(const char *pszURL,
408✔
1640
                                                  FileProp &oFileProp)
1641
{
1642
    CPLMutexHolder oHolder(&hMutex);
816✔
1643
    bool inCache;
1644
    if (oCacheFileProp.tryGet(std::string(pszURL), inCache))
408✔
1645
    {
1646
        if (VSICURLGetCachedFileProp(pszURL, oFileProp))
346✔
1647
        {
1648
            return true;
346✔
1649
        }
1650
        oCacheFileProp.remove(std::string(pszURL));
×
1651
    }
1652
    return false;
62✔
1653
}
1654

1655
/************************************************************************/
1656
/*                         SetCachedFileProp()                          */
1657
/************************************************************************/
1658

1659
void VSICurlStreamingFSHandler::SetCachedFileProp(const char *pszURL,
408✔
1660
                                                  FileProp &oFileProp)
1661
{
1662
    CPLMutexHolder oHolder(&hMutex);
816✔
1663
    oCacheFileProp.insert(std::string(pszURL), true);
408✔
1664
    VSICURLSetCachedFileProp(pszURL, oFileProp);
408✔
1665
}
408✔
1666

1667
/************************************************************************/
1668
/*                                Stat()                                */
1669
/************************************************************************/
1670

1671
int VSICurlStreamingFSHandler::Stat(const char *pszFilename,
12✔
1672
                                    VSIStatBufL *pStatBuf, int nFlags)
1673
{
1674
    if (!STARTS_WITH_CI(pszFilename, GetFSPrefix()))
12✔
1675
        return -1;
2✔
1676

1677
    if ((nFlags & VSI_STAT_CACHE_ONLY) != 0)
10✔
1678
    {
1679
        const std::string osVSICURLFilename =
1680
            std::string("/vsicurl/") + (pszFilename + GetFSPrefix().size());
×
1681
        return VSIStatExL(osVSICURLFilename.c_str(), pStatBuf, nFlags);
×
1682
    }
1683

1684
    memset(pStatBuf, 0, sizeof(VSIStatBufL));
10✔
1685

1686
    VSICurlStreamingHandle *poHandle =
1687
        CreateFileHandle(pszFilename, pszFilename + GetFSPrefix().size());
10✔
1688
    if (poHandle == nullptr)
10✔
1689
    {
1690
        return -1;
×
1691
    }
1692
    if (poHandle->IsKnownFileSize() ||
19✔
1693
        ((nFlags & VSI_STAT_SIZE_FLAG) && !poHandle->IsDirectory() &&
9✔
1694
         CPLTestBool(CPLGetConfigOption("CPL_VSIL_CURL_SLOW_GET_SIZE", "YES"))))
9✔
1695
    {
1696
        pStatBuf->st_size = poHandle->GetFileSize();
10✔
1697
    }
1698

1699
    int nRet = (poHandle->Exists(pszFilename, nullptr)) ? 0 : -1;
10✔
1700
    pStatBuf->st_mode = poHandle->IsDirectory() ? S_IFDIR : S_IFREG;
10✔
1701

1702
    delete poHandle;
10✔
1703
    return nRet;
10✔
1704
}
1705

1706
/************************************************************************/
1707
/*                          GetActualURL()                              */
1708
/************************************************************************/
1709

1710
const char *VSICurlStreamingFSHandler::GetActualURL(const char *pszFilename)
3✔
1711
{
1712
    if (!STARTS_WITH_CI(pszFilename, GetFSPrefix()))
3✔
1713
        return pszFilename;
×
1714
    auto poHandle = std::unique_ptr<VSICurlStreamingHandle>(
1715
        CreateFileHandle(pszFilename, pszFilename + GetFSPrefix().size()));
6✔
1716
    if (poHandle == nullptr)
3✔
1717
        return pszFilename;
×
1718
    return CPLSPrintf("%s", poHandle->GetURL());
3✔
1719
}
1720

1721
/************************************************************************/
1722
/*                       GetNonStreamingFilename()                      */
1723
/************************************************************************/
1724

1725
std::string VSICurlStreamingFSHandler::GetNonStreamingFilename(
65✔
1726
    const std::string &osFilename) const
1727
{
1728
    if (STARTS_WITH(osFilename.c_str(), GetFSPrefix().c_str()))
65✔
1729
        return GetNonStreamingPrefix() +
130✔
1730
               osFilename.substr(GetFSPrefix().size());
195✔
1731
    return osFilename;
×
1732
}
1733

1734
/************************************************************************/
1735
/*                      IVSIS3LikeStreamingFSHandler                    */
1736
/************************************************************************/
1737

1738
class IVSIS3LikeStreamingFSHandler : public VSICurlStreamingFSHandler
1739
{
1740
    CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeStreamingFSHandler)
1741

1742
  public:
1743
    IVSIS3LikeStreamingFSHandler() = default;
8,090✔
1744

1745
    char **ReadDirEx(const char *pszDirname, int nMaxFiles) override
1✔
1746
    {
1747
        if (STARTS_WITH(pszDirname, GetFSPrefix()))
1✔
1748
        {
1749
            return VSIReadDirEx(
1✔
1750
                (GetNonStreamingPrefix() + (pszDirname + GetFSPrefix().size()))
2✔
1751
                    .c_str(),
1752
                nMaxFiles);
1✔
1753
        }
1754
        return nullptr;
×
1755
    }
1756

1757
    const char *GetOptions() override
5✔
1758
    {
1759
        return VSIGetFileSystemOptions(GetNonStreamingPrefix().c_str());
5✔
1760
    }
1761
};
1762

1763
/************************************************************************/
1764
/*                       VSIS3StreamingFSHandler                        */
1765
/************************************************************************/
1766

1767
class VSIS3StreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
1768
{
1769
    CPL_DISALLOW_COPY_ASSIGN(VSIS3StreamingFSHandler)
1770

1771
  protected:
1772
    CPLString GetFSPrefix() const override
228✔
1773
    {
1774
        return "/vsis3_streaming/";
228✔
1775
    }
1776

1777
    std::string GetNonStreamingPrefix() const override
33✔
1778
    {
1779
        return "/vsis3/";
33✔
1780
    }
1781

1782
    VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
1783
                                             const char *pszURL) override;
1784

1785
  public:
1786
    VSIS3StreamingFSHandler() = default;
1,618✔
1787
    ~VSIS3StreamingFSHandler() override = default;
2,250✔
1788

1789
    void ClearCache() override
321✔
1790
    {
1791
        IVSIS3LikeStreamingFSHandler::ClearCache();
321✔
1792
        VSIS3UpdateParams::ClearCache();
321✔
1793
    }
321✔
1794
};
1795

1796
/************************************************************************/
1797
/*                          VSIS3LikeStreamingHandle                    */
1798
/************************************************************************/
1799

1800
class VSIS3LikeStreamingHandle final : public VSICurlStreamingHandle
1801
{
1802
    CPL_DISALLOW_COPY_ASSIGN(VSIS3LikeStreamingHandle)
1803

1804
    IVSIS3LikeHandleHelper *m_poS3HandleHelper = nullptr;
1805

1806
  protected:
1807
    struct curl_slist *
1808
    GetCurlHeaders(const CPLString &osVerb,
1809
                   const struct curl_slist *psExistingHeaders) override;
1810

1811
    bool StopReceivingBytesOnError() override
71✔
1812
    {
1813
        return false;
71✔
1814
    }
1815

1816
    bool CanRestartOnError(const char *pszErrorMsg, const char *pszHeaders,
1817
                           bool bSetError) override;
1818

1819
    bool InterpretRedirect() override
686✔
1820
    {
1821
        return false;
686✔
1822
    }
1823

1824
  public:
1825
    VSIS3LikeStreamingHandle(IVSIS3LikeStreamingFSHandler *poFS,
1826
                             const char *pszFilename,
1827
                             IVSIS3LikeHandleHelper *poS3HandleHelper);
1828
    ~VSIS3LikeStreamingHandle() override;
1829
};
1830

1831
/************************************************************************/
1832
/*                          CreateFileHandle()                          */
1833
/************************************************************************/
1834

1835
VSICurlStreamingHandle *
1836
VSIS3StreamingFSHandler::CreateFileHandle(const char *pszFilename,
32✔
1837
                                          const char *pszURL)
1838
{
1839
    VSIS3HandleHelper *poS3HandleHelper =
1840
        VSIS3HandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str(), false);
32✔
1841
    if (poS3HandleHelper)
32✔
1842
    {
1843
        return new VSIS3LikeStreamingHandle(this, pszFilename,
1844
                                            poS3HandleHelper);
31✔
1845
    }
1846
    return nullptr;
1✔
1847
}
1848

1849
/************************************************************************/
1850
/*                     VSIS3LikeStreamingHandle()                       */
1851
/************************************************************************/
1852

1853
VSIS3LikeStreamingHandle::VSIS3LikeStreamingHandle(
55✔
1854
    IVSIS3LikeStreamingFSHandler *poFS, const char *pszFilename,
1855
    IVSIS3LikeHandleHelper *poS3HandleHelper)
55✔
1856
    : VSICurlStreamingHandle(poFS, pszFilename,
1857
                             poS3HandleHelper->GetURL().c_str()),
110✔
1858
      m_poS3HandleHelper(poS3HandleHelper)
55✔
1859
{
1860
}
55✔
1861

1862
/************************************************************************/
1863
/*                     ~VSIS3LikeStreamingHandle()                      */
1864
/************************************************************************/
1865

1866
VSIS3LikeStreamingHandle::~VSIS3LikeStreamingHandle()
110✔
1867
{
1868
    delete m_poS3HandleHelper;
55✔
1869
}
110✔
1870

1871
/************************************************************************/
1872
/*                           GetCurlHeaders()                           */
1873
/************************************************************************/
1874

1875
struct curl_slist *VSIS3LikeStreamingHandle::GetCurlHeaders(
55✔
1876
    const CPLString &osVerb, const struct curl_slist *psExistingHeaders)
1877
{
1878
    return m_poS3HandleHelper->GetCurlHeaders(osVerb, psExistingHeaders);
55✔
1879
}
1880

1881
/************************************************************************/
1882
/*                          CanRestartOnError()                         */
1883
/************************************************************************/
1884

1885
bool VSIS3LikeStreamingHandle::CanRestartOnError(const char *pszErrorMsg,
24✔
1886
                                                 const char *pszHeaders,
1887
                                                 bool bSetError)
1888
{
1889
    if (m_poS3HandleHelper->CanRestartOnError(pszErrorMsg, pszHeaders,
24✔
1890
                                              bSetError))
24✔
1891
    {
1892
        SetURL(m_poS3HandleHelper->GetURL().c_str());
4✔
1893
        return true;
4✔
1894
    }
1895
    return false;
20✔
1896
}
1897

1898
/************************************************************************/
1899
/*                       VSIGSStreamingFSHandler                        */
1900
/************************************************************************/
1901

1902
class VSIGSStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
1903
{
1904
  protected:
1905
    CPLString GetFSPrefix() const override
67✔
1906
    {
1907
        return "/vsigs_streaming/";
67✔
1908
    }
1909

1910
    std::string GetNonStreamingPrefix() const override
10✔
1911
    {
1912
        return "/vsigs/";
10✔
1913
    }
1914

1915
    VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
1916
                                             const char *pszURL) override;
1917

1918
  public:
1919
    VSIGSStreamingFSHandler()
1,618✔
1920
    {
1,618✔
1921
    }
1,618✔
1922

1923
    ~VSIGSStreamingFSHandler() override
2,250✔
1924
    {
1,125✔
1925
    }
2,250✔
1926
};
1927

1928
/************************************************************************/
1929
/*                          CreateFileHandle()                          */
1930
/************************************************************************/
1931

1932
VSICurlStreamingHandle *
1933
VSIGSStreamingFSHandler::CreateFileHandle(const char *pszFilename,
10✔
1934
                                          const char *pszURL)
1935
{
1936
    VSIGSHandleHelper *poGCHandleHelper =
1937
        VSIGSHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str());
10✔
1938
    if (poGCHandleHelper)
10✔
1939
    {
1940
        return new VSIS3LikeStreamingHandle(this, pszFilename,
1941
                                            poGCHandleHelper);
9✔
1942
    }
1943
    return nullptr;
1✔
1944
}
1945

1946
/************************************************************************/
1947
/*                      VSIAzureStreamingFSHandler                      */
1948
/************************************************************************/
1949

1950
class VSIAzureStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
1951
{
1952
  protected:
1953
    CPLString GetFSPrefix() const override
39✔
1954
    {
1955
        return "/vsiaz_streaming/";
39✔
1956
    }
1957

1958
    std::string GetNonStreamingPrefix() const override
6✔
1959
    {
1960
        return "/vsiaz/";
6✔
1961
    }
1962

1963
    VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
1964
                                             const char *pszURL) override;
1965

1966
  public:
1967
    VSIAzureStreamingFSHandler()
1,618✔
1968
    {
1,618✔
1969
    }
1,618✔
1970

1971
    ~VSIAzureStreamingFSHandler() override
2,250✔
1972
    {
1,125✔
1973
    }
2,250✔
1974
};
1975

1976
/************************************************************************/
1977
/*                          CreateFileHandle()                          */
1978
/************************************************************************/
1979

1980
VSICurlStreamingHandle *
1981
VSIAzureStreamingFSHandler::CreateFileHandle(const char *pszFilename,
6✔
1982
                                             const char *pszURL)
1983
{
1984
    VSIAzureBlobHandleHelper *poHandleHelper =
1985
        VSIAzureBlobHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str());
6✔
1986
    if (poHandleHelper)
6✔
1987
    {
1988
        return new VSIS3LikeStreamingHandle(this, pszFilename, poHandleHelper);
5✔
1989
    }
1990
    return nullptr;
1✔
1991
}
1992

1993
/************************************************************************/
1994
/*                       VSIOSSStreamingFSHandler                        */
1995
/************************************************************************/
1996

1997
class VSIOSSStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
1998
{
1999
    CPL_DISALLOW_COPY_ASSIGN(VSIOSSStreamingFSHandler)
2000

2001
  protected:
2002
    CPLString GetFSPrefix() const override
60✔
2003
    {
2004
        return "/vsioss_streaming/";
60✔
2005
    }
2006

2007
    std::string GetNonStreamingPrefix() const override
9✔
2008
    {
2009
        return "/vsioss/";
9✔
2010
    }
2011

2012
    VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
2013
                                             const char *pszURL) override;
2014

2015
  public:
2016
    VSIOSSStreamingFSHandler() = default;
1,618✔
2017
    ~VSIOSSStreamingFSHandler() override = default;
2,250✔
2018

2019
    void ClearCache() override
321✔
2020
    {
2021
        IVSIS3LikeStreamingFSHandler::ClearCache();
321✔
2022
        VSIOSSUpdateParams::ClearCache();
321✔
2023
    }
321✔
2024
};
2025

2026
/************************************************************************/
2027
/*                          CreateFileHandle()                          */
2028
/************************************************************************/
2029

2030
VSICurlStreamingHandle *
2031
VSIOSSStreamingFSHandler::CreateFileHandle(const char *pszFilename,
9✔
2032
                                           const char *pszURL)
2033
{
2034
    VSIOSSHandleHelper *poOSSHandleHelper =
2035
        VSIOSSHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str(), false);
9✔
2036
    if (poOSSHandleHelper)
9✔
2037
    {
2038
        return new VSIS3LikeStreamingHandle(this, pszFilename,
2039
                                            poOSSHandleHelper);
8✔
2040
    }
2041
    return nullptr;
1✔
2042
}
2043

2044
/************************************************************************/
2045
/*                      VSISwiftStreamingFSHandler                      */
2046
/************************************************************************/
2047

2048
class VSISwiftStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
2049
{
2050
  protected:
2051
    CPLString GetFSPrefix() const override
18✔
2052
    {
2053
        return "/vsiswift_streaming/";
18✔
2054
    }
2055

2056
    std::string GetNonStreamingPrefix() const override
3✔
2057
    {
2058
        return "/vsiswift/";
3✔
2059
    }
2060

2061
    VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
2062
                                             const char *pszURL) override;
2063

2064
  public:
2065
    VSISwiftStreamingFSHandler()
1,618✔
2066
    {
1,618✔
2067
    }
1,618✔
2068

2069
    ~VSISwiftStreamingFSHandler() override
2,250✔
2070
    {
1,125✔
2071
    }
2,250✔
2072
};
2073

2074
/************************************************************************/
2075
/*                          CreateFileHandle()                          */
2076
/************************************************************************/
2077

2078
VSICurlStreamingHandle *
2079
VSISwiftStreamingFSHandler::CreateFileHandle(const char *pszFilename,
3✔
2080
                                             const char *pszURL)
2081
{
2082
    VSISwiftHandleHelper *poHandleHelper =
2083
        VSISwiftHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str());
3✔
2084
    if (poHandleHelper)
3✔
2085
    {
2086
        return new VSIS3LikeStreamingHandle(this, pszFilename, poHandleHelper);
2✔
2087
    }
2088
    return nullptr;
1✔
2089
}
2090

2091
//! @endcond
2092

2093
} /* namespace cpl */
2094

2095
/************************************************************************/
2096
/*                 VSIInstallCurlStreamingFileHandler()                 */
2097
/************************************************************************/
2098

2099
/*!
2100
 \brief Install /vsicurl_streaming/ HTTP/FTP file system handler (requires
2101
 libcurl).
2102

2103
  \verbatim embed:rst
2104
 See :ref:`/vsicurl_streaming/ documentation <vsicurl_streaming>`
2105
 \endverbatim
2106

2107
 @since GDAL 1.10
2108
 */
2109
void VSIInstallCurlStreamingFileHandler(void)
1,618✔
2110
{
2111
    VSIFileManager::InstallHandler("/vsicurl_streaming/",
1,618✔
2112
                                   new cpl::VSICurlStreamingFSHandler);
1,618✔
2113
}
1,618✔
2114

2115
/************************************************************************/
2116
/*                   VSIInstallS3StreamingFileHandler()                 */
2117
/************************************************************************/
2118

2119
/*!
2120
 \brief Install /vsis3_streaming/ Amazon S3 file system handler (requires
2121
 libcurl).
2122

2123
  \verbatim embed:rst
2124
 See :ref:`/vsis3_streaming/ documentation <vsis3_streaming>`
2125
 \endverbatim
2126

2127
 @since GDAL 2.1
2128
 */
2129
void VSIInstallS3StreamingFileHandler(void)
1,618✔
2130
{
2131
    VSIFileManager::InstallHandler("/vsis3_streaming/",
1,618✔
2132
                                   new cpl::VSIS3StreamingFSHandler);
1,618✔
2133
}
1,618✔
2134

2135
/************************************************************************/
2136
/*                      VSIInstallGSStreamingFileHandler()              */
2137
/************************************************************************/
2138

2139
/*!
2140
 \brief Install /vsigs_streaming/ Google Cloud Storage file system handler
2141
 (requires libcurl)
2142

2143
  \verbatim embed:rst
2144
 See :ref:`/vsigs_streaming/ documentation <vsigs_streaming>`
2145
 \endverbatim
2146

2147
 @since GDAL 2.2
2148
 */
2149

2150
void VSIInstallGSStreamingFileHandler(void)
1,618✔
2151
{
2152
    VSIFileManager::InstallHandler("/vsigs_streaming/",
1,618✔
2153
                                   new cpl::VSIGSStreamingFSHandler);
1,618✔
2154
}
1,618✔
2155

2156
/************************************************************************/
2157
/*                   VSIInstallAzureStreamingFileHandler()              */
2158
/************************************************************************/
2159

2160
/*!
2161
 \brief Install /vsiaz_streaming/ Microsoft Azure Blob file system handler
2162
 (requires libcurl)
2163

2164
  \verbatim embed:rst
2165
 See :ref:`/vsiaz_streaming/ documentation <vsiaz_streaming>`
2166
 \endverbatim
2167

2168
 @since GDAL 2.3
2169
 */
2170

2171
void VSIInstallAzureStreamingFileHandler(void)
1,618✔
2172
{
2173
    VSIFileManager::InstallHandler("/vsiaz_streaming/",
1,618✔
2174
                                   new cpl::VSIAzureStreamingFSHandler);
1,618✔
2175
}
1,618✔
2176

2177
/************************************************************************/
2178
/*                    VSIInstallOSSStreamingFileHandler()               */
2179
/************************************************************************/
2180

2181
/*!
2182
 \brief Install /vsiaz_streaming/ Alibaba Cloud Object Storage Service (OSS)
2183
 (requires libcurl)
2184

2185
  \verbatim embed:rst
2186
 See :ref:`/vsioss_streaming/ documentation <vsioss_streaming>`
2187
 \endverbatim
2188

2189
 @since GDAL 2.3
2190
 */
2191

2192
void VSIInstallOSSStreamingFileHandler(void)
1,618✔
2193
{
2194
    VSIFileManager::InstallHandler("/vsioss_streaming/",
1,618✔
2195
                                   new cpl::VSIOSSStreamingFSHandler);
1,618✔
2196
}
1,618✔
2197

2198
/************************************************************************/
2199
/*                  VSIInstallSwiftStreamingFileHandler()               */
2200
/************************************************************************/
2201

2202
/*!
2203
 \brief Install /vsiswift_streaming/ OpenStack Swif Object Storage (Swift) file
2204
 system handler (requires libcurl)
2205

2206
  \verbatim embed:rst
2207
 See :ref:`/vsiswift_streaming/ documentation <vsiswift_streaming>`
2208
 \endverbatim
2209

2210
 @since GDAL 2.3
2211
 */
2212

2213
void VSIInstallSwiftStreamingFileHandler(void)
1,618✔
2214
{
2215
    VSIFileManager::InstallHandler("/vsiswift_streaming/",
1,618✔
2216
                                   new cpl::VSISwiftStreamingFSHandler);
1,618✔
2217
}
1,618✔
2218

2219
//! @cond Doxygen_Suppress
2220

2221
/************************************************************************/
2222
/*                      VSICurlStreamingClearCache()                    */
2223
/************************************************************************/
2224

2225
void VSICurlStreamingClearCache(void)
321✔
2226
{
2227
    // FIXME ? Currently we have different filesystem instances for
2228
    // vsicurl/, /vsis3/, /vsigs/ . So each one has its own cache of regions.
2229
    // File properties cache are now shared
2230
    char **papszPrefix = VSIFileManager::GetPrefixes();
321✔
2231
    for (size_t i = 0; papszPrefix && papszPrefix[i]; ++i)
9,951✔
2232
    {
2233
        auto poFSHandler = dynamic_cast<cpl::VSICurlStreamingFSHandler *>(
×
2234
            VSIFileManager::GetHandler(papszPrefix[i]));
9,630✔
2235

2236
        if (poFSHandler)
9,630✔
2237
            poFSHandler->ClearCache();
1,926✔
2238
    }
2239
    CSLDestroy(papszPrefix);
321✔
2240
}
321✔
2241

2242
//! @endcond
2243

2244
#undef ENABLE_DEBUG
2245

2246
#endif  // !defined(HAVE_CURL) || defined(CPL_MULTIPROC_STUB)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc