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