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

OSGeo / gdal / 8872387746

29 Apr 2024 02:16AM UTC coverage: 69.076% (+0.003%) from 69.073%
8872387746

Pull #9801

github

web-flow
Bump actions/upload-artifact from 4.3.2 to 4.3.3

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.2 to 4.3.3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/1746f4ab6...65462800f)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #9801: Bump actions/upload-artifact from 4.3.2 to 4.3.3

534153 of 773282 relevant lines covered (69.08%)

205719.18 hits per line

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

86.67
/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
 * 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
    size_t GetCapacity() const
200✔
120
    {
121
        return nCapacity;
200✔
122
    }
123

124
    size_t GetSize() const
697✔
125
    {
126
        return nLength;
697✔
127
    }
128

129
    void Reset();
130
    void Write(void *pBuffer, size_t nSize);
131
    void Read(void *pBuffer, size_t nSize);
132
};
133

134
RingBuffer::RingBuffer(size_t nCapacityIn)
62✔
135
    : pabyBuffer(static_cast<GByte *>(CPLMalloc(nCapacityIn))),
62✔
136
      nCapacity(nCapacityIn)
62✔
137
{
138
}
62✔
139

140
RingBuffer::~RingBuffer()
124✔
141
{
142
    CPLFree(pabyBuffer);
62✔
143
}
62✔
144

145
void RingBuffer::Reset()
148✔
146
{
147
    nOffset = 0;
148✔
148
    nLength = 0;
148✔
149
}
148✔
150

151
void RingBuffer::Write(void *pBuffer, size_t nSize)
198✔
152
{
153
    CPLAssert(nLength + nSize <= nCapacity);
198✔
154

155
    const size_t nEndOffset = (nOffset + nLength) % nCapacity;
198✔
156
    const size_t nSz = std::min(nSize, nCapacity - nEndOffset);
198✔
157
    memcpy(pabyBuffer + nEndOffset, pBuffer, nSz);
198✔
158
    if (nSz < nSize)
198✔
159
        memcpy(pabyBuffer, static_cast<GByte *>(pBuffer) + nSz, nSize - nSz);
×
160

161
    nLength += nSize;
198✔
162
}
198✔
163

164
void RingBuffer::Read(void *pBuffer, size_t nSize)
205✔
165
{
166
    CPLAssert(nSize <= nLength);
205✔
167

168
    if (pBuffer)
205✔
169
    {
170
        const size_t nSz = std::min(nSize, nCapacity - nOffset);
205✔
171
        memcpy(pBuffer, pabyBuffer + nOffset, nSz);
205✔
172
        if (nSz < nSize)
205✔
173
            memcpy(static_cast<GByte *>(pBuffer) + nSz, pabyBuffer,
×
174
                   nSize - nSz);
175
    }
176

177
    nOffset = (nOffset + nSize) % nCapacity;
205✔
178
    nLength -= nSize;
205✔
179
}
205✔
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
    virtual CPLString GetFSPrefix()
32✔
234
    {
235
        return "/vsicurl_streaming/";
32✔
236
    }
237

238
    const char *GetActualURL(const char *pszFilename) override;
239

240
    const char *GetOptions() override
