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