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 202 : size_t GetCapacity() const
104 : {
105 202 : return nCapacity;
106 : }
107 :
108 723 : size_t GetSize() const
109 : {
110 723 : 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 200 : void RingBuffer::Write(void *pBuffer, size_t nSize)
136 : {
137 200 : CPLAssert(nLength + nSize <= nCapacity);
138 :
139 200 : const size_t nEndOffset = (nOffset + nLength) % nCapacity;
140 200 : const size_t nSz = std::min(nSize, nCapacity - nEndOffset);
141 200 : memcpy(pabyBuffer + nEndOffset, pBuffer, nSz);
142 200 : if (nSz < nSize)
143 0 : memcpy(pabyBuffer, static_cast<GByte *>(pBuffer) + nSz, nSize - nSz);
144 :
145 200 : nLength += nSize;
146 200 : }
147 :
148 215 : void RingBuffer::Read(void *pBuffer, size_t nSize)
149 : {
150 215 : CPLAssert(nSize <= nLength);
151 :
152 215 : if (pBuffer)
153 : {
154 215 : const size_t nSz = std::min(nSize, nCapacity - nOffset);
155 215 : memcpy(pBuffer, pabyBuffer + nOffset, nSz);
156 215 : if (nSz < nSize)
157 0 : memcpy(static_cast<GByte *>(pBuffer) + nSz, pabyBuffer,
158 : nSize - nSz);
159 : }
160 :
161 215 : nOffset = (nOffset + nSize) % nCapacity;
162 215 : nLength -= nSize;
163 215 : }
164 :
165 : /************************************************************************/
166 :
167 : namespace
168 : {
169 :
170 : typedef struct
171 : {
172 : char *pBuffer;
173 : size_t nSize;
174 : int bIsHTTP;
175 : int bIsInHeader;
176 : int nHTTPCode;
177 : int bDownloadHeaderOnly;
178 : } WriteFuncStructStreaming;
179 :
180 : } // namespace
181 :
182 : namespace cpl
183 : {
184 :
185 : /************************************************************************/
186 : /* VSICurlStreamingFSHandler */
187 : /************************************************************************/
188 :
189 : class VSICurlStreamingHandle;
190 :
191 : class VSICurlStreamingFSHandler : public VSIFilesystemHandler
192 : {
193 : CPL_DISALLOW_COPY_ASSIGN(VSICurlStreamingFSHandler)
194 :
195 : // LRU cache that just keeps in memory if this file system handler is
196 : // spposed to know the file properties of a file. The actual cache is a
197 : // shared one among all network file systems.
198 : // The aim of that design is that invalidating /vsis3/foo results in
199 : // /vsis3_streaming/foo to be invalidated as well.
200 : lru11::Cache<std::string, bool> oCacheFileProp;
201 :
202 : protected:
203 : CPLMutex *hMutex = nullptr;
204 :
205 : virtual VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
206 : const char *pszURL);
207 :
208 10 : virtual std::string GetNonStreamingPrefix() const
209 : {
210 10 : return "/vsicurl/";
211 : }
212 :
213 : public:
214 : VSICurlStreamingFSHandler();
215 : virtual ~VSICurlStreamingFSHandler();
216 :
217 : virtual VSIVirtualHandle *Open(const char *pszFilename,
218 : const char *pszAccess, bool bSetError,
219 : CSLConstList /* papszOptions */) override;
220 : virtual int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
221 : int nFlags) override;
222 :
223 64 : virtual CPLString GetFSPrefix() const
224 : {
225 64 : return "/vsicurl_streaming/";
226 : }
227 :
228 : std::string
229 : GetNonStreamingFilename(const std::string &osFilename) const override;
230 :
231 : const char *GetActualURL(const char *pszFilename) override;
232 :
233 1 : const char *GetOptions() override
234 : {
235 1 : return VSIGetFileSystemOptions("/vsicurl/");
236 : }
237 :
238 : void AcquireMutex();
239 : void ReleaseMutex();
240 :
241 : bool GetCachedFileProp(const char *pszURL, FileProp &oFileProp);
242 : void SetCachedFileProp(const char *pszURL, FileProp &oFileProp);
243 :
244 : virtual void ClearCache();
245 : };
246 :
247 : /************************************************************************/
248 : /* VSICurlStreamingHandle */
249 : /************************************************************************/
250 :
251 : class VSICurlStreamingHandle : public VSIVirtualHandle
252 : {
253 : CPL_DISALLOW_COPY_ASSIGN(VSICurlStreamingHandle)
254 :
255 : protected:
256 : VSICurlStreamingFSHandler *m_poFS = nullptr;
257 : CPLStringList m_aosHTTPOptions{};
258 : const CPLHTTPRetryParameters m_oRetryParameters;
259 :
260 : private:
261 : char *m_pszURL = nullptr;
262 :
263 : #ifdef notdef
264 : unsigned int nRecomputedChecksumOfFirst1024Bytes = 0;
265 : #endif
266 : vsi_l_offset curOffset = 0;
267 : vsi_l_offset fileSize = 0;
268 : bool bHasComputedFileSize = false;
269 : ExistStatus eExists = EXIST_UNKNOWN;
270 : bool bIsDirectory = false;
271 :
272 : bool bCanTrustCandidateFileSize = true;
273 : bool bHasCandidateFileSize = false;
274 : vsi_l_offset nCandidateFileSize = 0;
275 :
276 : bool bEOF = false;
277 : bool m_bError = false;
278 :
279 : size_t nCachedSize = 0;
280 : GByte *pCachedData = nullptr;
281 :
282 : volatile int bDownloadInProgress = FALSE;
283 : volatile int bDownloadStopped = FALSE;
284 : volatile int bAskDownloadEnd = FALSE;
285 : vsi_l_offset nRingBufferFileOffset = 0;
286 : CPLJoinableThread *hThread = nullptr;
287 : CPLMutex *hRingBufferMutex = nullptr;
288 : CPLCond *hCondProducer = nullptr;
289 : CPLCond *hCondConsumer = nullptr;
290 : RingBuffer oRingBuffer{};
291 : void StartDownload();
292 : void StopDownload();
293 : void PutRingBufferInCache();
294 :
295 : GByte *pabyHeaderData = nullptr;
296 : size_t nHeaderSize = 0;
297 : vsi_l_offset nBodySize = 0;
298 : int nHTTPCode = 0;
299 : char m_szCurlErrBuf[CURL_ERROR_SIZE + 1];
300 : bool m_bErrorOccurredInThread = false;
301 :
302 : void AcquireMutex();
303 : void ReleaseMutex();
304 :
305 : void AddRegion(vsi_l_offset nFileOffsetStart, size_t nSize, GByte *pData);
306 :
307 : protected:
308 : virtual struct curl_slist *
309 14 : GetCurlHeaders(const CPLString &,
310 : const struct curl_slist * /* psExistingHeaders */)
311 : {
312 14 : return nullptr;
313 : }
314 :
315 9 : virtual bool StopReceivingBytesOnError()
316 : {
317 9 : 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 1215 : void VSICurlStreamingHandle::AcquireMutex()
432 : {
433 1215 : CPLAcquireMutex(hRingBufferMutex, 1000.0);
434 1215 : }
435 :
436 : /************************************************************************/
437 : /* ReleaseMutex() */
438 : /************************************************************************/
439 :
440 1280 : void VSICurlStreamingHandle::ReleaseMutex()
441 : {
442 1280 : CPLReleaseMutex(hRingBufferMutex);
443 1280 : }
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 18 : VSICURLStreamingInitWriteFuncStructStreaming(WriteFuncStructStreaming *psStruct)
488 : {
489 18 : psStruct->pBuffer = nullptr;
490 18 : psStruct->nSize = 0;
491 18 : psStruct->bIsHTTP = FALSE;
492 18 : psStruct->bIsInHeader = TRUE;
493 18 : psStruct->nHTTPCode = 0;
494 18 : psStruct->bDownloadHeaderOnly = FALSE;
495 18 : }
496 :
497 : /************************************************************************/
498 : /* VSICurlStreamingHandleWriteFuncForHeader() */
499 : /************************************************************************/
500 :
501 50 : static size_t VSICurlStreamingHandleWriteFuncForHeader(void *buffer,
502 : size_t count,
503 : size_t nmemb, void *req)
504 : {
505 50 : WriteFuncStructStreaming *psStruct =
506 : static_cast<WriteFuncStructStreaming *>(req);
507 50 : const size_t nSize = count * nmemb;
508 :
509 : char *pNewBuffer = static_cast<char *>(
510 50 : VSIRealloc(psStruct->pBuffer, psStruct->nSize + nSize + 1));
511 50 : if (pNewBuffer)
512 : {
513 50 : psStruct->pBuffer = pNewBuffer;
514 50 : memcpy(psStruct->pBuffer + psStruct->nSize, buffer, nSize);
515 50 : psStruct->pBuffer[psStruct->nSize + nSize] = '\0';
516 50 : 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 50 : psStruct->nSize += nSize;
545 50 : 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 2 : const vsi_l_offset nRet = fileSize;
566 2 : ReleaseMutex();
567 2 : return nRet;
568 : }
569 9 : ReleaseMutex();
570 :
571 9 : CURL *hLocalHandle = curl_easy_init();
572 :
573 : struct curl_slist *headers =
574 9 : VSICurlSetOptions(hLocalHandle, m_pszURL, m_aosHTTPOptions.List());
575 :
576 9 : 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 18 : CPLString osVerb;
582 9 : 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 9 : unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_NOBODY, 1);
596 9 : unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HTTPGET, 0);
597 9 : unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HEADER, 1);
598 9 : osVerb = "HEAD";
599 : }
600 :
601 9 : headers = VSICurlMergeHeaders(headers, GetCurlHeaders(osVerb, headers));
602 9 : 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 9 : 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 9 : VSICURLStreamingInitWriteFuncStructStreaming(&sWriteFuncData);
611 9 : unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_WRITEDATA,
612 : &sWriteFuncData);
613 9 : unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_WRITEFUNCTION,
614 : VSICurlStreamingHandleWriteFuncForHeader);
615 :
616 9 : char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
617 9 : unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf);
618 :
619 9 : void *old_handler = CPLHTTPIgnoreSigPipe();
620 9 : curl_easy_perform(hLocalHandle);
621 9 : CPLHTTPRestoreSigPipeHandler(old_handler);
622 9 : if (headers != nullptr)
623 9 : curl_slist_free_all(headers);
624 :
625 9 : AcquireMutex();
626 :
627 9 : eExists = EXIST_UNKNOWN;
628 9 : bHasComputedFileSize = true;
629 :
630 9 : 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 9 : double dfSize = 0;
648 9 : if (eExists != EXIST_YES)
649 : {
650 9 : curl_off_t nSizeTmp = 0;
651 9 : const CURLcode code = curl_easy_getinfo(
652 : hLocalHandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &nSizeTmp);
653 9 : CPL_IGNORE_RET_VAL(dfSize);
654 9 : dfSize = static_cast<double>(nSizeTmp);
655 9 : if (code == 0)
656 : {
657 9 : eExists = EXIST_YES;
658 9 : if (dfSize < 0)
659 0 : fileSize = 0;
660 : else
661 9 : 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 9 : long response_code = 0;
672 9 : curl_easy_getinfo(hLocalHandle, CURLINFO_HTTP_CODE, &response_code);
673 9 : 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 9 : char *pszEffectiveURL = nullptr;
682 9 : curl_easy_getinfo(hLocalHandle, CURLINFO_EFFECTIVE_URL,
683 : &pszEffectiveURL);
684 9 : if (pszEffectiveURL != nullptr &&
685 9 : strncmp(m_pszURL, pszEffectiveURL, strlen(m_pszURL)) == 0 &&
686 9 : 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 9 : CPLFree(sWriteFuncData.pBuffer);
700 9 : CPLFree(sWriteFuncHeaderData.pBuffer);
701 :
702 9 : FileProp cachedFileProp;
703 9 : m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
704 9 : cachedFileProp.bHasComputedFileSize = true;
705 9 : cachedFileProp.fileSize = fileSize;
706 9 : cachedFileProp.eExists = eExists;
707 9 : cachedFileProp.bIsDirectory = bIsDirectory;
708 9 : if (cachedFileProp.nMode == 0)
709 9 : cachedFileProp.nMode = bIsDirectory ? S_IFDIR : S_IFREG;
710 9 : m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
711 :
712 9 : const vsi_l_offset nRet = fileSize;
713 9 : ReleaseMutex();
714 :
715 9 : curl_easy_cleanup(hLocalHandle);
716 :
717 9 : 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 199 : size_t VSICurlStreamingHandle::ReceivedBytes(GByte *buffer, size_t count,
779 : size_t nmemb)
780 : {
781 199 : size_t nSize = count * nmemb;
782 199 : nBodySize += nSize;
783 :
784 : if (ENABLE_DEBUG)
785 : CPLDebug("VSICURL", "Receiving %d bytes...", static_cast<int>(nSize));
786 :
787 199 : if (bHasCandidateFileSize && bCanTrustCandidateFileSize &&
788 185 : !bHasComputedFileSize)
789 : {
790 370 : FileProp cachedFileProp;
791 185 : m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
792 185 : cachedFileProp.fileSize = fileSize = nCandidateFileSize;
793 185 : bHasCandidateFileSize = TRUE;
794 185 : cachedFileProp.bHasComputedFileSize = bHasComputedFileSize;
795 185 : m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
796 : if (ENABLE_DEBUG)
797 : CPLDebug("VSICURL", "File size = " CPL_FRMT_GUIB, fileSize);
798 : }
799 :
800 199 : AcquireMutex();
801 199 : 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 199 : else if (eExists == EXIST_NO && StopReceivingBytesOnError())
809 : {
810 0 : ReleaseMutex();
811 0 : return 0;
812 : }
813 :
814 : while (true)
815 : {
816 200 : const size_t nFree = oRingBuffer.GetCapacity() - oRingBuffer.GetSize();
817 200 : if (nSize <= nFree)
818 : {
819 199 : oRingBuffer.Write(buffer, nSize);
820 :
821 : // Signal to the consumer that we have added bytes to the buffer.
822 199 : CPLCondSignal(hCondProducer);
823 :
824 199 : if (bAskDownloadEnd)
825 : {
826 : if (ENABLE_DEBUG)
827 : CPLDebug("VSICURL", "Download interruption asked");
828 :
829 0 : ReleaseMutex();
830 0 : return 0;
831 : }
832 199 : 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 199 : ReleaseMutex();
865 :
866 199 : return nmemb;
867 : }
868 :
869 : /************************************************************************/
870 : /* VSICurlStreamingHandleReceivedBytes() */
871 : /************************************************************************/
872 :
873 199 : static size_t VSICurlStreamingHandleReceivedBytes(void *buffer, size_t count,
874 : size_t nmemb, void *req)
875 : {
876 199 : return static_cast<VSICurlStreamingHandle *>(req)->ReceivedBytes(
877 199 : 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 = VSICurlMergeHeaders(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 47 : 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 100 : FileProp cachedFileProp;
1072 50 : m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
1073 50 : fileSize = nBodySize;
1074 50 : cachedFileProp.fileSize = fileSize;
1075 50 : bHasComputedFileSize = true;
1076 50 : cachedFileProp.bHasComputedFileSize = bHasComputedFileSize;
1077 50 : 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 97 : void VSICurlStreamingHandle::StartDownload()
1102 : {
1103 97 : if (bDownloadInProgress || bDownloadStopped)
1104 37 : 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 60 : while (bDownloadInProgress)
1132 0 : 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 42 : void VSICurlStreamingHandle::PutRingBufferInCache()
1154 : {
1155 42 : if (nRingBufferFileOffset >= BKGND_BUFFER_SIZE)
1156 0 : return;
1157 :
1158 42 : AcquireMutex();
1159 :
1160 : // Cache any remaining bytes available in the ring buffer.
1161 42 : size_t nBufSize = oRingBuffer.GetSize();
1162 42 : if (nBufSize > 0)
1163 : {
1164 28 : if (nRingBufferFileOffset + nBufSize > BKGND_BUFFER_SIZE)
1165 1 : nBufSize =
1166 1 : static_cast<size_t>(BKGND_BUFFER_SIZE - nRingBufferFileOffset);
1167 28 : GByte *pabyTmp = static_cast<GByte *>(CPLMalloc(nBufSize));
1168 28 : oRingBuffer.Read(pabyTmp, nBufSize);
1169 :
1170 : // Signal to the producer that we have ingested some bytes.
1171 28 : CPLCondSignal(hCondConsumer);
1172 :
1173 28 : AddRegion(nRingBufferFileOffset, nBufSize, pabyTmp);
1174 28 : nRingBufferFileOffset += nBufSize;
1175 28 : CPLFree(pabyTmp);
1176 : }
1177 :
1178 42 : 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 42 : 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 38 : curOffset <= nCachedSize && curOffset + nRemaining > fileSizeLocal &&
1240 31 : fileSize == nCachedSize)
1241 : {
1242 22 : 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 22 : memcpy(pabyBuffer, pCachedData + curOffset, nSz);
1248 22 : pabyBuffer += nSz;
1249 22 : curOffset += nSz;
1250 22 : nRemaining -= nSz;
1251 22 : 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 4 : if (curOffset < nRingBufferFileOffset)
1261 0 : StopDownload();
1262 :
1263 4 : StartDownload();
1264 :
1265 4 : const vsi_l_offset SKIP_BUFFER_SIZE = 32768;
1266 4 : GByte *pabyTmp = static_cast<GByte *>(CPLMalloc(SKIP_BUFFER_SIZE));
1267 :
1268 4 : CPLAssert(curOffset >= nRingBufferFileOffset);
1269 4 : vsi_l_offset nBytesToSkip = curOffset - nRingBufferFileOffset;
1270 12 : while (nBytesToSkip > 0)
1271 : {
1272 10 : vsi_l_offset nBytesToRead = nBytesToSkip;
1273 :
1274 10 : AcquireMutex();
1275 10 : if (nBytesToRead > oRingBuffer.GetSize())
1276 8 : nBytesToRead = oRingBuffer.GetSize();
1277 10 : if (nBytesToRead > SKIP_BUFFER_SIZE)
1278 0 : nBytesToRead = SKIP_BUFFER_SIZE;
1279 10 : oRingBuffer.Read(pabyTmp, static_cast<size_t>(nBytesToRead));
1280 :
1281 : // Signal to the producer that we have ingested some bytes.
1282 10 : CPLCondSignal(hCondConsumer);
1283 10 : ReleaseMutex();
1284 :
1285 10 : if (nBytesToRead)
1286 4 : AddRegion(nRingBufferFileOffset,
1287 : static_cast<size_t>(nBytesToRead), pabyTmp);
1288 :
1289 10 : nBytesToSkip -= nBytesToRead;
1290 10 : nRingBufferFileOffset += nBytesToRead;
1291 :
1292 10 : if (nBytesToRead == 0 && nBytesToSkip != 0)
1293 : {
1294 : if (ENABLE_DEBUG)
1295 : CPLDebug("VSICURL",
1296 : "Waiting for writer to produce some bytes...");
1297 :
1298 6 : AcquireMutex();
1299 10 : while (oRingBuffer.GetSize() == 0 && bDownloadInProgress)
1300 4 : CPLCondWait(hCondProducer, hRingBufferMutex);
1301 6 : const int bBufferEmpty = (oRingBuffer.GetSize() == 0);
1302 6 : bErrorOccurred = m_bErrorOccurredInThread;
1303 6 : ReleaseMutex();
1304 :
1305 6 : if (bBufferEmpty && !bDownloadInProgress)
1306 2 : break;
1307 : }
1308 : }
1309 :
1310 4 : CPLFree(pabyTmp);
1311 :
1312 4 : 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 268 : while (!bEOF && nRemaining > 0 && !bErrorOccurred)
1327 : {
1328 177 : AcquireMutex();
1329 177 : size_t nToRead = oRingBuffer.GetSize();
1330 177 : if (nToRead > nRemaining)
1331 52 : nToRead = nRemaining;
1332 177 : oRingBuffer.Read(pabyBuffer, nToRead);
1333 :
1334 : // Signal to the producer that we have ingested some bytes.
1335 177 : CPLCondSignal(hCondConsumer);
1336 177 : ReleaseMutex();
1337 :
1338 177 : if (nToRead)
1339 84 : AddRegion(curOffset, nToRead, pabyBuffer);
1340 :
1341 177 : nRemaining -= nToRead;
1342 177 : pabyBuffer += nToRead;
1343 177 : curOffset += nToRead;
1344 177 : nRingBufferFileOffset += nToRead;
1345 :
1346 177 : if (nToRead == 0 && nRemaining != 0)
1347 : {
1348 : if (ENABLE_DEBUG)
1349 : CPLDebug("VSICURL",
1350 : "Waiting for writer to produce some bytes...");
1351 :
1352 93 : AcquireMutex();
1353 175 : while (oRingBuffer.GetSize() == 0 && bDownloadInProgress)
1354 82 : CPLCondWait(hCondProducer, hRingBufferMutex);
1355 93 : const bool bBufferEmpty = oRingBuffer.GetSize() == 0;
1356 93 : bErrorOccurred = m_bErrorOccurredInThread;
1357 93 : ReleaseMutex();
1358 :
1359 93 : if (bBufferEmpty && !bDownloadInProgress)
1360 37 : 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 116 : void VSICurlStreamingHandle::AddRegion(vsi_l_offset nFileOffsetStart,
1451 : size_t nSize, GByte *pData)
1452 : {
1453 116 : if (nFileOffsetStart >= BKGND_BUFFER_SIZE)
1454 3 : return;
1455 :
1456 113 : if (pCachedData == nullptr)
1457 50 : pCachedData = static_cast<GByte *>(CPLMalloc(BKGND_BUFFER_SIZE));
1458 :
1459 113 : if (nFileOffsetStart <= nCachedSize &&
1460 113 : 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 9996 : VSICurlStreamingFSHandler::VSICurlStreamingFSHandler()
1535 9996 : : oCacheFileProp{100 * 1024}
1536 : {
1537 9996 : hMutex = CPLCreateMutex();
1538 9996 : CPLReleaseMutex(hMutex);
1539 9996 : }
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 8688 : void VSICurlStreamingFSHandler::ClearCache()
1558 : {
1559 17376 : 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 8688 : oCacheFileProp.cwalk(lambda);
1565 8688 : oCacheFileProp.clear();
1566 : }
1567 8688 : }
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 59 : VSIVirtualHandle *VSICurlStreamingFSHandler::Open(const char *pszFilename,
1603 : const char *pszAccess,
1604 : bool /* bSetError */,
1605 : CSLConstList papszOptions)
1606 : {
1607 59 : if (!STARTS_WITH_CI(pszFilename, GetFSPrefix()))
1608 2 : return nullptr;
1609 :
1610 57 : if (strchr(pszAccess, 'w') != nullptr || strchr(pszAccess, '+') != nullptr)
1611 : {
1612 0 : CPLError(CE_Failure, CPLE_AppDefined,
1613 : "Only read-only mode is supported for %s",
1614 0 : GetFSPrefix().c_str());
1615 0 : return nullptr;
1616 : }
1617 :
1618 : VSICurlStreamingHandle *poHandle =
1619 57 : CreateFileHandle(pszFilename, pszFilename + GetFSPrefix().size());
1620 : // If we didn't get a filelist, check that the file really exists.
1621 57 : if (poHandle == nullptr || !poHandle->Exists(pszFilename, papszOptions))
1622 : {
1623 27 : delete poHandle;
1624 27 : return nullptr;
1625 : }
1626 :
1627 30 : if (CPLTestBool(CPLGetConfigOption("VSI_CACHE", "FALSE")))
1628 0 : return VSICreateCachedFile(poHandle);
1629 :
1630 30 : return poHandle;
1631 : }
1632 :
1633 : /************************************************************************/
1634 : /* GetCachedFileProp() */
1635 : /************************************************************************/
1636 :
1637 413 : bool VSICurlStreamingFSHandler::GetCachedFileProp(const char *pszURL,
1638 : FileProp &oFileProp)
1639 : {
1640 826 : CPLMutexHolder oHolder(&hMutex);
1641 : bool inCache;
1642 413 : if (oCacheFileProp.tryGet(std::string(pszURL), inCache))
1643 : {
1644 351 : if (VSICURLGetCachedFileProp(pszURL, oFileProp))
1645 : {
1646 351 : return true;
1647 : }
1648 0 : oCacheFileProp.remove(std::string(pszURL));
1649 : }
1650 62 : return false;
1651 : }
1652 :
1653 : /************************************************************************/
1654 : /* SetCachedFileProp() */
1655 : /************************************************************************/
1656 :
1657 413 : void VSICurlStreamingFSHandler::SetCachedFileProp(const char *pszURL,
1658 : FileProp &oFileProp)
1659 : {
1660 826 : CPLMutexHolder oHolder(&hMutex);
1661 413 : oCacheFileProp.insert(std::string(pszURL), true);
1662 413 : VSICURLSetCachedFileProp(pszURL, oFileProp);
1663 413 : }
1664 :
1665 : /************************************************************************/
1666 : /* Stat() */
1667 : /************************************************************************/
1668 :
1669 12 : int VSICurlStreamingFSHandler::Stat(const char *pszFilename,
1670 : VSIStatBufL *pStatBuf, int nFlags)
1671 : {
1672 12 : if (!STARTS_WITH_CI(pszFilename, GetFSPrefix()))
1673 2 : return -1;
1674 :
1675 10 : if ((nFlags & VSI_STAT_CACHE_ONLY) != 0)
1676 : {
1677 : const std::string osVSICURLFilename =
1678 0 : std::string("/vsicurl/") + (pszFilename + GetFSPrefix().size());
1679 0 : return VSIStatExL(osVSICURLFilename.c_str(), pStatBuf, nFlags);
1680 : }
1681 :
1682 10 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
1683 :
1684 : VSICurlStreamingHandle *poHandle =
1685 10 : CreateFileHandle(pszFilename, pszFilename + GetFSPrefix().size());
1686 10 : if (poHandle == nullptr)
1687 : {
1688 0 : return -1;
1689 : }
1690 19 : if (poHandle->IsKnownFileSize() ||
1691 9 : ((nFlags & VSI_STAT_SIZE_FLAG) && !poHandle->IsDirectory() &&
1692 9 : CPLTestBool(CPLGetConfigOption("CPL_VSIL_CURL_SLOW_GET_SIZE", "YES"))))
1693 : {
1694 10 : pStatBuf->st_size = poHandle->GetFileSize();
1695 : }
1696 :
1697 10 : int nRet = (poHandle->Exists(pszFilename, nullptr)) ? 0 : -1;
1698 10 : pStatBuf->st_mode = poHandle->IsDirectory() ? S_IFDIR : S_IFREG;
1699 :
1700 10 : delete poHandle;
1701 10 : return nRet;
1702 : }
1703 :
1704 : /************************************************************************/
1705 : /* GetActualURL() */
1706 : /************************************************************************/
1707 :
1708 3 : const char *VSICurlStreamingFSHandler::GetActualURL(const char *pszFilename)
1709 : {
1710 3 : if (!STARTS_WITH_CI(pszFilename, GetFSPrefix()))
1711 0 : return pszFilename;
1712 : auto poHandle = std::unique_ptr<VSICurlStreamingHandle>(
1713 6 : CreateFileHandle(pszFilename, pszFilename + GetFSPrefix().size()));
1714 3 : if (poHandle == nullptr)
1715 0 : return pszFilename;
1716 3 : return CPLSPrintf("%s", poHandle->GetURL());
1717 : }
1718 :
1719 : /************************************************************************/
1720 : /* GetNonStreamingFilename() */
1721 : /************************************************************************/
1722 :
1723 65 : std::string VSICurlStreamingFSHandler::GetNonStreamingFilename(
1724 : const std::string &osFilename) const
1725 : {
1726 65 : if (STARTS_WITH(osFilename.c_str(), GetFSPrefix().c_str()))
1727 130 : return GetNonStreamingPrefix() +
1728 195 : osFilename.substr(GetFSPrefix().size());
1729 0 : return osFilename;
1730 : }
1731 :
1732 : /************************************************************************/
1733 : /* IVSIS3LikeStreamingFSHandler */
1734 : /************************************************************************/
1735 :
1736 : class IVSIS3LikeStreamingFSHandler : public VSICurlStreamingFSHandler
1737 : {
1738 : CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeStreamingFSHandler)
1739 :
1740 : public:
1741 8330 : IVSIS3LikeStreamingFSHandler() = default;
1742 :
1743 : char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
1744 :
1745 5 : const char *GetOptions() override
1746 : {
1747 5 : return VSIGetFileSystemOptions(GetNonStreamingPrefix().c_str());
1748 : }
1749 : };
1750 :
1751 1 : char **IVSIS3LikeStreamingFSHandler::ReadDirEx(const char *pszDirname,
1752 : int nMaxFiles)
1753 : {
1754 1 : if (STARTS_WITH(pszDirname, GetFSPrefix()))
1755 : {
1756 1 : return VSIReadDirEx(
1757 2 : (GetNonStreamingPrefix() + (pszDirname + GetFSPrefix().size()))
1758 : .c_str(),
1759 1 : nMaxFiles);
1760 : }
1761 0 : return nullptr;
1762 : }
1763 :
1764 : /************************************************************************/
1765 : /* VSIS3StreamingFSHandler */
1766 : /************************************************************************/
1767 :
1768 : class VSIS3StreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
1769 : {
1770 : CPL_DISALLOW_COPY_ASSIGN(VSIS3StreamingFSHandler)
1771 :
1772 : protected:
1773 228 : CPLString GetFSPrefix() const override
1774 : {
1775 228 : return "/vsis3_streaming/";
1776 : }
1777 :
1778 33 : std::string GetNonStreamingPrefix() const override
1779 : {
1780 33 : return "/vsis3/";
1781 : }
1782 :
1783 : VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
1784 : const char *pszURL) override;
1785 :
1786 : public:
1787 1666 : VSIS3StreamingFSHandler() = default;
1788 2242 : ~VSIS3StreamingFSHandler() override = default;
1789 :
1790 327 : void ClearCache() override
1791 : {
1792 327 : IVSIS3LikeStreamingFSHandler::ClearCache();
1793 327 : VSIS3UpdateParams::ClearCache();
1794 327 : }
1795 : };
1796 :
1797 : /************************************************************************/
1798 : /* VSIS3LikeStreamingHandle */
1799 : /************************************************************************/
1800 :
1801 : class VSIS3LikeStreamingHandle final : public VSICurlStreamingHandle
1802 : {
1803 : CPL_DISALLOW_COPY_ASSIGN(VSIS3LikeStreamingHandle)
1804 :
1805 : IVSIS3LikeHandleHelper *m_poS3HandleHelper = nullptr;
1806 :
1807 : protected:
1808 : struct curl_slist *
1809 : GetCurlHeaders(const CPLString &osVerb,
1810 : const struct curl_slist *psExistingHeaders) override;
1811 :
1812 71 : bool StopReceivingBytesOnError() override
1813 : {
1814 71 : return false;
1815 : }
1816 :
1817 : bool CanRestartOnError(const char *pszErrorMsg, const char *pszHeaders,
1818 : bool bSetError) override;
1819 :
1820 686 : bool InterpretRedirect() override
1821 : {
1822 686 : return false;
1823 : }
1824 :
1825 : public:
1826 : VSIS3LikeStreamingHandle(IVSIS3LikeStreamingFSHandler *poFS,
1827 : const char *pszFilename,
1828 : IVSIS3LikeHandleHelper *poS3HandleHelper);
1829 : ~VSIS3LikeStreamingHandle() override;
1830 : };
1831 :
1832 : /************************************************************************/
1833 : /* CreateFileHandle() */
1834 : /************************************************************************/
1835 :
1836 : VSICurlStreamingHandle *
1837 32 : VSIS3StreamingFSHandler::CreateFileHandle(const char *pszFilename,
1838 : const char *pszURL)
1839 : {
1840 : VSIS3HandleHelper *poS3HandleHelper =
1841 32 : VSIS3HandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str(), false);
1842 32 : if (poS3HandleHelper)
1843 : {
1844 : return new VSIS3LikeStreamingHandle(this, pszFilename,
1845 31 : poS3HandleHelper);
1846 : }
1847 1 : return nullptr;
1848 : }
1849 :
1850 : /************************************************************************/
1851 : /* VSIS3LikeStreamingHandle() */
1852 : /************************************************************************/
1853 :
1854 55 : VSIS3LikeStreamingHandle::VSIS3LikeStreamingHandle(
1855 : IVSIS3LikeStreamingFSHandler *poFS, const char *pszFilename,
1856 55 : IVSIS3LikeHandleHelper *poS3HandleHelper)
1857 : : VSICurlStreamingHandle(poFS, pszFilename,
1858 110 : poS3HandleHelper->GetURL().c_str()),
1859 55 : m_poS3HandleHelper(poS3HandleHelper)
1860 : {
1861 55 : }
1862 :
1863 : /************************************************************************/
1864 : /* ~VSIS3LikeStreamingHandle() */
1865 : /************************************************************************/
1866 :
1867 110 : VSIS3LikeStreamingHandle::~VSIS3LikeStreamingHandle()
1868 : {
1869 55 : delete m_poS3HandleHelper;
1870 110 : }
1871 :
1872 : /************************************************************************/
1873 : /* GetCurlHeaders() */
1874 : /************************************************************************/
1875 :
1876 55 : struct curl_slist *VSIS3LikeStreamingHandle::GetCurlHeaders(
1877 : const CPLString &osVerb, const struct curl_slist *psExistingHeaders)
1878 : {
1879 55 : return m_poS3HandleHelper->GetCurlHeaders(osVerb, psExistingHeaders);
1880 : }
1881 :
1882 : /************************************************************************/
1883 : /* CanRestartOnError() */
1884 : /************************************************************************/
1885 :
1886 24 : bool VSIS3LikeStreamingHandle::CanRestartOnError(const char *pszErrorMsg,
1887 : const char *pszHeaders,
1888 : bool bSetError)
1889 : {
1890 24 : if (m_poS3HandleHelper->CanRestartOnError(pszErrorMsg, pszHeaders,
1891 24 : bSetError))
1892 : {
1893 4 : SetURL(m_poS3HandleHelper->GetURL().c_str());
1894 4 : return true;
1895 : }
1896 20 : return false;
1897 : }
1898 :
1899 : /************************************************************************/
1900 : /* VSIGSStreamingFSHandler */
1901 : /************************************************************************/
1902 :
1903 : class VSIGSStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
1904 : {
1905 : protected:
1906 67 : CPLString GetFSPrefix() const override
1907 : {
1908 67 : return "/vsigs_streaming/";
1909 : }
1910 :
1911 10 : std::string GetNonStreamingPrefix() const override
1912 : {
1913 10 : return "/vsigs/";
1914 : }
1915 :
1916 : VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
1917 : const char *pszURL) override;
1918 :
1919 : public:
1920 1666 : VSIGSStreamingFSHandler()
1921 1666 : {
1922 1666 : }
1923 :
1924 2242 : ~VSIGSStreamingFSHandler() override
1925 1121 : {
1926 2242 : }
1927 : };
1928 :
1929 : /************************************************************************/
1930 : /* CreateFileHandle() */
1931 : /************************************************************************/
1932 :
1933 : VSICurlStreamingHandle *
1934 10 : VSIGSStreamingFSHandler::CreateFileHandle(const char *pszFilename,
1935 : const char *pszURL)
1936 : {
1937 : VSIGSHandleHelper *poGCHandleHelper =
1938 10 : VSIGSHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str());
1939 10 : if (poGCHandleHelper)
1940 : {
1941 : return new VSIS3LikeStreamingHandle(this, pszFilename,
1942 9 : poGCHandleHelper);
1943 : }
1944 1 : return nullptr;
1945 : }
1946 :
1947 : /************************************************************************/
1948 : /* VSIAzureStreamingFSHandler */
1949 : /************************************************************************/
1950 :
1951 : class VSIAzureStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
1952 : {
1953 : protected:
1954 39 : CPLString GetFSPrefix() const override
1955 : {
1956 39 : return "/vsiaz_streaming/";
1957 : }
1958 :
1959 6 : std::string GetNonStreamingPrefix() const override
1960 : {
1961 6 : return "/vsiaz/";
1962 : }
1963 :
1964 : VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
1965 : const char *pszURL) override;
1966 :
1967 : public:
1968 1666 : VSIAzureStreamingFSHandler()
1969 1666 : {
1970 1666 : }
1971 :
1972 2242 : ~VSIAzureStreamingFSHandler() override
1973 1121 : {
1974 2242 : }
1975 : };
1976 :
1977 : /************************************************************************/
1978 : /* CreateFileHandle() */
1979 : /************************************************************************/
1980 :
1981 : VSICurlStreamingHandle *
1982 6 : VSIAzureStreamingFSHandler::CreateFileHandle(const char *pszFilename,
1983 : const char *pszURL)
1984 : {
1985 : VSIAzureBlobHandleHelper *poHandleHelper =
1986 6 : VSIAzureBlobHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str());
1987 6 : if (poHandleHelper)
1988 : {
1989 5 : return new VSIS3LikeStreamingHandle(this, pszFilename, poHandleHelper);
1990 : }
1991 1 : return nullptr;
1992 : }
1993 :
1994 : /************************************************************************/
1995 : /* VSIOSSStreamingFSHandler */
1996 : /************************************************************************/
1997 :
1998 : class VSIOSSStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
1999 : {
2000 : CPL_DISALLOW_COPY_ASSIGN(VSIOSSStreamingFSHandler)
2001 :
2002 : protected:
2003 60 : CPLString GetFSPrefix() const override
2004 : {
2005 60 : return "/vsioss_streaming/";
2006 : }
2007 :
2008 9 : std::string GetNonStreamingPrefix() const override
2009 : {
2010 9 : return "/vsioss/";
2011 : }
2012 :
2013 : VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
2014 : const char *pszURL) override;
2015 :
2016 : public:
2017 1666 : VSIOSSStreamingFSHandler() = default;
2018 2242 : ~VSIOSSStreamingFSHandler() override = default;
2019 :
2020 327 : void ClearCache() override
2021 : {
2022 327 : IVSIS3LikeStreamingFSHandler::ClearCache();
2023 327 : VSIOSSUpdateParams::ClearCache();
2024 327 : }
2025 : };
2026 :
2027 : /************************************************************************/
2028 : /* CreateFileHandle() */
2029 : /************************************************************************/
2030 :
2031 : VSICurlStreamingHandle *
2032 9 : VSIOSSStreamingFSHandler::CreateFileHandle(const char *pszFilename,
2033 : const char *pszURL)
2034 : {
2035 : VSIOSSHandleHelper *poOSSHandleHelper =
2036 9 : VSIOSSHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str(), false);
2037 9 : if (poOSSHandleHelper)
2038 : {
2039 : return new VSIS3LikeStreamingHandle(this, pszFilename,
2040 8 : poOSSHandleHelper);
2041 : }
2042 1 : return nullptr;
2043 : }
2044 :
2045 : /************************************************************************/
2046 : /* VSISwiftStreamingFSHandler */
2047 : /************************************************************************/
2048 :
2049 : class VSISwiftStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
2050 : {
2051 : protected:
2052 18 : CPLString GetFSPrefix() const override
2053 : {
2054 18 : return "/vsiswift_streaming/";
2055 : }
2056 :
2057 3 : std::string GetNonStreamingPrefix() const override
2058 : {
2059 3 : return "/vsiswift/";
2060 : }
2061 :
2062 : VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
2063 : const char *pszURL) override;
2064 :
2065 : public:
2066 1666 : VSISwiftStreamingFSHandler()
2067 1666 : {
2068 1666 : }
2069 :
2070 2242 : ~VSISwiftStreamingFSHandler() override
2071 1121 : {
2072 2242 : }
2073 : };
2074 :
2075 : /************************************************************************/
2076 : /* CreateFileHandle() */
2077 : /************************************************************************/
2078 :
2079 : VSICurlStreamingHandle *
2080 3 : VSISwiftStreamingFSHandler::CreateFileHandle(const char *pszFilename,
2081 : const char *pszURL)
2082 : {
2083 : VSISwiftHandleHelper *poHandleHelper =
2084 3 : VSISwiftHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str());
2085 3 : if (poHandleHelper)
2086 : {
2087 2 : return new VSIS3LikeStreamingHandle(this, pszFilename, poHandleHelper);
2088 : }
2089 1 : return nullptr;
2090 : }
2091 :
2092 : //! @endcond
2093 :
2094 : } /* namespace cpl */
2095 :
2096 : /************************************************************************/
2097 : /* VSIInstallCurlStreamingFileHandler() */
2098 : /************************************************************************/
2099 :
2100 : /*!
2101 : \brief Install /vsicurl_streaming/ HTTP/FTP file system handler (requires
2102 : libcurl).
2103 :
2104 : \verbatim embed:rst
2105 : See :ref:`/vsicurl_streaming/ documentation <vsicurl_streaming>`
2106 : \endverbatim
2107 :
2108 : @since GDAL 1.10
2109 : */
2110 1666 : void VSIInstallCurlStreamingFileHandler(void)
2111 : {
2112 1666 : VSIFileManager::InstallHandler("/vsicurl_streaming/",
2113 1666 : new cpl::VSICurlStreamingFSHandler);
2114 1666 : }
2115 :
2116 : /************************************************************************/
2117 : /* VSIInstallS3StreamingFileHandler() */
2118 : /************************************************************************/
2119 :
2120 : /*!
2121 : \brief Install /vsis3_streaming/ Amazon S3 file system handler (requires
2122 : libcurl).
2123 :
2124 : \verbatim embed:rst
2125 : See :ref:`/vsis3_streaming/ documentation <vsis3_streaming>`
2126 : \endverbatim
2127 :
2128 : @since GDAL 2.1
2129 : */
2130 1666 : void VSIInstallS3StreamingFileHandler(void)
2131 : {
2132 1666 : VSIFileManager::InstallHandler("/vsis3_streaming/",
2133 1666 : new cpl::VSIS3StreamingFSHandler);
2134 1666 : }
2135 :
2136 : /************************************************************************/
2137 : /* VSIInstallGSStreamingFileHandler() */
2138 : /************************************************************************/
2139 :
2140 : /*!
2141 : \brief Install /vsigs_streaming/ Google Cloud Storage file system handler
2142 : (requires libcurl)
2143 :
2144 : \verbatim embed:rst
2145 : See :ref:`/vsigs_streaming/ documentation <vsigs_streaming>`
2146 : \endverbatim
2147 :
2148 : @since GDAL 2.2
2149 : */
2150 :
2151 1666 : void VSIInstallGSStreamingFileHandler(void)
2152 : {
2153 1666 : VSIFileManager::InstallHandler("/vsigs_streaming/",
2154 1666 : new cpl::VSIGSStreamingFSHandler);
2155 1666 : }
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 : @since GDAL 2.3
2170 : */
2171 :
2172 1666 : void VSIInstallAzureStreamingFileHandler(void)
2173 : {
2174 1666 : VSIFileManager::InstallHandler("/vsiaz_streaming/",
2175 1666 : new cpl::VSIAzureStreamingFSHandler);
2176 1666 : }
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 : @since GDAL 2.3
2191 : */
2192 :
2193 1666 : void VSIInstallOSSStreamingFileHandler(void)
2194 : {
2195 1666 : VSIFileManager::InstallHandler("/vsioss_streaming/",
2196 1666 : new cpl::VSIOSSStreamingFSHandler);
2197 1666 : }
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 : @since GDAL 2.3
2212 : */
2213 :
2214 1666 : void VSIInstallSwiftStreamingFileHandler(void)
2215 : {
2216 1666 : VSIFileManager::InstallHandler("/vsiswift_streaming/",
2217 1666 : new cpl::VSISwiftStreamingFSHandler);
2218 1666 : }
2219 :
2220 : //! @cond Doxygen_Suppress
2221 :
2222 : /************************************************************************/
2223 : /* VSICurlStreamingClearCache() */
2224 : /************************************************************************/
2225 :
2226 327 : 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 327 : char **papszPrefix = VSIFileManager::GetPrefixes();
2232 10137 : for (size_t i = 0; papszPrefix && papszPrefix[i]; ++i)
2233 : {
2234 0 : auto poFSHandler = dynamic_cast<cpl::VSICurlStreamingFSHandler *>(
2235 9810 : VSIFileManager::GetHandler(papszPrefix[i]));
2236 :
2237 9810 : if (poFSHandler)
2238 1962 : poFSHandler->ClearCache();
2239 : }
2240 327 : CSLDestroy(papszPrefix);
2241 327 : }
2242 :
2243 : //! @endcond
2244 :
2245 : #undef ENABLE_DEBUG
2246 :
2247 : #endif // !defined(HAVE_CURL) || defined(CPL_MULTIPROC_STUB)
|