1✔
241
    {
242
        return VSIGetFileSystemOptions("/vsicurl/");
1✔
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
    GetCurlHeaders(const CPLString &,
11✔
315
                   const struct curl_slist * /* psExistingHeaders */)
316
    {
317
        return nullptr;
11✔
318
    }
319

320
    virtual bool StopReceivingBytesOnError()
8✔
321
    {
322
        return true;
8✔
323
    }
324

325
    virtual bool CanRestartOnError(const char * /*pszErrorMsg*/,
×
326
                                   const char * /*pszHeaders*/,
327
                                   bool /*bSetError*/)
328
    {
329
        return false;
×
330
    }
331

332
    virtual bool InterpretRedirect()
106✔
333
    {
334
        return true;
106✔
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
    bool IsKnownFileSize() const
9✔
356
    {
357
        return bHasComputedFileSize;
9✔
358
    }
359

360
    vsi_l_offset GetFileSize();
361
    bool Exists(const char *pszFilename, CSLConstList papszOptions);
362

363
    bool IsDirectory() const
17✔
364
    {
365
        return bIsDirectory;
17✔
366
    }
367

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

374
/************************************************************************/
375
/*                       VSICurlStreamingHandle()                       */
376
/************************************************************************/
377

378
VSICurlStreamingHandle::VSICurlStreamingHandle(VSICurlStreamingFSHandler *poFS,
62✔
379
                                               const char *pszURL)
62✔
380
    : m_poFS(poFS), m_papszHTTPOptions(CPLHTTPGetOptionsFromEnv(
62✔
381
                        (poFS->GetFSPrefix() + pszURL).c_str())),
124✔
382
      m_pszURL(CPLStrdup(pszURL))
186✔
383
{
384
    FileProp cachedFileProp;
62✔
385
    poFS->GetCachedFileProp(pszURL, cachedFileProp);
62✔
386
    eExists = cachedFileProp.eExists;
62✔
387
    fileSize = cachedFileProp.fileSize;
62✔
388
    bHasComputedFileSize = cachedFileProp.bHasComputedFileSize;
62✔
389
    bIsDirectory = cachedFileProp.bIsDirectory;
62✔
390
    poFS->SetCachedFileProp(pszURL, cachedFileProp);
62✔
391

392
    hRingBufferMutex = CPLCreateMutex();
62✔
393
    ReleaseMutex();
62✔
394
    hCondProducer = CPLCreateCond();
62✔
395
    hCondConsumer = CPLCreateCond();
62✔
396

397
    memset(m_szCurlErrBuf, 0, sizeof(m_szCurlErrBuf));
62✔
398
}
62✔
399

400
/************************************************************************/
401
/*                       ~VSICurlStreamingHandle()                      */
402
/************************************************************************/
403

404
VSICurlStreamingHandle::~VSICurlStreamingHandle()
69✔
405
{
406
    StopDownload();
62✔
407

408
    CPLFree(m_pszURL);
62✔
409
    CSLDestroy(m_papszHTTPOptions);
62✔
410

411
    CPLFree(pCachedData);
62✔
412

413
    CPLFree(pabyHeaderData);
62✔
414

415
    CPLDestroyMutex(hRingBufferMutex);
62✔
416
    CPLDestroyCond(hCondProducer);
62✔
417
    CPLDestroyCond(hCondConsumer);
62✔
418
}
69✔
419

420
/************************************************************************/
421
/*                            SetURL()                                  */
422
/************************************************************************/
423

424
void VSICurlStreamingHandle::SetURL(const char *pszURLIn)
4✔
425
{
426
    CPLFree(m_pszURL);
4✔
427
    m_pszURL = CPLStrdup(pszURLIn);
4✔
428
}
4✔
429

430
/************************************************************************/
431
/*                         AcquireMutex()                               */
432
/************************************************************************/
433

434
void VSICurlStreamingHandle::AcquireMutex()
1,175✔
435
{
436
    CPLAcquireMutex(hRingBufferMutex, 1000.0);
1,175✔
437
}
1,175✔
438

439
/************************************************************************/
440
/*                          ReleaseMutex()                              */
441
/************************************************************************/
442

443
void VSICurlStreamingHandle::ReleaseMutex()
1,237✔
444
{
445
    CPLReleaseMutex(hRingBufferMutex);
1,237✔
446
}
1,237✔
447

448
/************************************************************************/
449
/*                                Seek()                                */
450
/************************************************************************/
451

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

469
    if (nWhence == SEEK_SET)
57✔
470
    {
471
        curOffset = nOffset;
57✔
472
    }
473
    else if (nWhence == SEEK_CUR)
×
474
    {
475
        curOffset = curOffset + nOffset;
×
476
    }
477
    else
478
    {
479
        curOffset = GetFileSize() + nOffset;
×
480
    }
481
    bEOF = false;
57✔
482
    return 0;
57✔
483
}
484

485
/************************************************************************/
486
/*                  VSICURLStreamingInitWriteFuncStructStreaming() */
487
/************************************************************************/
488

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

500
/************************************************************************/
501
/*                 VSICurlStreamingHandleWriteFuncForHeader()           */
502
/************************************************************************/
503

504
static size_t VSICurlStreamingHandleWriteFuncForHeader(void *buffer,
45✔
505
                                                       size_t count,
506
                                                       size_t nmemb, void *req)
507
{
508
    WriteFuncStructStreaming *psStruct =
45✔
509
        static_cast<WriteFuncStructStreaming *>(req);
510
    const size_t nSize = count * nmemb;
45✔
511

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

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

555
/************************************************************************/
556
/*                           GetFileSize()                              */
557
/************************************************************************/
558

559
vsi_l_offset VSICurlStreamingHandle::GetFileSize()
9✔
560
{
561
    WriteFuncStructStreaming sWriteFuncData;
562
    WriteFuncStructStreaming sWriteFuncHeaderData;
563

564
    AcquireMutex();
9✔
565
    if (bHasComputedFileSize)
9✔
566
    {
567
        const vsi_l_offset nRet = fileSize;
1✔
568
        ReleaseMutex();
1✔
569
        return nRet;
1✔
570
    }
571
    ReleaseMutex();
8✔
572

573
    CURL *hLocalHandle = curl_easy_init();
8✔
574

575
    struct curl_slist *headers =
576
        VSICurlSetOptions(hLocalHandle, m_pszURL, m_papszHTTPOptions);
8✔
577

578
    VSICURLStreamingInitWriteFuncStructStreaming(&sWriteFuncHeaderData);
8✔
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
    CPLString osVerb;
16✔
584
    if (strstr(m_pszURL, ".tiles.mapbox.com/") != nullptr)
8✔
585
    {
586
        unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HEADERDATA,
×
587
                                   &sWriteFuncHeaderData);
588
        unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HEADERFUNCTION,
×
589
                                   VSICurlStreamingHandleWriteFuncForHeader);
590

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

603
    headers = VSICurlMergeHeaders(headers, GetCurlHeaders(osVerb, headers));
8✔
604
    unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HTTPHEADER, headers);
8✔
605

606
    // We need that otherwise OSGEO4W's libcurl issue a dummy range request
607
    // when doing a HEAD when recycling connections.
608
    unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_RANGE, nullptr);
8✔
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
    VSICURLStreamingInitWriteFuncStructStreaming(&sWriteFuncData);
8✔
613
    unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_WRITEDATA,
8✔
614
                               &sWriteFuncData);
615
    unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_WRITEFUNCTION,
8✔
616
                               VSICurlStreamingHandleWriteFuncForHeader);
617

618
    char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
8✔
619
    unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf);
8✔
620

621
    void *old_handler = CPLHTTPIgnoreSigPipe();
8✔
622
    curl_easy_perform(hLocalHandle);
8✔
623
    CPLHTTPRestoreSigPipeHandler(old_handler);
8✔
624
    if (headers != nullptr)
8✔
625
        curl_slist_free_all(headers);
8✔
626

627
    AcquireMutex();
8✔
628

629
    eExists = EXIST_UNKNOWN;
8✔
630
    bHasComputedFileSize = true;
8✔
631

632
    if (STARTS_WITH(m_pszURL, "ftp"))
8✔
633
    {
634
        if (sWriteFuncData.pBuffer != nullptr &&
×
635
            STARTS_WITH_CI(sWriteFuncData.pBuffer, "Content-Length: "))
×
636
        {
637
            const char *pszBuffer =
×
638
                sWriteFuncData.pBuffer + strlen("Content-Length: ");
×
639
            eExists = EXIST_YES;
×
640
            fileSize = CPLScanUIntBig(
×
641
                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
    double dfSize = 0;
8✔
650
    if (eExists != EXIST_YES)
8✔
651
    {
652
        curl_off_t nSizeTmp = 0;
8✔
653
        const CURLcode code = curl_easy_getinfo(
8✔
654
            hLocalHandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &nSizeTmp);
655
        CPL_IGNORE_RET_VAL(dfSize);
8✔
656
        dfSize = static_cast<double>(nSizeTmp);
8✔
657
        if (code == 0)
8✔
658
        {
659
            eExists = EXIST_YES;
8✔
660
            if (dfSize < 0)
8✔
661
                fileSize = 0;
×
662
            else
663
                fileSize = static_cast<GUIntBig>(dfSize);
8✔
664
        }
665
        else
666
        {
667
            eExists = EXIST_NO;
×
668
            fileSize = 0;
×
669
            CPLError(CE_Failure, CPLE_AppDefined,
×
670
                     "VSICurlStreamingHandle::GetFileSize failed");
671
        }
672

673
        long response_code = 0;
8✔
674
        curl_easy_getinfo(hLocalHandle, CURLINFO_HTTP_CODE, &response_code);
8✔
675
        if (response_code != 200)
8✔
676
        {
677
            eExists = EXIST_NO;
×
678
            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
        char *pszEffectiveURL = nullptr;
8✔
684
        curl_easy_getinfo(hLocalHandle, CURLINFO_EFFECTIVE_URL,
8✔
685
                          &pszEffectiveURL);
686
        if (pszEffectiveURL != nullptr &&
8✔
687
            strncmp(m_pszURL, pszEffectiveURL, strlen(m_pszURL)) == 0 &&
8✔
688
            pszEffectiveURL[strlen(m_pszURL)] == '/')
8✔
689
        {
690
            eExists = EXIST_YES;
×
691
            fileSize = 0;
×
692
            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
    CPLFree(sWriteFuncData.pBuffer);
8✔
702
    CPLFree(sWriteFuncHeaderData.pBuffer);
8✔
703

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

714
    const vsi_l_offset nRet = fileSize;
8✔
715
    ReleaseMutex();
8✔
716

717
    curl_easy_cleanup(hLocalHandle);
8✔
718

719
    return nRet;
8✔
720
}
721

722
/************************************************************************/
723
/*                                 Exists()                             */
724
/************************************************************************/
725

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

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

749
                return false;
×
750
            }
751
        }
752

753
        char chFirstByte = '\0';
48✔
754
        int bExists = (Read(&chFirstByte, 1, 1) == 1);
48✔
755

756
        FileProp cachedFileProp;
96✔
757
        m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
48✔
758
        cachedFileProp.eExists = eExists = bExists ? EXIST_YES : EXIST_NO;
48✔
759
        m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
48✔
760

761
        Seek(0, SEEK_SET);
48✔
762
    }
763

764
    return eExists == EXIST_YES;
59✔
765
}
766

767
/************************************************************************/
768
/*                                  Tell()                              */
769
/************************************************************************/
770

771
vsi_l_offset VSICurlStreamingHandle::Tell()
×
772
{
773
    return curOffset;
×
774
}
775

776
/************************************************************************/
777
/*                         ReceivedBytes()                              */
778
/************************************************************************/
779

780
size_t VSICurlStreamingHandle::ReceivedBytes(GByte *buffer, size_t count,
197✔
781
                                             size_t nmemb)
782
{
783
    size_t nSize = count * nmemb;
197✔
784
    nBodySize += nSize;
197✔
785

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

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

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

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

823
            // Signal to the consumer that we have added bytes to the buffer.
824
            CPLCondSignal(hCondProducer);
197✔
825

826
            if (bAskDownloadEnd)
197✔
827
            {
828
                if (ENABLE_DEBUG)
829
                    CPLDebug("VSICURL", "Download interruption asked");
830

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

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

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

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

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

860
                ReleaseMutex();
×
861
                return 0;
×
862
            }
863
        }
864
    }
1✔
865

866
    ReleaseMutex();
197✔
867

868
    return nmemb;
197✔
869
}
870

871
/************************************************************************/
872
/*                 VSICurlStreamingHandleReceivedBytes()                */
873
/************************************************************************/
874

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

882
/************************************************************************/
883
/*              VSICurlStreamingHandleReceivedBytesHeader()             */
884
/************************************************************************/
885

886
#define HEADER_SIZE 32768
887

888
size_t VSICurlStreamingHandle::ReceivedBytesHeader(GByte *buffer, size_t count,
399✔
889
                                                   size_t nmemb)
890
{
891
    const size_t nSize = count * nmemb;
399✔
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
    if (nSize >= 9 && InterpretRedirect() &&
343✔
898
        (nHTTPCode == 301 || nHTTPCode == 302) &&
742✔
899
        STARTS_WITH_CI(reinterpret_cast<char *>(buffer), "HTTP/"))
×
900
    {
901
        nHeaderSize = 0;
×
902
        nHTTPCode = 0;
×
903
    }
904

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

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

916
        AcquireMutex();
399✔
917

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

931
            // If moved permanently/temporarily, go on.
932
            if (eExists == EXIST_UNKNOWN &&
106✔
933
                !(InterpretRedirect() &&
50✔
934
                  (nHTTPCode == 301 || nHTTPCode == 302)))
7✔
935
            {
936
                eExists = nHTTPCode == 200 ? EXIST_YES : EXIST_NO;
50✔
937
                FileProp cachedFileProp;
100✔
938
                m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
50✔
939
                cachedFileProp.eExists = eExists;
50✔
940
                m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
50✔
941
            }
942
        }
943

944
        if (!(InterpretRedirect() && (nHTTPCode == 301 || nHTTPCode == 302)) &&
798✔
945
            !bHasComputedFileSize)
399✔
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
            const char *pszContentLength = strstr(
375✔
952
                reinterpret_cast<char *>(pabyHeaderData), "Content-Length: ");
375✔
953
            const char *pszEndOfLine =
375✔
954
                pszContentLength ? strchr(pszContentLength, '\n') : nullptr;
375✔
955
            if (bCanTrustCandidateFileSize && pszEndOfLine != nullptr)
375✔
956
            {
957
                const char *pszVal =
97✔
958
                    pszContentLength + strlen("Content-Length: ");
959
                bHasCandidateFileSize = true;
97✔
960
                nCandidateFileSize = CPLScanUIntBig(
97✔
961
                    pszVal, static_cast<int>(pszEndOfLine - pszVal));
97✔
962
                if (ENABLE_DEBUG)
963
                    CPLDebug("VSICURL",
964
                             "Has found candidate file size = " CPL_FRMT_GUIB,
965
                             nCandidateFileSize);
966
            }
967

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

986
        ReleaseMutex();
399✔
987
    }
988

989
    return nmemb;
399✔
990
}
991

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

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

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

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

1012
    struct curl_slist *headers =
1013
        VSICurlSetOptions(hCurlHandle, m_pszURL, m_papszHTTPOptions);
58✔
1014
    headers = VSICurlMergeHeaders(headers, GetCurlHeaders("GET", headers));
58✔
1015
    unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
58✔
1016

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

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

1035
    unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA, this);
58✔
1036
    unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION,
58✔
1037
                               VSICurlStreamingHandleReceivedBytesHeader);
1038

1039
    unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, this);
58✔
1040
    unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
58✔
1041
                               VSICurlStreamingHandleReceivedBytes);
1042

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

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

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

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

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

1083
    bDownloadInProgress = FALSE;
58✔
1084
    bDownloadStopped = TRUE;
58✔
1085

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

1090
    curl_easy_cleanup(hCurlHandle);
58✔
1091
}
58✔
1092

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

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

1102
void VSICurlStreamingHandle::StartDownload()
96✔
1103
{
1104
    if (bDownloadInProgress || bDownloadStopped)
96✔
1105
        return;
38✔
1106

1107
    CPLDebug("VSICURL", "Start download for %s", m_pszURL);
58✔
1108

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

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

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

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

1131
        // Wait for the producer to have finished.
1132
        while (bDownloadInProgress)
58✔
1133
            CPLCondWait(hCondProducer, hRingBufferMutex);
×
1134

1135
        bAskDownloadEnd = FALSE;
58✔
1136

1137
        ReleaseMutex();
58✔
1138

1139
        CPLJoinThread(hThread);
58✔
1140
        hThread = nullptr;
58✔
1141
    }
1142

1143
    oRingBuffer.Reset();
90✔
1144
    bDownloadStopped = FALSE;
90✔
1145
    m_bErrorOccurred = false;
90✔
1146
    nRingBufferFileOffset = 0;
90✔
1147
    bEOF = false;
90✔
1148
}
90✔
1149

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

1154
void VSICurlStreamingHandle::PutRingBufferInCache()
38✔
1155
{
1156
    if (nRingBufferFileOffset >= BKGND_BUFFER_SIZE)
38✔
1157
        return;
1✔
1158

1159
    AcquireMutex();
37✔
1160

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

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

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

1179
    ReleaseMutex();
37✔
1180
}
1181

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

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

1195
    int nRetryCount = 0;
113✔
1196
    double dfRetryDelay = 0;
113✔
1197

1198
retry:
121✔
1199
    GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
121✔
1200
    size_t nRemaining = nBufferRequestSize;
121✔
1201

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

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

1217
    if (curOffset < nRingBufferFileOffset)
121✔
1218
        PutRingBufferInCache();
38✔
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
    if (pCachedData != nullptr && curOffset < nCachedSize)
121✔
1226
    {
1227
        const size_t nSz =
1228
            std::min(nRemaining, static_cast<size_t>(nCachedSize - curOffset));
41✔
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
        memcpy(pabyBuffer, pCachedData + curOffset, nSz);
41✔
1234
        pabyBuffer += nSz;
41✔
1235
        curOffset += nSz;
41✔
1236
        nRemaining -= nSz;
41✔
1237
    }
1238

1239
    // Is the request partially covered by the cache and going beyond file size?
1240
    if (pCachedData != nullptr && bHasComputedFileSizeLocal &&
121✔
1241
        curOffset <= nCachedSize && curOffset + nRemaining > fileSizeLocal &&
34✔
1242
        fileSize == nCachedSize)
29✔
1243
    {
1244
        size_t nSz = static_cast<size_t>(nCachedSize - curOffset);
13✔
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
        memcpy(pabyBuffer, pCachedData + curOffset, nSz);
13✔
1250
        pabyBuffer += nSz;
13✔
1251
        curOffset += nSz;
13✔
1252
        nRemaining -= nSz;
13✔
1253
        bEOF = true;
13✔
1254
    }
1255

1256
    // Has a Seek() being done since the last Read()?
1257
    bool bErrorOccurred = false;
121✔
1258

1259
    if (!bEOF && nRemaining > 0 && curOffset != nRingBufferFileOffset)
121✔
1260
    {
1261
        // Backward seek: Need to restart the download from the beginning.
1262
        if (curOffset < nRingBufferFileOffset)
3✔
1263
            StopDownload();
×
1264

1265
        StartDownload();
3✔
1266

1267
        const vsi_l_offset SKIP_BUFFER_SIZE = 32768;
3✔
1268
        GByte *pabyTmp = static_cast<GByte *>(CPLMalloc(SKIP_BUFFER_SIZE));
3✔
1269

1270
        CPLAssert(curOffset >= nRingBufferFileOffset);
3✔
1271
        vsi_l_offset nBytesToSkip = curOffset - nRingBufferFileOffset;
3✔
1272
        while (nBytesToSkip > 0)
9✔
1273
        {
1274
            vsi_l_offset nBytesToRead = nBytesToSkip;
8✔
1275

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

1283
            // Signal to the producer that we have ingested some bytes.
1284
            CPLCondSignal(hCondConsumer);
8✔
1285
            ReleaseMutex();
8✔
1286

1287
            if (nBytesToRead)
8✔
1288
                AddRegion(nRingBufferFileOffset,
3✔
1289
                          static_cast<size_t>(nBytesToRead), pabyTmp);
1290

1291
            nBytesToSkip -= nBytesToRead;
8✔
1292
            nRingBufferFileOffset += nBytesToRead;
8✔
1293

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

1300
                AcquireMutex();
5✔
1301
                while (oRingBuffer.GetSize() == 0 && bDownloadInProgress)
8✔
1302
                    CPLCondWait(hCondProducer, hRingBufferMutex);
3✔
1303
                const int bBufferEmpty = (oRingBuffer.GetSize() == 0);
5✔
1304
                bErrorOccurred = m_bErrorOccurred;
5✔
1305
                ReleaseMutex();
5✔
1306

1307
                if (bBufferEmpty && !bDownloadInProgress)
5✔
1308
                    break;
2✔
1309
            }
1310
        }
1311

1312
        CPLFree(pabyTmp);
3✔
1313

1314
        if (nBytesToSkip != 0 && !bErrorOccurred)
3✔
1315
        {
1316
            bEOF = true;
×
1317
            return 0;
×
1318
        }
1319
    }
1320

1321
    if (!bEOF && nRemaining > 0 && !bErrorOccurred)
121✔
1322
    {
1323
        StartDownload();
93✔
1324
        CPLAssert(curOffset == nRingBufferFileOffset);
93✔
1325
    }
1326

1327
    // Fill the destination buffer from the ring buffer.
1328
    while (!bEOF && nRemaining > 0 && !bErrorOccurred)
252✔
1329
    {
1330
        AcquireMutex();
171✔
1331
        size_t nToRead = oRingBuffer.GetSize();
171✔
1332
        if (nToRead > nRemaining)
171✔
1333
            nToRead = nRemaining;
50✔
1334
        oRingBuffer.Read(pabyBuffer, nToRead);
171✔
1335

1336
        // Signal to the producer that we have ingested some bytes.
1337
        CPLCondSignal(hCondConsumer);
171✔
1338
        ReleaseMutex();
171✔
1339

1340
        if (nToRead)
171✔
1341
            AddRegion(curOffset, nToRead, pabyBuffer);
78✔
1342

1343
        nRemaining -= nToRead;
171✔
1344
        pabyBuffer += nToRead;
171✔
1345
        curOffset += nToRead;
171✔
1346
        nRingBufferFileOffset += nToRead;
171✔
1347

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

1354
            AcquireMutex();
93✔
1355
            while (oRingBuffer.GetSize() == 0 && bDownloadInProgress)
168✔
1356
                CPLCondWait(hCondProducer, hRingBufferMutex);
75✔
1357
            const bool bBufferEmpty = oRingBuffer.GetSize() == 0;
93✔
1358
            bErrorOccurred = m_bErrorOccurred;
93✔
1359
            ReleaseMutex();
93✔
1360

1361
            if (bBufferEmpty && !bDownloadInProgress)
93✔
1362
                break;
40✔
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
    size_t nRet = (nBufferRequestSize - nRemaining) / nSize;
121✔
1371
    if (nRet < nMemb)
121✔
1372
        bEOF = true;
56✔
1373

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

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

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

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

1419
        CPLFree(pabyErrorBuffer);
20✔
1420
    }
1421

1422
    if (bErrorOccurred)
117✔
1423
    {
1424
        const int nMaxRetry = atoi(CPLGetConfigOption(
7✔
1425
            "GDAL_HTTP_MAX_RETRY", CPLSPrintf("%d", CPL_HTTP_MAX_RETRY)));
1426
        // coverity[tainted_data]
1427
        if (dfRetryDelay == 0)
7✔
1428
            dfRetryDelay = CPLAtof(
5✔
1429
                CPLGetConfigOption("GDAL_HTTP_RETRY_DELAY",
1430
                                   CPLSPrintf("%f", CPL_HTTP_RETRY_DELAY)));
1431

1432
        // Look if we should attempt a retry
1433
        AcquireMutex();
7✔
1434
        const double dfNewRetryDelay = CPLHTTPGetNewRetryDelay(
14✔
1435
            static_cast<int>(nHTTPCode), dfRetryDelay, nullptr, m_szCurlErrBuf);
7✔
1436
        ReleaseMutex();
7✔
1437
        if (dfNewRetryDelay > 0 && nRetryCount < nMaxRetry)
7✔
1438
        {
1439
            StopDownload();
4✔
1440

1441
            CPLError(CE_Warning, CPLE_AppDefined,
4✔
1442
                     "HTTP error code: %d - %s. "
1443
                     "Retrying again in %.1f secs",
1444
                     static_cast<int>(nHTTPCode), m_pszURL, dfRetryDelay);
4✔
1445
            CPLSleep(dfRetryDelay);
4✔
1446
            dfRetryDelay = dfNewRetryDelay;
4✔
1447
            nRetryCount++;
4✔
1448
            curOffset = curOffsetOri;
4✔
1449
            goto retry;
4✔
1450
        }
1451
    }
1452

1453
    return nRet;
113✔
1454
}
1455

1456
/************************************************************************/
1457
/*                          AddRegion()                                 */
1458
/************************************************************************/
1459

1460
void VSICurlStreamingHandle::AddRegion(vsi_l_offset nFileOffsetStart,
107✔
1461
                                       size_t nSize, GByte *pData)
1462
{
1463
    if (nFileOffsetStart >= BKGND_BUFFER_SIZE)
107✔
1464
        return;
1✔
1465

1466
    if (pCachedData == nullptr)
106✔
1467
        pCachedData = static_cast<GByte *>(CPLMalloc(BKGND_BUFFER_SIZE));
48✔
1468

1469
    if (nFileOffsetStart <= nCachedSize &&
106✔
1470
        nFileOffsetStart + nSize > nCachedSize)
106✔
1471
    {
1472
        const size_t nSz = std::min(
1473
            nSize, static_cast<size_t>(BKGND_BUFFER_SIZE - nFileOffsetStart));
103✔
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
        memcpy(pCachedData + nFileOffsetStart, pData, nSz);
103✔
1479
        nCachedSize = static_cast<size_t>(nFileOffsetStart + nSz);
103✔
1480
    }
1481
}
1482

1483
/************************************************************************/
1484
/*                               Write()                                */
1485
/************************************************************************/
1486

1487
size_t VSICurlStreamingHandle::Write(const void * /* pBuffer */,
×
1488
                                     size_t /* nSize */, size_t /* nMemb */)
1489
{
1490
    return 0;
×
1491
}
1492

1493
/************************************************************************/
1494
/*                                 Eof()                                */
1495
/************************************************************************/
1496

1497
int VSICurlStreamingHandle::Eof()
4✔
1498
{
1499
    return bEOF;
4✔
1500
}
1501

1502
/************************************************************************/
1503
/*                                 Flush()                              */
1504
/************************************************************************/
1505

1506
int VSICurlStreamingHandle::Flush()
×
1507
{
1508
    return 0;
×
1509
}
1510

1511
/************************************************************************/
1512
/*                                  Close()                             */
1513
/************************************************************************/
1514

1515
int VSICurlStreamingHandle::Close()
28✔
1516
{
1517
    return 0;
28✔
1518
}
1519

1520
/************************************************************************/
1521
/*                      VSICurlStreamingFSHandler()                     */
1522
/************************************************************************/
1523

1524
VSICurlStreamingFSHandler::VSICurlStreamingFSHandler()
7,368✔
1525
    : oCacheFileProp{100 * 1024}
7,368✔
1526
{
1527
    hMutex = CPLCreateMutex();
7,368✔
1528
    CPLReleaseMutex(hMutex);
7,368✔
1529
}
7,368✔
1530

1531
/************************************************************************/
1532
/*                      ~VSICurlStreamingFSHandler()                    */
1533
/************************************************************************/
1534

1535
VSICurlStreamingFSHandler::~VSICurlStreamingFSHandler()
5,964✔
1536
{
1537
    VSICurlStreamingFSHandler::ClearCache();
5,112✔
1538

1539
    CPLDestroyMutex(hMutex);
5,112✔
1540
    hMutex = nullptr;
5,112✔
1541
}
5,964✔
1542

1543
/************************************************************************/
1544
/*                            ClearCache()                              */
1545
/************************************************************************/
1546

1547
void VSICurlStreamingFSHandler::ClearCache()
6,588✔
1548
{
1549
    CPLMutexHolder oHolder(&hMutex);
13,176✔
1550

1551
    {
1552
        const auto lambda = [](const lru11::KeyValuePair<std::string, bool> &kv)
58✔
1553
        { VSICURLInvalidateCachedFileProp(kv.key.c_str()); };
58✔
1554
        oCacheFileProp.cwalk(lambda);
6,588✔
1555
        oCacheFileProp.clear();
6,588✔
1556
    }
1557
}
6,588✔
1558

1559
/************************************************************************/
1560
/*                         AcquireMutex()                               */
1561
/************************************************************************/
1562

1563
void VSICurlStreamingFSHandler::AcquireMutex()
×
1564
{
1565
    CPLAcquireMutex(hMutex, 1000.0);
×
1566
}
×
1567

1568
/************************************************************************/
1569
/*                         ReleaseMutex()                               */
1570
/************************************************************************/
1571

1572
void VSICurlStreamingFSHandler::ReleaseMutex()
×
1573
{
1574
    CPLReleaseMutex(hMutex);
×
1575
}
×
1576

1577
/************************************************************************/
1578
/*                          CreateFileHandle()                          */
1579
/************************************************************************/
1580

1581
VSICurlStreamingHandle *
1582
VSICurlStreamingFSHandler::CreateFileHandle(const char *pszURL)
7✔
1583
{
1584
    return new VSICurlStreamingHandle(this, pszURL);
7✔
1585
}
1586

1587
/************************************************************************/
1588
/*                                Open()                                */
1589
/************************************************************************/
1590

1591
VSIVirtualHandle *VSICurlStreamingFSHandler::Open(const char *pszFilename,
57✔
1592
                                                  const char *pszAccess,
1593
                                                  bool /* bSetError */,
1594
                                                  CSLConstList papszOptions)
1595
{
1596
    if (!STARTS_WITH_CI(pszFilename, GetFSPrefix()))
57✔
1597
        return nullptr;
2✔
1598

1599
    if (strchr(pszAccess, 'w') != nullptr || strchr(pszAccess, '+') != nullptr)
55✔
1600
    {
1601
        CPLError(CE_Failure, CPLE_AppDefined,
×
1602
                 "Only read-only mode is supported for %s",
1603
                 GetFSPrefix().c_str());
×
1604
        return nullptr;
×
1605
    }
1606

1607
    VSICurlStreamingHandle *poHandle =
1608
        CreateFileHandle(pszFilename + GetFSPrefix().size());
55✔
1609
    // If we didn't get a filelist, check that the file really exists.
1610
    if (poHandle == nullptr || !poHandle->Exists(pszFilename, papszOptions))
55✔
1611
    {
1612
        delete poHandle;
27✔
1613
        return nullptr;
27✔
1614
    }
1615

1616
    if (CPLTestBool(CPLGetConfigOption("VSI_CACHE", "FALSE")))
28✔
1617
        return VSICreateCachedFile(poHandle);
×
1618

1619
    return poHandle;
28✔
1620
}
1621

1622
/************************************************************************/
1623
/*                         GetCachedFileProp()                          */
1624
/************************************************************************/
1625

1626
bool VSICurlStreamingFSHandler::GetCachedFileProp(const char *pszURL,
405✔
1627
                                                  FileProp &oFileProp)
1628
{
1629
    CPLMutexHolder oHolder(&hMutex);
810✔
1630
    bool inCache;
1631
    if (oCacheFileProp.tryGet(std::string(pszURL), inCache))
405✔
1632
    {
1633
        if (VSICURLGetCachedFileProp(pszURL, oFileProp))
345✔
1634
        {
1635
            return true;
345✔
1636
        }
1637
        oCacheFileProp.remove(std::string(pszURL));
×
1638
    }
1639
    return false;
60✔
1640
}
1641

1642
/************************************************************************/
1643
/*                         SetCachedFileProp()                          */
1644
/************************************************************************/
1645

1646
void VSICurlStreamingFSHandler::SetCachedFileProp(const char *pszURL,
405✔
1647
                                                  FileProp &oFileProp)
1648
{
1649
    CPLMutexHolder oHolder(&hMutex);
810✔
1650
    oCacheFileProp.insert(std::string(pszURL), true);
405✔
1651
    VSICURLSetCachedFileProp(pszURL, oFileProp);
405✔
1652
}
405✔
1653

1654
/************************************************************************/
1655
/*                                Stat()                                */
1656
/************************************************************************/
1657

1658
int VSICurlStreamingFSHandler::Stat(const char *pszFilename,
11✔
1659
                                    VSIStatBufL *pStatBuf, int nFlags)
1660
{
1661
    if (!STARTS_WITH_CI(pszFilename, GetFSPrefix()))
11✔
1662
        return -1;
2✔
1663

1664
    if ((nFlags & VSI_STAT_CACHE_ONLY) != 0)
9✔
1665
    {
1666
        const std::string osVSICURLFilename =
1667
            std::string("/vsicurl/") + (pszFilename + GetFSPrefix().size());
×
1668
        return VSIStatExL(osVSICURLFilename.c_str(), pStatBuf, nFlags);
×
1669
    }
1670

1671
    memset(pStatBuf, 0, sizeof(VSIStatBufL));
9✔
1672

1673
    VSICurlStreamingHandle *poHandle =
1674
        CreateFileHandle(pszFilename + GetFSPrefix().size());
9✔
1675
    if (poHandle == nullptr)
9✔
1676
    {
1677
        return -1;
×
1678
    }
1679
    if (poHandle->IsKnownFileSize() ||
17✔
1680
        ((nFlags & VSI_STAT_SIZE_FLAG) && !poHandle->IsDirectory() &&
8✔
1681
         CPLTestBool(CPLGetConfigOption("CPL_VSIL_CURL_SLOW_GET_SIZE", "YES"))))
8✔
1682
    {
1683
        pStatBuf->st_size = poHandle->GetFileSize();
9✔
1684
    }
1685

1686
    int nRet = (poHandle->Exists(pszFilename, nullptr)) ? 0 : -1;
9✔
1687
    pStatBuf->st_mode = poHandle->IsDirectory() ? S_IFDIR : S_IFREG;
9✔
1688

1689
    delete poHandle;
9✔
1690
    return nRet;
9✔
1691
}
1692

1693
/************************************************************************/
1694
/*                          GetActualURL()                              */
1695
/************************************************************************/
1696

1697
const char *VSICurlStreamingFSHandler::GetActualURL(const char *pszFilename)
3✔
1698
{
1699
    if (!STARTS_WITH_CI(pszFilename, GetFSPrefix()))
3✔
1700
        return pszFilename;
×
1701
    auto poHandle = std::unique_ptr<VSICurlStreamingHandle>(
1702
        CreateFileHandle(pszFilename + GetFSPrefix().size()));
6✔
1703
    if (poHandle == nullptr)
3✔
1704
        return pszFilename;
×
1705
    return CPLSPrintf("%s", poHandle->GetURL());
3✔
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
    IVSIS3LikeStreamingFSHandler() = default;
6,140✔
1721

1722
    char **ReadDirEx(const char *pszDirname, int nMaxFiles) override
1✔
1723
    {
1724
        if (STARTS_WITH(pszDirname, GetFSPrefix()))
1✔
1725
        {
1726
            return VSIReadDirEx(
1✔
1727
                (GetNonStreamingPrefix() + (pszDirname + GetFSPrefix().size()))
2✔
1728
                    .c_str(),
1729
                nMaxFiles);
1✔
1730
        }
1731
        return nullptr;
×
1732
    }
1733

1734
    const char *GetOptions() override
5✔
1735
    {
1736
        return VSIGetFileSystemOptions(GetNonStreamingPrefix().c_str());
5✔
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
    CPLString GetFSPrefix() override
166✔
1750
    {
1751
        return "/vsis3_streaming/";
166✔
1752
    }
1753

1754
    std::string GetNonStreamingPrefix() const override
2✔
1755
    {
1756
        return "/vsis3/";
2✔
1757
    }
1758

1759
    VSICurlStreamingHandle *CreateFileHandle(const char *pszURL) override;
1760

1761
  public:
1762
    VSIS3StreamingFSHandler() = default;
1,228✔
1763
    ~VSIS3StreamingFSHandler() override = default;
1,704✔
1764

1765
    void ClearCache() override
246✔
1766
    {
1767
        IVSIS3LikeStreamingFSHandler::ClearCache();
246✔
1768
        VSIS3UpdateParams::ClearCache();
246✔
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
    bool StopReceivingBytesOnError() override
71✔
1788
    {
1789
        return false;
71✔
1790
    }
1791

1792
    bool CanRestartOnError(const char *pszErrorMsg, const char *pszHeaders,
1793
                           bool bSetError) override;
1794

1795
    bool InterpretRedirect() override
686✔
1796
    {
1797
        return false;
686✔
1798
    }
1799

1800
  public:
1801
    VSIS3LikeStreamingHandle(IVSIS3LikeStreamingFSHandler *poFS,
1802
                             IVSIS3LikeHandleHelper *poS3HandleHelper);
1803
    ~VSIS3LikeStreamingHandle() override;
1804
};
1805

1806
/************************************************************************/
1807
/*                          CreateFileHandle()                          */
1808
/************************************************************************/
1809

1810
VSICurlStreamingHandle *
1811
VSIS3StreamingFSHandler::CreateFileHandle(const char *pszURL)
32✔
1812
{
1813
    VSIS3HandleHelper *poS3HandleHelper =
1814
        VSIS3HandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str(), false);
32✔
1815
    if (poS3HandleHelper)
32✔
1816
    {
1817
        return new VSIS3LikeStreamingHandle(this, poS3HandleHelper);
31✔
1818
    }
1819
    return nullptr;
1✔
1820
}
1821

1822
/************************************************************************/
1823
/*                     VSIS3LikeStreamingHandle()                       */
1824
/************************************************************************/
1825

1826
VSIS3LikeStreamingHandle::VSIS3LikeStreamingHandle(
55✔
1827
    IVSIS3LikeStreamingFSHandler *poFS,
1828
    IVSIS3LikeHandleHelper *poS3HandleHelper)
55✔
1829
    : VSICurlStreamingHandle(poFS, poS3HandleHelper->GetURL().c_str()),
110✔
1830
      m_poS3HandleHelper(poS3HandleHelper)
55✔
1831
{
1832
}
55✔
1833

1834
/************************************************************************/
1835
/*                     ~VSIS3LikeStreamingHandle()                      */
1836
/************************************************************************/
1837

1838
VSIS3LikeStreamingHandle::~VSIS3LikeStreamingHandle()
110✔
1839
{
1840
    delete m_poS3HandleHelper;
55✔
1841
}
110✔
1842

1843
/************************************************************************/
1844
/*                           GetCurlHeaders()                           */
1845
/************************************************************************/
1846

1847
struct curl_slist *VSIS3LikeStreamingHandle::GetCurlHeaders(
55✔
1848
    const CPLString &osVerb, const struct curl_slist *psExistingHeaders)
1849
{
1850
    return m_poS3HandleHelper->GetCurlHeaders(osVerb, psExistingHeaders);
55✔
1851
}
1852

1853
/************************************************************************/
1854
/*                          CanRestartOnError()                         */
1855
/************************************************************************/
1856

1857
bool VSIS3LikeStreamingHandle::CanRestartOnError(const char *pszErrorMsg,
24✔
1858
                                                 const char *pszHeaders,
1859
                                                 bool bSetError)
1860
{
1861
    if (m_poS3HandleHelper->CanRestartOnError(pszErrorMsg, pszHeaders,
24✔
1862
                                              bSetError))
24✔
1863
    {
1864
        SetURL(m_poS3HandleHelper->GetURL().c_str());
4✔
1865
        return true;
4✔
1866
    }
1867
    return false;
20✔
1868
}
1869

1870
/************************************************************************/
1871
/*                       VSIGSStreamingFSHandler                        */
1872
/************************************************************************/
1873

1874
class VSIGSStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
1875
{
1876
  protected:
1877
    CPLString GetFSPrefix() override
49✔
1878
    {
1879
        return "/vsigs_streaming/";
49✔
1880
    }
1881

1882
    std::string GetNonStreamingPrefix() const override
1✔
1883
    {
1884
        return "/vsigs/";
1✔
1885
    }
1886

1887
    VSICurlStreamingHandle *CreateFileHandle(const char *pszURL) override;
1888

1889
  public:
1890
    VSIGSStreamingFSHandler()
1,228✔
1891
    {
1,228✔
1892
    }
1,228✔
1893

1894
    ~VSIGSStreamingFSHandler() override
1,704✔
1895
    {
852✔
1896
    }
1,704✔
1897
};
1898

1899
/************************************************************************/
1900
/*                          CreateFileHandle()                          */
1901
/************************************************************************/
1902

1903
VSICurlStreamingHandle *
1904
VSIGSStreamingFSHandler::CreateFileHandle(const char *pszURL)
10✔
1905
{
1906
    VSIGSHandleHelper *poGCHandleHelper =
1907
        VSIGSHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str());
10✔
1908
    if (poGCHandleHelper)
10✔
1909
    {
1910
        return new VSIS3LikeStreamingHandle(this, poGCHandleHelper);
9✔
1911
    }
1912
    return nullptr;
1✔
1913
}
1914

1915
/************************************************************************/
1916
/*                      VSIAzureStreamingFSHandler                      */
1917
/************************************************************************/
1918

1919
class VSIAzureStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
1920
{
1921
  protected:
1922
    CPLString GetFSPrefix() override
29✔
1923
    {
1924
        return "/vsiaz_streaming/";
29✔
1925
    }
1926

1927
    std::string GetNonStreamingPrefix() const override
1✔
1928
    {
1929
        return "/vsiaz/";
1✔
1930
    }
1931

1932
    VSICurlStreamingHandle *CreateFileHandle(const char *pszURL) override;
1933

1934
  public:
1935
    VSIAzureStreamingFSHandler()
1,228✔
1936
    {
1,228✔
1937
    }
1,228✔
1938

1939
    ~VSIAzureStreamingFSHandler() override
1,704✔
1940
    {
852✔
1941
    }
1,704✔
1942
};
1943

1944
/************************************************************************/
1945
/*                          CreateFileHandle()                          */
1946
/************************************************************************/
1947

1948
VSICurlStreamingHandle *
1949
VSIAzureStreamingFSHandler::CreateFileHandle(const char *pszURL)
6✔
1950
{
1951
    VSIAzureBlobHandleHelper *poHandleHelper =
1952
        VSIAzureBlobHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str());
6✔
1953
    if (poHandleHelper)
6✔
1954
    {
1955
        return new VSIS3LikeStreamingHandle(this, poHandleHelper);
5✔
1956
    }
1957
    return nullptr;
1✔
1958
}
1959

1960
/************************************************************************/
1961
/*                       VSIOSSStreamingFSHandler                        */
1962
/************************************************************************/
1963

1964
class VSIOSSStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
1965
{
1966
    CPL_DISALLOW_COPY_ASSIGN(VSIOSSStreamingFSHandler)
1967

1968
  protected:
1969
    CPLString GetFSPrefix() override
44✔
1970
    {
1971
        return "/vsioss_streaming/";
44✔
1972
    }
1973

1974
    std::string GetNonStreamingPrefix() const override
1✔
1975
    {
1976
        return "/vsioss/";
1✔
1977
    }
1978

1979
    VSICurlStreamingHandle *CreateFileHandle(const char *pszURL) override;
1980

1981
  public:
1982
    VSIOSSStreamingFSHandler() = default;
1,228✔
1983
    ~VSIOSSStreamingFSHandler() override = default;
1,704✔
1984

1985
    void ClearCache() override
246✔
1986
    {
1987
        IVSIS3LikeStreamingFSHandler::ClearCache();
246✔
1988
        VSIOSSUpdateParams::ClearCache();
246✔
1989
    }
246✔
1990
};
1991

1992
/************************************************************************/
1993
/*                          CreateFileHandle()                          */
1994
/************************************************************************/
1995

1996
VSICurlStreamingHandle *
1997
VSIOSSStreamingFSHandler::CreateFileHandle(const char *pszURL)
9✔
1998
{
1999
    VSIOSSHandleHelper *poOSSHandleHelper =
2000
        VSIOSSHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str(), false);
9✔
2001
    if (poOSSHandleHelper)
9✔
2002
    {
2003
        return new VSIS3LikeStreamingHandle(this, poOSSHandleHelper);
8✔
2004
    }
2005
    return nullptr;
1✔
2006
}
2007

2008
/************************************************************************/
2009
/*                      VSISwiftStreamingFSHandler                      */
2010
/************************************************************************/
2011

2012
class VSISwiftStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
2013
{
2014
  protected:
2015
    CPLString GetFSPrefix() override
14✔
2016
    {
2017
        return "/vsiswift_streaming/";
14✔
2018
    }
2019

2020
    std::string GetNonStreamingPrefix() const override
1✔
2021
    {
2022
        return "/vsiswift/";
1✔
2023
    }
2024

2025
    VSICurlStreamingHandle *CreateFileHandle(const char *pszURL) override;
2026

2027
  public:
2028
    VSISwiftStreamingFSHandler()
1,228✔
2029
    {
1,228✔
2030
    }
1,228✔
2031

2032
    ~VSISwiftStreamingFSHandler() override
1,704✔
2033
    {
852✔
2034
    }
1,704✔
2035
};
2036

2037
/************************************************************************/
2038
/*                          CreateFileHandle()                          */
2039
/************************************************************************/
2040

2041
VSICurlStreamingHandle *
2042
VSISwiftStreamingFSHandler::CreateFileHandle(const char *pszURL)
3✔
2043
{
2044
    VSISwiftHandleHelper *poHandleHelper =
2045
        VSISwiftHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str());
3✔
2046
    if (poHandleHelper)
3✔
2047
    {
2048
        return new VSIS3LikeStreamingHandle(this, poHandleHelper);
2✔
2049
    }
2050
    return nullptr;
1✔
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
void VSIInstallCurlStreamingFileHandler(void)
1,228✔
2072
{
2073
    VSIFileManager::InstallHandler("/vsicurl_streaming/",
1,228✔
2074
                                   new cpl::VSICurlStreamingFSHandler);
1,228✔
2075
}
1,228✔
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
void VSIInstallS3StreamingFileHandler(void)
1,228✔
2092
{
2093
    VSIFileManager::InstallHandler("/vsis3_streaming/",
1,228✔
2094
                                   new cpl::VSIS3StreamingFSHandler);
1,228✔
2095
}
1,228✔
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
void VSIInstallGSStreamingFileHandler(void)
1,228✔
2113
{
2114
    VSIFileManager::InstallHandler("/vsigs_streaming/",
1,228✔
2115
                                   new cpl::VSIGSStreamingFSHandler);
1,228✔
2116
}
1,228✔
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
void VSIInstallAzureStreamingFileHandler(void)
1,228✔
2134
{
2135
    VSIFileManager::InstallHandler("/vsiaz_streaming/",
1,228✔
2136
                                   new cpl::VSIAzureStreamingFSHandler);
1,228✔
2137
}
1,228✔
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
void VSIInstallOSSStreamingFileHandler(void)
1,228✔
2155
{
2156
    VSIFileManager::InstallHandler("/vsioss_streaming/",
1,228✔
2157
                                   new cpl::VSIOSSStreamingFSHandler);
1,228✔
2158
}
1,228✔
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
void VSIInstallSwiftStreamingFileHandler(void)
1,228✔
2176
{
2177
    VSIFileManager::InstallHandler("/vsiswift_streaming/",
1,228✔
2178
                                   new cpl::VSISwiftStreamingFSHandler);
1,228✔
2179
}
1,228✔
2180

2181
//! @cond Doxygen_Suppress
2182

2183
/************************************************************************/
2184
/*                      VSICurlStreamingClearCache()                    */
2185
/************************************************************************/
2186

2187
void VSICurlStreamingClearCache(void)
246✔
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
    char **papszPrefix = VSIFileManager::GetPrefixes();
246✔
2193
    for (size_t i = 0; papszPrefix && papszPrefix[i]; ++i)
6,888✔
2194
    {
2195
        auto poFSHandler = dynamic_cast<cpl::VSICurlStreamingFSHandler *>(
×
2196
            VSIFileManager::GetHandler(papszPrefix[i]));
6,642✔
2197

2198
        if (poFSHandler)
6,642✔
2199
            poFSHandler->ClearCache();
1,476✔
2200
    }
2201
    CSLDestroy(papszPrefix);
246✔
2202
}
246✔
2203

2204
//! @endcond
2205

2206
#undef ENABLE_DEBUG
2207

2208
#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