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 198 : size_t GetCapacity() const
104 : {
105 198 : return nCapacity;
106 : }
107 :
108 700 : size_t GetSize() const
109 : {
110 700 : 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 63 : RingBuffer::RingBuffer(size_t nCapacityIn)
119 63 : : pabyBuffer(static_cast<GByte *>(CPLMalloc(nCapacityIn))),
120 64 : nCapacity(nCapacityIn)
121 : {
122 64 : }
123 :
124 128 : RingBuffer::~RingBuffer()
125 : {
126 64 : CPLFree(pabyBuffer);
127 64 : }
128 :
129 151 : void RingBuffer::Reset()
130 : {
131 151 : nOffset = 0;
132 151 : nLength = 0;
133 151 : }
134 :
135 196 : void RingBuffer::Write(void *pBuffer, size_t nSize)
136 : {
137 196 : CPLAssert(nLength + nSize <= nCapacity);
138 :
139 196 : const size_t nEndOffset = (nOffset + nLength) % nCapacity;
140 196 : const size_t nSz = std::min(nSize, nCapacity - nEndOffset);
141 196 : memcpy(pabyBuffer + nEndOffset, pBuffer, nSz);
142 196 : if (nSz < nSize)
143 0 : memcpy(pabyBuffer, static_cast<GByte *>(pBuffer) + nSz, nSize - nSz);
144 :
145 196 : nLength += nSize;
146 196 : }
147 :
148 207 : void RingBuffer::Read(void *pBuffer, size_t nSize)
149 : {
150 207 : CPLAssert(nSize <= nLength);
151 :
152 207 : if (pBuffer)
153 : {
154 207 : const size_t nSz = std::min(nSize, nCapacity - nOffset);
155 207 : memcpy(pBuffer, pabyBuffer + nOffset, nSz);
156 207 : if (nSz < nSize)
157 0 : memcpy(static_cast<GByte *>(pBuffer) + nSz, pabyBuffer,
158 : nSize - nSz);
159 : }
160 :
161 207 : nOffset = (nOffset + nSize) % nCapacity;
162 207 : nLength -= nSize;
163 207 : }
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 9 : virtual std::string GetNonStreamingPrefix() const
209 : {
210 9 : 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 58 : virtual CPLString GetFSPrefix() const
224 : {
225 58 : 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 13 : GetCurlHeaders(const CPLString &,
310 : const struct curl_slist * /* psExistingHeaders */)
311 : {
312 13 : return nullptr;
313 : }
314 :
315 8 : virtual bool StopReceivingBytesOnError()
316 : {
317 8 : 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 115 : virtual bool InterpretRedirect()
328 : {
329 115 : 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 64 : VSICurlStreamingHandle::VSICurlStreamingHandle(VSICurlStreamingFSHandler *poFS,
377 : const char *pszFilename,
378 64 : const char *pszURL)
379 : : m_poFS(poFS), m_aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszFilename)),
380 64 : m_oRetryParameters(m_aosHTTPOptions), m_pszURL(CPLStrdup(pszURL))
381 : {
382 64 : FileProp cachedFileProp;
383 64 : poFS->GetCachedFileProp(pszURL, cachedFileProp);
384 64 : eExists = cachedFileProp.eExists;
385 64 : fileSize = cachedFileProp.fileSize;
386 64 : bHasComputedFileSize = cachedFileProp.bHasComputedFileSize;
387 64 : bIsDirectory = cachedFileProp.bIsDirectory;
388 64 : poFS->SetCachedFileProp(pszURL, cachedFileProp);
389 :
390 64 : hRingBufferMutex = CPLCreateMutex();
391 64 : ReleaseMutex();
392 64 : hCondProducer = CPLCreateCond();
393 64 : hCondConsumer = CPLCreateCond();
394 :
395 64 : memset(m_szCurlErrBuf, 0, sizeof(m_szCurlErrBuf));
396 64 : }
397 :
398 : /************************************************************************/
399 : /* ~VSICurlStreamingHandle() */
400 : /************************************************************************/
401 :
402 73 : VSICurlStreamingHandle::~VSICurlStreamingHandle()
403 : {
404 64 : StopDownload();
405 :
406 64 : CPLFree(m_pszURL);
407 :
408 64 : CPLFree(pCachedData);
409 :
410 64 : CPLFree(pabyHeaderData);
411 :
412 64 : CPLDestroyMutex(hRingBufferMutex);
413 64 : CPLDestroyCond(hCondProducer);
414 64 : CPLDestroyCond(hCondConsumer);
415 73 : }
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 1188 : void VSICurlStreamingHandle::AcquireMutex()
432 : {
433 1188 : CPLAcquireMutex(hRingBufferMutex, 1000.0);
434 1188 : }
435 :
436 : /************************************************************************/
437 : /* ReleaseMutex() */
438 : /************************************************************************/
439 :
440 1252 : void VSICurlStreamingHandle::ReleaseMutex()
441 : {
442 1252 : CPLReleaseMutex(hRingBufferMutex);
443 1252 : }
444 :
445 : /************************************************************************/
446 : /* Seek() */
447 : /************************************************************************/
448 :
449 63 : int VSICurlStreamingHandle::Seek(vsi_l_offset nOffset, int nWhence)
450 : {
451 63 : 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 63 : if (nWhence == SEEK_SET)
467 : {
468 63 : curOffset = nOffset;
469 : }
470 0 : else if (nWhence == SEEK_CUR)
471 : {
472 0 : curOffset = curOffset + nOffset;
473 : }
474 : else
475 : {
476 0 : curOffset = GetFileSize() + nOffset;
477 : }
478 63 : bEOF = false;
479 63 : 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 : return 0;
536 : }
537 : else
538 : {
539 0 : psStruct->bIsInHeader = FALSE;
540 : }
541 : }
542 : }
543 50 : psStruct->nSize += nSize;
544 50 : return nmemb;
545 : }
546 : else
547 : {
548 0 : return 0;
549 : }
550 : }
551 :
552 : /************************************************************************/
553 : /* GetFileSize() */
554 : /************************************************************************/
555 :
556 10 : vsi_l_offset VSICurlStreamingHandle::GetFileSize()
557 : {
558 : WriteFuncStructStreaming sWriteFuncData;
559 : WriteFuncStructStreaming sWriteFuncHeaderData;
560 :
561 10 : AcquireMutex();
562 10 : if (bHasComputedFileSize)
563 : {
564 1 : const vsi_l_offset nRet = fileSize;
565 1 : ReleaseMutex();
566 1 : return nRet;
567 : }
568 9 : ReleaseMutex();
569 :
570 9 : CURL *hLocalHandle = curl_easy_init();
571 :
572 : struct curl_slist *headers =
573 9 : VSICurlSetOptions(hLocalHandle, m_pszURL, m_aosHTTPOptions.List());
574 :
575 9 : VSICURLStreamingInitWriteFuncStructStreaming(&sWriteFuncHeaderData);
576 :
577 : // HACK for mbtiles driver: Proper fix would be to auto-detect servers that
578 : // don't accept HEAD http://a.tiles.mapbox.com/v3/ doesn't accept HEAD, so
579 : // let's start a GET and interrupt is as soon as the header is found.
580 18 : CPLString osVerb;
581 9 : if (strstr(m_pszURL, ".tiles.mapbox.com/") != nullptr)
582 : {
583 0 : unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HEADERDATA,
584 : &sWriteFuncHeaderData);
585 0 : unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HEADERFUNCTION,
586 : VSICurlStreamingHandleWriteFuncForHeader);
587 :
588 0 : sWriteFuncHeaderData.bIsHTTP = STARTS_WITH(m_pszURL, "http");
589 0 : sWriteFuncHeaderData.bDownloadHeaderOnly = TRUE;
590 0 : osVerb = "GET";
591 : }
592 : else
593 : {
594 9 : unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_NOBODY, 1);
595 9 : unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HTTPGET, 0);
596 9 : unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HEADER, 1);
597 9 : osVerb = "HEAD";
598 : }
599 :
600 9 : headers = VSICurlMergeHeaders(headers, GetCurlHeaders(osVerb, headers));
601 9 : unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_HTTPHEADER, headers);
602 :
603 : // We need that otherwise OSGEO4W's libcurl issue a dummy range request
604 : // when doing a HEAD when recycling connections.
605 9 : unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_RANGE, nullptr);
606 :
607 : // Bug with older curl versions (<=7.16.4) and FTP.
608 : // See http://curl.haxx.se/mail/lib-2007-08/0312.html
609 9 : VSICURLStreamingInitWriteFuncStructStreaming(&sWriteFuncData);
610 9 : unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_WRITEDATA,
611 : &sWriteFuncData);
612 9 : unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_WRITEFUNCTION,
613 : VSICurlStreamingHandleWriteFuncForHeader);
614 :
615 9 : char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
616 9 : unchecked_curl_easy_setopt(hLocalHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf);
617 :
618 9 : void *old_handler = CPLHTTPIgnoreSigPipe();
619 9 : curl_easy_perform(hLocalHandle);
620 9 : CPLHTTPRestoreSigPipeHandler(old_handler);
621 9 : if (headers != nullptr)
622 9 : curl_slist_free_all(headers);
623 :
624 9 : AcquireMutex();
625 :
626 9 : eExists = EXIST_UNKNOWN;
627 9 : bHasComputedFileSize = true;
628 :
629 9 : if (STARTS_WITH(m_pszURL, "ftp"))
630 : {
631 0 : if (sWriteFuncData.pBuffer != nullptr &&
632 0 : STARTS_WITH_CI(sWriteFuncData.pBuffer, "Content-Length: "))
633 : {
634 0 : const char *pszBuffer =
635 0 : sWriteFuncData.pBuffer + strlen("Content-Length: ");
636 0 : eExists = EXIST_YES;
637 0 : fileSize = CPLScanUIntBig(
638 0 : pszBuffer, static_cast<int>(sWriteFuncData.nSize -
639 : strlen("Content-Length: ")));
640 : if (ENABLE_DEBUG)
641 : CPLDebug("VSICURL", "GetFileSize(%s)=" CPL_FRMT_GUIB, m_pszURL,
642 : fileSize);
643 : }
644 : }
645 :
646 9 : double dfSize = 0;
647 9 : if (eExists != EXIST_YES)
648 : {
649 9 : curl_off_t nSizeTmp = 0;
650 9 : const CURLcode code = curl_easy_getinfo(
651 : hLocalHandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &nSizeTmp);
652 9 : CPL_IGNORE_RET_VAL(dfSize);
653 9 : dfSize = static_cast<double>(nSizeTmp);
654 9 : if (code == 0)
655 : {
656 9 : eExists = EXIST_YES;
657 9 : if (dfSize < 0)
658 0 : fileSize = 0;
659 : else
660 9 : fileSize = static_cast<GUIntBig>(dfSize);
661 : }
662 : else
663 : {
664 0 : eExists = EXIST_NO;
665 0 : fileSize = 0;
666 0 : CPLError(CE_Failure, CPLE_AppDefined,
667 : "VSICurlStreamingHandle::GetFileSize failed");
668 : }
669 :
670 9 : long response_code = 0;
671 9 : curl_easy_getinfo(hLocalHandle, CURLINFO_HTTP_CODE, &response_code);
672 9 : if (response_code != 200)
673 : {
674 0 : eExists = EXIST_NO;
675 0 : fileSize = 0;
676 : }
677 :
678 : // Try to guess if this is a directory. Generally if this is a
679 : // directory, curl will retry with an URL with slash added.
680 9 : char *pszEffectiveURL = nullptr;
681 9 : curl_easy_getinfo(hLocalHandle, CURLINFO_EFFECTIVE_URL,
682 : &pszEffectiveURL);
683 9 : if (pszEffectiveURL != nullptr &&
684 9 : strncmp(m_pszURL, pszEffectiveURL, strlen(m_pszURL)) == 0 &&
685 9 : pszEffectiveURL[strlen(m_pszURL)] == '/')
686 : {
687 0 : eExists = EXIST_YES;
688 0 : fileSize = 0;
689 0 : bIsDirectory = true;
690 : }
691 :
692 : if (ENABLE_DEBUG)
693 : CPLDebug("VSICURL",
694 : "GetFileSize(%s)=" CPL_FRMT_GUIB " response_code=%d",
695 : m_pszURL, fileSize, static_cast<int>(response_code));
696 : }
697 :
698 9 : CPLFree(sWriteFuncData.pBuffer);
699 9 : CPLFree(sWriteFuncHeaderData.pBuffer);
700 :
701 9 : FileProp cachedFileProp;
702 9 : m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
703 9 : cachedFileProp.bHasComputedFileSize = true;
704 9 : cachedFileProp.fileSize = fileSize;
705 9 : cachedFileProp.eExists = eExists;
706 9 : cachedFileProp.bIsDirectory = bIsDirectory;
707 9 : if (cachedFileProp.nMode == 0)
708 9 : cachedFileProp.nMode = bIsDirectory ? S_IFDIR : S_IFREG;
709 9 : m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
710 :
711 9 : const vsi_l_offset nRet = fileSize;
712 9 : ReleaseMutex();
713 :
714 9 : curl_easy_cleanup(hLocalHandle);
715 :
716 9 : return nRet;
717 : }
718 :
719 : /************************************************************************/
720 : /* Exists() */
721 : /************************************************************************/
722 :
723 61 : bool VSICurlStreamingHandle::Exists(const char *pszFilename,
724 : CSLConstList papszOptions)
725 : {
726 61 : if (eExists == EXIST_UNKNOWN)
727 : {
728 48 : if (!papszOptions ||
729 0 : !CPLTestBool(CSLFetchNameValueDef(
730 : papszOptions, "IGNORE_FILENAME_RESTRICTIONS", "NO")))
731 : {
732 48 : if (!VSICurlFilesystemHandlerBase::IsAllowedFilename(pszFilename))
733 : {
734 0 : eExists = EXIST_NO;
735 0 : fileSize = 0;
736 :
737 0 : FileProp cachedFileProp;
738 0 : m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
739 0 : cachedFileProp.bHasComputedFileSize = true;
740 0 : cachedFileProp.fileSize = fileSize;
741 0 : cachedFileProp.eExists = eExists;
742 0 : cachedFileProp.bIsDirectory = false;
743 0 : cachedFileProp.nMode = S_IFREG;
744 0 : m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
745 :
746 0 : return false;
747 : }
748 : }
749 :
750 48 : char chFirstByte = '\0';
751 48 : int bExists = (Read(&chFirstByte, 1, 1) == 1);
752 :
753 96 : FileProp cachedFileProp;
754 48 : m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
755 48 : cachedFileProp.eExists = eExists = bExists ? EXIST_YES : EXIST_NO;
756 48 : m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
757 :
758 48 : Seek(0, SEEK_SET);
759 : }
760 :
761 61 : return eExists == EXIST_YES;
762 : }
763 :
764 : /************************************************************************/
765 : /* Tell() */
766 : /************************************************************************/
767 :
768 0 : vsi_l_offset VSICurlStreamingHandle::Tell()
769 : {
770 0 : return curOffset;
771 : }
772 :
773 : /************************************************************************/
774 : /* ReceivedBytes() */
775 : /************************************************************************/
776 :
777 195 : size_t VSICurlStreamingHandle::ReceivedBytes(GByte *buffer, size_t count,
778 : size_t nmemb)
779 : {
780 195 : size_t nSize = count * nmemb;
781 195 : nBodySize += nSize;
782 :
783 : if (ENABLE_DEBUG)
784 : CPLDebug("VSICURL", "Receiving %d bytes...", static_cast<int>(nSize));
785 :
786 195 : if (bHasCandidateFileSize && bCanTrustCandidateFileSize &&
787 181 : !bHasComputedFileSize)
788 : {
789 362 : FileProp cachedFileProp;
790 181 : m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
791 181 : cachedFileProp.fileSize = fileSize = nCandidateFileSize;
792 181 : bHasCandidateFileSize = TRUE;
793 181 : cachedFileProp.bHasComputedFileSize = bHasComputedFileSize;
794 181 : m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
795 : if (ENABLE_DEBUG)
796 : CPLDebug("VSICURL", "File size = " CPL_FRMT_GUIB, fileSize);
797 : }
798 :
799 195 : AcquireMutex();
800 195 : if (eExists == EXIST_UNKNOWN)
801 : {
802 0 : FileProp cachedFileProp;
803 0 : m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
804 0 : cachedFileProp.eExists = eExists = EXIST_YES;
805 0 : m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
806 : }
807 195 : else if (eExists == EXIST_NO && StopReceivingBytesOnError())
808 : {
809 0 : ReleaseMutex();
810 0 : return 0;
811 : }
812 :
813 : while (true)
814 : {
815 196 : const size_t nFree = oRingBuffer.GetCapacity() - oRingBuffer.GetSize();
816 196 : if (nSize <= nFree)
817 : {
818 195 : oRingBuffer.Write(buffer, nSize);
819 :
820 : // Signal to the consumer that we have added bytes to the buffer.
821 195 : CPLCondSignal(hCondProducer);
822 :
823 195 : if (bAskDownloadEnd)
824 : {
825 : if (ENABLE_DEBUG)
826 : CPLDebug("VSICURL", "Download interruption asked");
827 :
828 1 : ReleaseMutex();
829 1 : return 0;
830 : }
831 194 : break;
832 : }
833 : else
834 : {
835 1 : oRingBuffer.Write(buffer, nFree);
836 1 : buffer += nFree;
837 1 : nSize -= nFree;
838 :
839 : // Signal to the consumer that we have added bytes to the buffer.
840 1 : CPLCondSignal(hCondProducer);
841 :
842 : if (ENABLE_DEBUG)
843 : CPLDebug("VSICURL",
844 : "Waiting for reader to consume some bytes...");
845 :
846 3 : while (oRingBuffer.GetSize() == oRingBuffer.GetCapacity() &&
847 1 : !bAskDownloadEnd)
848 : {
849 1 : CPLCondWait(hCondConsumer, hRingBufferMutex);
850 : }
851 :
852 1 : if (bAskDownloadEnd)
853 : {
854 : if (ENABLE_DEBUG)
855 : CPLDebug("VSICURL", "Download interruption asked");
856 :
857 0 : ReleaseMutex();
858 0 : return 0;
859 : }
860 : }
861 1 : }
862 :
863 194 : ReleaseMutex();
864 :
865 194 : return nmemb;
866 : }
867 :
868 : /************************************************************************/
869 : /* VSICurlStreamingHandleReceivedBytes() */
870 : /************************************************************************/
871 :
872 195 : static size_t VSICurlStreamingHandleReceivedBytes(void *buffer, size_t count,
873 : size_t nmemb, void *req)
874 : {
875 195 : return static_cast<VSICurlStreamingHandle *>(req)->ReceivedBytes(
876 195 : static_cast<GByte *>(buffer), count, nmemb);
877 : }
878 :
879 : /************************************************************************/
880 : /* VSICurlStreamingHandleReceivedBytesHeader() */
881 : /************************************************************************/
882 :
883 : #define HEADER_SIZE 32768
884 :
885 404 : size_t VSICurlStreamingHandle::ReceivedBytesHeader(GByte *buffer, size_t count,
886 : size_t nmemb)
887 : {
888 404 : const size_t nSize = count * nmemb;
889 : if (ENABLE_DEBUG)
890 : CPLDebug("VSICURL", "Receiving %d bytes for header...",
891 : static_cast<int>(nSize));
892 :
893 : // Reset buffer if we have followed link after a redirect.
894 347 : if (nSize >= 9 && InterpretRedirect() &&
895 751 : (nHTTPCode == 301 || nHTTPCode == 302) &&
896 0 : STARTS_WITH_CI(reinterpret_cast<char *>(buffer), "HTTP/"))
897 : {
898 0 : nHeaderSize = 0;
899 0 : nHTTPCode = 0;
900 : }
901 :
902 404 : if (nHeaderSize < HEADER_SIZE)
903 : {
904 404 : const size_t nSz = std::min(nSize, HEADER_SIZE - nHeaderSize);
905 404 : memcpy(pabyHeaderData + nHeaderSize, buffer, nSz);
906 404 : pabyHeaderData[nHeaderSize + nSz] = '\0';
907 404 : nHeaderSize += nSz;
908 :
909 : #if DEBUG_VERBOSE
910 : CPLDebug("VSICURL", "Header : %s", pabyHeaderData);
911 : #endif
912 :
913 404 : AcquireMutex();
914 :
915 404 : if (nHTTPCode == 0 &&
916 57 : strchr(reinterpret_cast<char *>(pabyHeaderData), '\n') != nullptr &&
917 57 : STARTS_WITH_CI(reinterpret_cast<char *>(pabyHeaderData), "HTTP/"))
918 : {
919 : const char *pszSpace =
920 57 : strchr(const_cast<const char *>(
921 57 : reinterpret_cast<char *>(pabyHeaderData)),
922 : ' ');
923 57 : if (pszSpace)
924 57 : nHTTPCode = atoi(pszSpace + 1);
925 : if (ENABLE_DEBUG)
926 : CPLDebug("VSICURL", "HTTP code = %d", nHTTPCode);
927 :
928 : // If moved permanently/temporarily, go on.
929 107 : if (eExists == EXIST_UNKNOWN &&
930 50 : !(InterpretRedirect() &&
931 7 : (nHTTPCode == 301 || nHTTPCode == 302)))
932 : {
933 50 : eExists = nHTTPCode == 200 ? EXIST_YES : EXIST_NO;
934 100 : FileProp cachedFileProp;
935 50 : m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
936 50 : cachedFileProp.eExists = eExists;
937 50 : m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
938 : }
939 : }
940 :
941 808 : if (!(InterpretRedirect() && (nHTTPCode == 301 || nHTTPCode == 302)) &&
942 404 : !bHasComputedFileSize)
943 : {
944 : // Caution: When gzip compression is enabled, the content-length is
945 : // the compressed size, which we are not interested in, so we must
946 : // not take it into account.
947 :
948 375 : const char *pszContentLength = strstr(
949 375 : reinterpret_cast<char *>(pabyHeaderData), "Content-Length: ");
950 375 : const char *pszEndOfLine =
951 375 : pszContentLength ? strchr(pszContentLength, '\n') : nullptr;
952 375 : if (bCanTrustCandidateFileSize && pszEndOfLine != nullptr)
953 : {
954 99 : const char *pszVal =
955 : pszContentLength + strlen("Content-Length: ");
956 99 : bHasCandidateFileSize = true;
957 99 : nCandidateFileSize = CPLScanUIntBig(
958 99 : pszVal, static_cast<int>(pszEndOfLine - pszVal));
959 : if (ENABLE_DEBUG)
960 : CPLDebug("VSICURL",
961 : "Has found candidate file size = " CPL_FRMT_GUIB,
962 : nCandidateFileSize);
963 : }
964 :
965 375 : const char *pszContentEncoding = strstr(
966 375 : reinterpret_cast<char *>(pabyHeaderData), "Content-Encoding: ");
967 375 : pszEndOfLine =
968 375 : pszContentEncoding ? strchr(pszContentEncoding, '\n') : nullptr;
969 375 : if (bHasCandidateFileSize && pszEndOfLine != nullptr)
970 : {
971 0 : const char *pszVal =
972 : pszContentEncoding + strlen("Content-Encoding: ");
973 0 : if (STARTS_WITH(pszVal, "gzip"))
974 : {
975 : if (ENABLE_DEBUG)
976 : CPLDebug("VSICURL", "GZip compression enabled --> "
977 : "cannot trust candidate file size");
978 0 : bCanTrustCandidateFileSize = false;
979 : }
980 : }
981 : }
982 :
983 404 : ReleaseMutex();
984 : }
985 :
986 404 : return nmemb;
987 : }
988 :
989 : /************************************************************************/
990 : /* VSICurlStreamingHandleReceivedBytesHeader() */
991 : /************************************************************************/
992 :
993 404 : static size_t VSICurlStreamingHandleReceivedBytesHeader(void *buffer,
994 : size_t count,
995 : size_t nmemb, void *req)
996 : {
997 404 : return static_cast<VSICurlStreamingHandle *>(req)->ReceivedBytesHeader(
998 404 : static_cast<GByte *>(buffer), count, nmemb);
999 : }
1000 :
1001 : /************************************************************************/
1002 : /* DownloadInThread() */
1003 : /************************************************************************/
1004 :
1005 59 : void VSICurlStreamingHandle::DownloadInThread()
1006 : {
1007 59 : CURL *hCurlHandle = curl_easy_init();
1008 :
1009 : struct curl_slist *headers =
1010 59 : VSICurlSetOptions(hCurlHandle, m_pszURL, m_aosHTTPOptions.List());
1011 59 : headers = VSICurlMergeHeaders(headers, GetCurlHeaders("GET", headers));
1012 59 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
1013 :
1014 : static bool bHasCheckVersion = false;
1015 : static bool bSupportGZip = false;
1016 59 : if (!bHasCheckVersion)
1017 : {
1018 2 : bSupportGZip = strstr(curl_version(), "zlib/") != nullptr;
1019 2 : bHasCheckVersion = true;
1020 : }
1021 59 : if (bSupportGZip && CPLTestBool(CPLGetConfigOption("CPL_CURL_GZIP", "YES")))
1022 : {
1023 59 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_ENCODING, "gzip");
1024 : }
1025 :
1026 59 : if (pabyHeaderData == nullptr)
1027 51 : pabyHeaderData = static_cast<GByte *>(CPLMalloc(HEADER_SIZE + 1));
1028 59 : nHeaderSize = 0;
1029 59 : nBodySize = 0;
1030 59 : nHTTPCode = 0;
1031 :
1032 59 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA, this);
1033 59 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION,
1034 : VSICurlStreamingHandleReceivedBytesHeader);
1035 :
1036 59 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, this);
1037 59 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
1038 : VSICurlStreamingHandleReceivedBytes);
1039 :
1040 59 : m_szCurlErrBuf[0] = '\0';
1041 59 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER,
1042 : m_szCurlErrBuf);
1043 :
1044 59 : void *old_handler = CPLHTTPIgnoreSigPipe();
1045 59 : CURLcode eRet = curl_easy_perform(hCurlHandle);
1046 59 : CPLHTTPRestoreSigPipeHandler(old_handler);
1047 59 : if (headers != nullptr)
1048 47 : curl_slist_free_all(headers);
1049 :
1050 59 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, nullptr);
1051 59 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION, nullptr);
1052 59 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA, nullptr);
1053 59 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION, nullptr);
1054 :
1055 59 : AcquireMutex();
1056 59 : m_bErrorOccurredInThread = eRet != CURLE_OK;
1057 59 : if (m_bErrorOccurredInThread)
1058 : {
1059 : // For autotest purposes only !
1060 8 : const char *pszSimulatedCurlError = CPLGetConfigOption(
1061 : "CPL_VSIL_CURL_STREMAING_SIMULATED_CURL_ERROR", nullptr);
1062 8 : if (pszSimulatedCurlError)
1063 5 : snprintf(m_szCurlErrBuf, sizeof(m_szCurlErrBuf), "%s",
1064 : pszSimulatedCurlError);
1065 : }
1066 :
1067 59 : if (!bAskDownloadEnd && eRet == CURLE_OK && !bHasComputedFileSize)
1068 : {
1069 96 : FileProp cachedFileProp;
1070 48 : m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
1071 48 : fileSize = nBodySize;
1072 48 : cachedFileProp.fileSize = fileSize;
1073 48 : bHasComputedFileSize = true;
1074 48 : cachedFileProp.bHasComputedFileSize = bHasComputedFileSize;
1075 48 : m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
1076 : if (ENABLE_DEBUG)
1077 : CPLDebug("VSICURL", "File size = " CPL_FRMT_GUIB, fileSize);
1078 : }
1079 :
1080 59 : bDownloadInProgress = FALSE;
1081 59 : bDownloadStopped = TRUE;
1082 :
1083 : // Signal to the consumer that the download has ended.
1084 59 : CPLCondSignal(hCondProducer);
1085 59 : ReleaseMutex();
1086 :
1087 59 : curl_easy_cleanup(hCurlHandle);
1088 59 : }
1089 :
1090 59 : static void VSICurlDownloadInThread(void *pArg)
1091 : {
1092 59 : static_cast<VSICurlStreamingHandle *>(pArg)->DownloadInThread();
1093 59 : }
1094 :
1095 : /************************************************************************/
1096 : /* StartDownload() */
1097 : /************************************************************************/
1098 :
1099 94 : void VSICurlStreamingHandle::StartDownload()
1100 : {
1101 94 : if (bDownloadInProgress || bDownloadStopped)
1102 35 : return;
1103 :
1104 59 : CPLDebug("VSICURL", "Start download for %s", m_pszURL);
1105 :
1106 59 : oRingBuffer.Reset();
1107 59 : bDownloadInProgress = TRUE;
1108 59 : nRingBufferFileOffset = 0;
1109 59 : m_bErrorOccurredInThread = false;
1110 59 : hThread = CPLCreateJoinableThread(VSICurlDownloadInThread, this);
1111 : }
1112 :
1113 : /************************************************************************/
1114 : /* StopDownload() */
1115 : /************************************************************************/
1116 :
1117 92 : void VSICurlStreamingHandle::StopDownload()
1118 : {
1119 92 : if (hThread)
1120 : {
1121 59 : CPLDebug("VSICURL", "Stop download for %s", m_pszURL);
1122 :
1123 59 : AcquireMutex();
1124 : // Signal to the producer that we ask for download interruption.
1125 59 : bAskDownloadEnd = TRUE;
1126 59 : CPLCondSignal(hCondConsumer);
1127 :
1128 : // Wait for the producer to have finished.
1129 60 : while (bDownloadInProgress)
1130 1 : CPLCondWait(hCondProducer, hRingBufferMutex);
1131 :
1132 59 : bAskDownloadEnd = FALSE;
1133 :
1134 59 : ReleaseMutex();
1135 :
1136 59 : CPLJoinThread(hThread);
1137 59 : hThread = nullptr;
1138 : }
1139 :
1140 92 : oRingBuffer.Reset();
1141 92 : bDownloadStopped = FALSE;
1142 92 : m_bErrorOccurredInThread = false;
1143 92 : nRingBufferFileOffset = 0;
1144 92 : bEOF = false;
1145 92 : }
1146 :
1147 : /************************************************************************/
1148 : /* PutRingBufferInCache() */
1149 : /************************************************************************/
1150 :
1151 41 : void VSICurlStreamingHandle::PutRingBufferInCache()
1152 : {
1153 41 : if (nRingBufferFileOffset >= BKGND_BUFFER_SIZE)
1154 0 : return;
1155 :
1156 41 : AcquireMutex();
1157 :
1158 : // Cache any remaining bytes available in the ring buffer.
1159 41 : size_t nBufSize = oRingBuffer.GetSize();
1160 41 : if (nBufSize > 0)
1161 : {
1162 27 : if (nRingBufferFileOffset + nBufSize > BKGND_BUFFER_SIZE)
1163 1 : nBufSize =
1164 1 : static_cast<size_t>(BKGND_BUFFER_SIZE - nRingBufferFileOffset);
1165 27 : GByte *pabyTmp = static_cast<GByte *>(CPLMalloc(nBufSize));
1166 27 : oRingBuffer.Read(pabyTmp, nBufSize);
1167 :
1168 : // Signal to the producer that we have ingested some bytes.
1169 27 : CPLCondSignal(hCondConsumer);
1170 :
1171 27 : AddRegion(nRingBufferFileOffset, nBufSize, pabyTmp);
1172 27 : nRingBufferFileOffset += nBufSize;
1173 27 : CPLFree(pabyTmp);
1174 : }
1175 :
1176 41 : ReleaseMutex();
1177 : }
1178 :
1179 : /************************************************************************/
1180 : /* Read() */
1181 : /************************************************************************/
1182 :
1183 118 : size_t VSICurlStreamingHandle::Read(void *const pBuffer, size_t const nSize,
1184 : size_t const nMemb)
1185 : {
1186 118 : const size_t nBufferRequestSize = nSize * nMemb;
1187 118 : const vsi_l_offset curOffsetOri = curOffset;
1188 118 : const vsi_l_offset nRingBufferFileOffsetOri = nRingBufferFileOffset;
1189 118 : if (nBufferRequestSize == 0)
1190 0 : return 0;
1191 :
1192 236 : CPLHTTPRetryContext oRetryContext(m_oRetryParameters);
1193 :
1194 126 : retry:
1195 126 : GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
1196 126 : size_t nRemaining = nBufferRequestSize;
1197 :
1198 126 : AcquireMutex();
1199 : // fileSize might be set wrongly to 0, such as
1200 : // /vsicurl_streaming/https://query.data.world/s/jgsghstpphjhicstradhy5kpjwrnfy
1201 126 : const bool bHasComputedFileSizeLocal = bHasComputedFileSize && fileSize > 0;
1202 126 : const vsi_l_offset fileSizeLocal = fileSize;
1203 126 : ReleaseMutex();
1204 :
1205 126 : if (bHasComputedFileSizeLocal && curOffset >= fileSizeLocal)
1206 : {
1207 0 : CPLDebug("VSICURL", "Read attempt beyond end of file");
1208 0 : bEOF = true;
1209 : }
1210 126 : if (bEOF)
1211 0 : return 0;
1212 :
1213 126 : if (curOffset < nRingBufferFileOffset)
1214 41 : PutRingBufferInCache();
1215 :
1216 : if (ENABLE_DEBUG)
1217 : CPLDebug("VSICURL", "Read [" CPL_FRMT_GUIB ", " CPL_FRMT_GUIB "[ in %s",
1218 : curOffset, curOffset + nBufferRequestSize, m_pszURL);
1219 :
1220 : // Can we use the cache?
1221 126 : if (pCachedData != nullptr && curOffset < nCachedSize)
1222 : {
1223 : const size_t nSz =
1224 45 : std::min(nRemaining, static_cast<size_t>(nCachedSize - curOffset));
1225 : if (ENABLE_DEBUG)
1226 : CPLDebug("VSICURL", "Using cache for [%d, %d[ in %s",
1227 : static_cast<int>(curOffset),
1228 : static_cast<int>(curOffset + nSz), m_pszURL);
1229 45 : memcpy(pabyBuffer, pCachedData + curOffset, nSz);
1230 45 : pabyBuffer += nSz;
1231 45 : curOffset += nSz;
1232 45 : nRemaining -= nSz;
1233 : }
1234 :
1235 : // Is the request partially covered by the cache and going beyond file size?
1236 126 : if (pCachedData != nullptr && bHasComputedFileSizeLocal &&
1237 32 : curOffset <= nCachedSize && curOffset + nRemaining > fileSizeLocal &&
1238 26 : fileSize == nCachedSize)
1239 : {
1240 23 : size_t nSz = static_cast<size_t>(nCachedSize - curOffset);
1241 : if (ENABLE_DEBUG && nSz != 0)
1242 : CPLDebug("VSICURL", "Using cache for [%d, %d[ in %s",
1243 : static_cast<int>(curOffset),
1244 : static_cast<int>(curOffset + nSz), m_pszURL);
1245 23 : memcpy(pabyBuffer, pCachedData + curOffset, nSz);
1246 23 : pabyBuffer += nSz;
1247 23 : curOffset += nSz;
1248 23 : nRemaining -= nSz;
1249 23 : bEOF = true;
1250 : }
1251 :
1252 126 : bool bErrorOccurred = false;
1253 :
1254 : // Has a Seek() being done since the last Read()?
1255 126 : if (!bEOF && nRemaining > 0 && curOffset != nRingBufferFileOffset)
1256 : {
1257 : // Backward seek: Need to restart the download from the beginning.
1258 4 : if (curOffset < nRingBufferFileOffset)
1259 0 : StopDownload();
1260 :
1261 4 : StartDownload();
1262 :
1263 4 : const vsi_l_offset SKIP_BUFFER_SIZE = 32768;
1264 4 : GByte *pabyTmp = static_cast<GByte *>(CPLMalloc(SKIP_BUFFER_SIZE));
1265 :
1266 4 : CPLAssert(curOffset >= nRingBufferFileOffset);
1267 4 : vsi_l_offset nBytesToSkip = curOffset - nRingBufferFileOffset;
1268 12 : while (nBytesToSkip > 0)
1269 : {
1270 10 : vsi_l_offset nBytesToRead = nBytesToSkip;
1271 :
1272 10 : AcquireMutex();
1273 10 : if (nBytesToRead > oRingBuffer.GetSize())
1274 8 : nBytesToRead = oRingBuffer.GetSize();
1275 10 : if (nBytesToRead > SKIP_BUFFER_SIZE)
1276 0 : nBytesToRead = SKIP_BUFFER_SIZE;
1277 10 : oRingBuffer.Read(pabyTmp, static_cast<size_t>(nBytesToRead));
1278 :
1279 : // Signal to the producer that we have ingested some bytes.
1280 10 : CPLCondSignal(hCondConsumer);
1281 10 : ReleaseMutex();
1282 :
1283 10 : if (nBytesToRead)
1284 4 : AddRegion(nRingBufferFileOffset,
1285 : static_cast<size_t>(nBytesToRead), pabyTmp);
1286 :
1287 10 : nBytesToSkip -= nBytesToRead;
1288 10 : nRingBufferFileOffset += nBytesToRead;
1289 :
1290 10 : if (nBytesToRead == 0 && nBytesToSkip != 0)
1291 : {
1292 : if (ENABLE_DEBUG)
1293 : CPLDebug("VSICURL",
1294 : "Waiting for writer to produce some bytes...");
1295 :
1296 6 : AcquireMutex();
1297 12 : while (oRingBuffer.GetSize() == 0 && bDownloadInProgress)
1298 6 : CPLCondWait(hCondProducer, hRingBufferMutex);
1299 6 : const int bBufferEmpty = (oRingBuffer.GetSize() == 0);
1300 6 : bErrorOccurred = m_bErrorOccurredInThread;
1301 6 : ReleaseMutex();
1302 :
1303 6 : if (bBufferEmpty && !bDownloadInProgress)
1304 2 : break;
1305 : }
1306 : }
1307 :
1308 4 : CPLFree(pabyTmp);
1309 :
1310 4 : if (nBytesToSkip != 0 && !bErrorOccurred)
1311 : {
1312 0 : bEOF = true;
1313 0 : return 0;
1314 : }
1315 : }
1316 :
1317 126 : if (!bEOF && nRemaining > 0 && !bErrorOccurred)
1318 : {
1319 90 : StartDownload();
1320 90 : CPLAssert(curOffset == nRingBufferFileOffset);
1321 : }
1322 :
1323 : // Fill the destination buffer from the ring buffer.
1324 261 : while (!bEOF && nRemaining > 0 && !bErrorOccurred)
1325 : {
1326 170 : AcquireMutex();
1327 170 : size_t nToRead = oRingBuffer.GetSize();
1328 170 : if (nToRead > nRemaining)
1329 51 : nToRead = nRemaining;
1330 170 : oRingBuffer.Read(pabyBuffer, nToRead);
1331 :
1332 : // Signal to the producer that we have ingested some bytes.
1333 170 : CPLCondSignal(hCondConsumer);
1334 170 : ReleaseMutex();
1335 :
1336 170 : if (nToRead)
1337 82 : AddRegion(curOffset, nToRead, pabyBuffer);
1338 :
1339 170 : nRemaining -= nToRead;
1340 170 : pabyBuffer += nToRead;
1341 170 : curOffset += nToRead;
1342 170 : nRingBufferFileOffset += nToRead;
1343 :
1344 170 : if (nToRead == 0 && nRemaining != 0)
1345 : {
1346 : if (ENABLE_DEBUG)
1347 : CPLDebug("VSICURL",
1348 : "Waiting for writer to produce some bytes...");
1349 :
1350 88 : AcquireMutex();
1351 167 : while (oRingBuffer.GetSize() == 0 && bDownloadInProgress)
1352 78 : CPLCondWait(hCondProducer, hRingBufferMutex);
1353 88 : const bool bBufferEmpty = oRingBuffer.GetSize() == 0;
1354 88 : bErrorOccurred = m_bErrorOccurredInThread;
1355 88 : ReleaseMutex();
1356 :
1357 88 : if (bBufferEmpty && !bDownloadInProgress)
1358 35 : break;
1359 : }
1360 : }
1361 :
1362 : if (ENABLE_DEBUG)
1363 : CPLDebug("VSICURL", "Read(%d) = %d",
1364 : static_cast<int>(nBufferRequestSize),
1365 : static_cast<int>(nBufferRequestSize - nRemaining));
1366 126 : size_t nRet = (nBufferRequestSize - nRemaining) / nSize;
1367 126 : if (nRet < nMemb)
1368 60 : bEOF = true;
1369 :
1370 : // Give a chance to specialized filesystem to deal with errors to redirect
1371 : // elsewhere.
1372 90 : if (curOffsetOri == 0 && nRingBufferFileOffsetOri == 0 &&
1373 240 : !StopReceivingBytesOnError() && eExists == EXIST_NO &&
1374 24 : nRemaining < nBufferRequestSize)
1375 : {
1376 24 : const size_t nErrorBufferMaxSize = 4096;
1377 : GByte *pabyErrorBuffer =
1378 24 : static_cast<GByte *>(CPLMalloc(nErrorBufferMaxSize + 1));
1379 24 : size_t nRead = nBufferRequestSize - nRemaining;
1380 24 : size_t nErrorBufferSize = std::min(nErrorBufferMaxSize, nRead);
1381 24 : memcpy(pabyErrorBuffer, pBuffer, nErrorBufferSize);
1382 24 : if (nRead < nErrorBufferMaxSize)
1383 24 : nErrorBufferSize +=
1384 24 : Read(pabyErrorBuffer + nRead, 1, nErrorBufferMaxSize - nRead);
1385 24 : pabyErrorBuffer[nErrorBufferSize] = 0;
1386 24 : StopDownload();
1387 24 : if (CanRestartOnError(reinterpret_cast<char *>(pabyErrorBuffer),
1388 24 : reinterpret_cast<char *>(pabyHeaderData), true))
1389 : {
1390 4 : curOffset = 0;
1391 :
1392 4 : AcquireMutex();
1393 4 : eExists = EXIST_UNKNOWN;
1394 4 : bHasComputedFileSize = false;
1395 4 : fileSize = 0;
1396 4 : ReleaseMutex();
1397 4 : nCachedSize = 0;
1398 :
1399 4 : FileProp cachedFileProp;
1400 4 : m_poFS->GetCachedFileProp(m_pszURL, cachedFileProp);
1401 4 : cachedFileProp.bHasComputedFileSize = false;
1402 4 : cachedFileProp.fileSize = 0;
1403 4 : cachedFileProp.eExists = EXIST_UNKNOWN;
1404 4 : m_poFS->SetCachedFileProp(m_pszURL, cachedFileProp);
1405 :
1406 4 : goto retry;
1407 : }
1408 : else
1409 : {
1410 20 : CPLDebug("VSICURL", "Error buffer: %s",
1411 : reinterpret_cast<char *>(pabyErrorBuffer));
1412 20 : nRet = 0;
1413 : }
1414 :
1415 20 : CPLFree(pabyErrorBuffer);
1416 : }
1417 :
1418 122 : 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 118 : if (bErrorOccurred)
1441 3 : m_bError = true;
1442 :
1443 118 : return nRet;
1444 : }
1445 :
1446 : /************************************************************************/
1447 : /* AddRegion() */
1448 : /************************************************************************/
1449 :
1450 113 : void VSICurlStreamingHandle::AddRegion(vsi_l_offset nFileOffsetStart,
1451 : size_t nSize, GByte *pData)
1452 : {
1453 113 : if (nFileOffsetStart >= BKGND_BUFFER_SIZE)
1454 1 : return;
1455 :
1456 112 : if (pCachedData == nullptr)
1457 49 : 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 108 : 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 108 : memcpy(pCachedData + nFileOffsetStart, pData, nSz);
1469 108 : 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 29 : int VSICurlStreamingHandle::Close()
1526 : {
1527 29 : return 0;
1528 : }
1529 :
1530 : /************************************************************************/
1531 : /* VSICurlStreamingFSHandler() */
1532 : /************************************************************************/
1533 :
1534 7824 : VSICurlStreamingFSHandler::VSICurlStreamingFSHandler()
1535 7824 : : oCacheFileProp{100 * 1024}
1536 : {
1537 7824 : hMutex = CPLCreateMutex();
1538 7824 : CPLReleaseMutex(hMutex);
1539 7824 : }
1540 :
1541 : /************************************************************************/
1542 : /* ~VSICurlStreamingFSHandler() */
1543 : /************************************************************************/
1544 :
1545 6531 : VSICurlStreamingFSHandler::~VSICurlStreamingFSHandler()
1546 : {
1547 5598 : VSICurlStreamingFSHandler::ClearCache();
1548 :
1549 5598 : CPLDestroyMutex(hMutex);
1550 5598 : hMutex = nullptr;
1551 6531 : }
1552 :
1553 : /************************************************************************/
1554 : /* ClearCache() */
1555 : /************************************************************************/
1556 :
1557 7392 : void VSICurlStreamingFSHandler::ClearCache()
1558 : {
1559 14784 : CPLMutexHolder oHolder(&hMutex);
1560 :
1561 : {
1562 59 : const auto lambda = [](const lru11::KeyValuePair<std::string, bool> &kv)
1563 59 : { VSICURLInvalidateCachedFileProp(kv.key.c_str()); };
1564 7392 : oCacheFileProp.cwalk(lambda);
1565 7392 : oCacheFileProp.clear();
1566 : }
1567 7392 : }
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 9 : VSICurlStreamingFSHandler::CreateFileHandle(const char *pszFilename,
1593 : const char *pszURL)
1594 : {
1595 9 : return new VSICurlStreamingHandle(this, pszFilename, pszURL);
1596 : }
1597 :
1598 : /************************************************************************/
1599 : /* Open() */
1600 : /************************************************************************/
1601 :
1602 58 : VSIVirtualHandle *VSICurlStreamingFSHandler::Open(const char *pszFilename,
1603 : const char *pszAccess,
1604 : bool /* bSetError */,
1605 : CSLConstList papszOptions)
1606 : {
1607 58 : if (!STARTS_WITH_CI(pszFilename, GetFSPrefix()))
1608 2 : return nullptr;
1609 :
1610 56 : 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 56 : CreateFileHandle(pszFilename, pszFilename + GetFSPrefix().size());
1620 : // If we didn't get a filelist, check that the file really exists.
1621 56 : if (poHandle == nullptr || !poHandle->Exists(pszFilename, papszOptions))
1622 : {
1623 27 : delete poHandle;
1624 27 : return nullptr;
1625 : }
1626 :
1627 29 : if (CPLTestBool(CPLGetConfigOption("VSI_CACHE", "FALSE")))
1628 0 : return VSICreateCachedFile(poHandle);
1629 :
1630 29 : return poHandle;
1631 : }
1632 :
1633 : /************************************************************************/
1634 : /* GetCachedFileProp() */
1635 : /************************************************************************/
1636 :
1637 404 : bool VSICurlStreamingFSHandler::GetCachedFileProp(const char *pszURL,
1638 : FileProp &oFileProp)
1639 : {
1640 808 : CPLMutexHolder oHolder(&hMutex);
1641 : bool inCache;
1642 404 : if (oCacheFileProp.tryGet(std::string(pszURL), inCache))
1643 : {
1644 343 : if (VSICURLGetCachedFileProp(pszURL, oFileProp))
1645 : {
1646 343 : return true;
1647 : }
1648 0 : oCacheFileProp.remove(std::string(pszURL));
1649 : }
1650 61 : return false;
1651 : }
1652 :
1653 : /************************************************************************/
1654 : /* SetCachedFileProp() */
1655 : /************************************************************************/
1656 :
1657 404 : void VSICurlStreamingFSHandler::SetCachedFileProp(const char *pszURL,
1658 : FileProp &oFileProp)
1659 : {
1660 808 : CPLMutexHolder oHolder(&hMutex);
1661 404 : oCacheFileProp.insert(std::string(pszURL), true);
1662 404 : VSICURLSetCachedFileProp(pszURL, oFileProp);
1663 404 : }
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 64 : std::string VSICurlStreamingFSHandler::GetNonStreamingFilename(
1724 : const std::string &osFilename) const
1725 : {
1726 64 : if (STARTS_WITH(osFilename.c_str(), GetFSPrefix().c_str()))
1727 128 : return GetNonStreamingPrefix() +
1728 192 : 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 6520 : IVSIS3LikeStreamingFSHandler() = default;
1742 :
1743 1 : char **ReadDirEx(const char *pszDirname, int nMaxFiles) override
1744 : {
1745 1 : if (STARTS_WITH(pszDirname, GetFSPrefix()))
1746 : {
1747 1 : return VSIReadDirEx(
1748 2 : (GetNonStreamingPrefix() + (pszDirname + GetFSPrefix().size()))
1749 : .c_str(),
1750 1 : nMaxFiles);
1751 : }
1752 0 : return nullptr;
1753 : }
1754 :
1755 5 : const char *GetOptions() override
1756 : {
1757 5 : return VSIGetFileSystemOptions(GetNonStreamingPrefix().c_str());
1758 : }
1759 : };
1760 :
1761 : /************************************************************************/
1762 : /* VSIS3StreamingFSHandler */
1763 : /************************************************************************/
1764 :
1765 : class VSIS3StreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
1766 : {
1767 : CPL_DISALLOW_COPY_ASSIGN(VSIS3StreamingFSHandler)
1768 :
1769 : protected:
1770 228 : CPLString GetFSPrefix() const override
1771 : {
1772 228 : return "/vsis3_streaming/";
1773 : }
1774 :
1775 33 : std::string GetNonStreamingPrefix() const override
1776 : {
1777 33 : return "/vsis3/";
1778 : }
1779 :
1780 : VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
1781 : const char *pszURL) override;
1782 :
1783 : public:
1784 1304 : VSIS3StreamingFSHandler() = default;
1785 1866 : ~VSIS3StreamingFSHandler() override = default;
1786 :
1787 299 : void ClearCache() override
1788 : {
1789 299 : IVSIS3LikeStreamingFSHandler::ClearCache();
1790 299 : VSIS3UpdateParams::ClearCache();
1791 299 : }
1792 : };
1793 :
1794 : /************************************************************************/
1795 : /* VSIS3LikeStreamingHandle */
1796 : /************************************************************************/
1797 :
1798 : class VSIS3LikeStreamingHandle final : public VSICurlStreamingHandle
1799 : {
1800 : CPL_DISALLOW_COPY_ASSIGN(VSIS3LikeStreamingHandle)
1801 :
1802 : IVSIS3LikeHandleHelper *m_poS3HandleHelper = nullptr;
1803 :
1804 : protected:
1805 : struct curl_slist *
1806 : GetCurlHeaders(const CPLString &osVerb,
1807 : const struct curl_slist *psExistingHeaders) override;
1808 :
1809 71 : bool StopReceivingBytesOnError() override
1810 : {
1811 71 : return false;
1812 : }
1813 :
1814 : bool CanRestartOnError(const char *pszErrorMsg, const char *pszHeaders,
1815 : bool bSetError) override;
1816 :
1817 686 : bool InterpretRedirect() override
1818 : {
1819 686 : return false;
1820 : }
1821 :
1822 : public:
1823 : VSIS3LikeStreamingHandle(IVSIS3LikeStreamingFSHandler *poFS,
1824 : const char *pszFilename,
1825 : IVSIS3LikeHandleHelper *poS3HandleHelper);
1826 : ~VSIS3LikeStreamingHandle() override;
1827 : };
1828 :
1829 : /************************************************************************/
1830 : /* CreateFileHandle() */
1831 : /************************************************************************/
1832 :
1833 : VSICurlStreamingHandle *
1834 32 : VSIS3StreamingFSHandler::CreateFileHandle(const char *pszFilename,
1835 : const char *pszURL)
1836 : {
1837 : VSIS3HandleHelper *poS3HandleHelper =
1838 32 : VSIS3HandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str(), false);
1839 32 : if (poS3HandleHelper)
1840 : {
1841 : return new VSIS3LikeStreamingHandle(this, pszFilename,
1842 31 : poS3HandleHelper);
1843 : }
1844 1 : return nullptr;
1845 : }
1846 :
1847 : /************************************************************************/
1848 : /* VSIS3LikeStreamingHandle() */
1849 : /************************************************************************/
1850 :
1851 55 : VSIS3LikeStreamingHandle::VSIS3LikeStreamingHandle(
1852 : IVSIS3LikeStreamingFSHandler *poFS, const char *pszFilename,
1853 55 : IVSIS3LikeHandleHelper *poS3HandleHelper)
1854 : : VSICurlStreamingHandle(poFS, pszFilename,
1855 110 : poS3HandleHelper->GetURL().c_str()),
1856 55 : m_poS3HandleHelper(poS3HandleHelper)
1857 : {
1858 55 : }
1859 :
1860 : /************************************************************************/
1861 : /* ~VSIS3LikeStreamingHandle() */
1862 : /************************************************************************/
1863 :
1864 110 : VSIS3LikeStreamingHandle::~VSIS3LikeStreamingHandle()
1865 : {
1866 55 : delete m_poS3HandleHelper;
1867 110 : }
1868 :
1869 : /************************************************************************/
1870 : /* GetCurlHeaders() */
1871 : /************************************************************************/
1872 :
1873 55 : struct curl_slist *VSIS3LikeStreamingHandle::GetCurlHeaders(
1874 : const CPLString &osVerb, const struct curl_slist *psExistingHeaders)
1875 : {
1876 55 : return m_poS3HandleHelper->GetCurlHeaders(osVerb, psExistingHeaders);
1877 : }
1878 :
1879 : /************************************************************************/
1880 : /* CanRestartOnError() */
1881 : /************************************************************************/
1882 :
1883 24 : bool VSIS3LikeStreamingHandle::CanRestartOnError(const char *pszErrorMsg,
1884 : const char *pszHeaders,
1885 : bool bSetError)
1886 : {
1887 24 : if (m_poS3HandleHelper->CanRestartOnError(pszErrorMsg, pszHeaders,
1888 24 : bSetError))
1889 : {
1890 4 : SetURL(m_poS3HandleHelper->GetURL().c_str());
1891 4 : return true;
1892 : }
1893 20 : return false;
1894 : }
1895 :
1896 : /************************************************************************/
1897 : /* VSIGSStreamingFSHandler */
1898 : /************************************************************************/
1899 :
1900 : class VSIGSStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
1901 : {
1902 : protected:
1903 67 : CPLString GetFSPrefix() const override
1904 : {
1905 67 : return "/vsigs_streaming/";
1906 : }
1907 :
1908 10 : std::string GetNonStreamingPrefix() const override
1909 : {
1910 10 : return "/vsigs/";
1911 : }
1912 :
1913 : VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
1914 : const char *pszURL) override;
1915 :
1916 : public:
1917 1304 : VSIGSStreamingFSHandler()
1918 1304 : {
1919 1304 : }
1920 :
1921 1866 : ~VSIGSStreamingFSHandler() override
1922 933 : {
1923 1866 : }
1924 : };
1925 :
1926 : /************************************************************************/
1927 : /* CreateFileHandle() */
1928 : /************************************************************************/
1929 :
1930 : VSICurlStreamingHandle *
1931 10 : VSIGSStreamingFSHandler::CreateFileHandle(const char *pszFilename,
1932 : const char *pszURL)
1933 : {
1934 : VSIGSHandleHelper *poGCHandleHelper =
1935 10 : VSIGSHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str());
1936 10 : if (poGCHandleHelper)
1937 : {
1938 : return new VSIS3LikeStreamingHandle(this, pszFilename,
1939 9 : poGCHandleHelper);
1940 : }
1941 1 : return nullptr;
1942 : }
1943 :
1944 : /************************************************************************/
1945 : /* VSIAzureStreamingFSHandler */
1946 : /************************************************************************/
1947 :
1948 : class VSIAzureStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
1949 : {
1950 : protected:
1951 39 : CPLString GetFSPrefix() const override
1952 : {
1953 39 : return "/vsiaz_streaming/";
1954 : }
1955 :
1956 6 : std::string GetNonStreamingPrefix() const override
1957 : {
1958 6 : return "/vsiaz/";
1959 : }
1960 :
1961 : VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
1962 : const char *pszURL) override;
1963 :
1964 : public:
1965 1304 : VSIAzureStreamingFSHandler()
1966 1304 : {
1967 1304 : }
1968 :
1969 1866 : ~VSIAzureStreamingFSHandler() override
1970 933 : {
1971 1866 : }
1972 : };
1973 :
1974 : /************************************************************************/
1975 : /* CreateFileHandle() */
1976 : /************************************************************************/
1977 :
1978 : VSICurlStreamingHandle *
1979 6 : VSIAzureStreamingFSHandler::CreateFileHandle(const char *pszFilename,
1980 : const char *pszURL)
1981 : {
1982 : VSIAzureBlobHandleHelper *poHandleHelper =
1983 6 : VSIAzureBlobHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str());
1984 6 : if (poHandleHelper)
1985 : {
1986 5 : return new VSIS3LikeStreamingHandle(this, pszFilename, poHandleHelper);
1987 : }
1988 1 : return nullptr;
1989 : }
1990 :
1991 : /************************************************************************/
1992 : /* VSIOSSStreamingFSHandler */
1993 : /************************************************************************/
1994 :
1995 : class VSIOSSStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
1996 : {
1997 : CPL_DISALLOW_COPY_ASSIGN(VSIOSSStreamingFSHandler)
1998 :
1999 : protected:
2000 60 : CPLString GetFSPrefix() const override
2001 : {
2002 60 : return "/vsioss_streaming/";
2003 : }
2004 :
2005 9 : std::string GetNonStreamingPrefix() const override
2006 : {
2007 9 : return "/vsioss/";
2008 : }
2009 :
2010 : VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
2011 : const char *pszURL) override;
2012 :
2013 : public:
2014 1304 : VSIOSSStreamingFSHandler() = default;
2015 1866 : ~VSIOSSStreamingFSHandler() override = default;
2016 :
2017 299 : void ClearCache() override
2018 : {
2019 299 : IVSIS3LikeStreamingFSHandler::ClearCache();
2020 299 : VSIOSSUpdateParams::ClearCache();
2021 299 : }
2022 : };
2023 :
2024 : /************************************************************************/
2025 : /* CreateFileHandle() */
2026 : /************************************************************************/
2027 :
2028 : VSICurlStreamingHandle *
2029 9 : VSIOSSStreamingFSHandler::CreateFileHandle(const char *pszFilename,
2030 : const char *pszURL)
2031 : {
2032 : VSIOSSHandleHelper *poOSSHandleHelper =
2033 9 : VSIOSSHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str(), false);
2034 9 : if (poOSSHandleHelper)
2035 : {
2036 : return new VSIS3LikeStreamingHandle(this, pszFilename,
2037 8 : poOSSHandleHelper);
2038 : }
2039 1 : return nullptr;
2040 : }
2041 :
2042 : /************************************************************************/
2043 : /* VSISwiftStreamingFSHandler */
2044 : /************************************************************************/
2045 :
2046 : class VSISwiftStreamingFSHandler final : public IVSIS3LikeStreamingFSHandler
2047 : {
2048 : protected:
2049 18 : CPLString GetFSPrefix() const override
2050 : {
2051 18 : return "/vsiswift_streaming/";
2052 : }
2053 :
2054 3 : std::string GetNonStreamingPrefix() const override
2055 : {
2056 3 : return "/vsiswift/";
2057 : }
2058 :
2059 : VSICurlStreamingHandle *CreateFileHandle(const char *pszFilename,
2060 : const char *pszURL) override;
2061 :
2062 : public:
2063 1304 : VSISwiftStreamingFSHandler()
2064 1304 : {
2065 1304 : }
2066 :
2067 1866 : ~VSISwiftStreamingFSHandler() override
2068 933 : {
2069 1866 : }
2070 : };
2071 :
2072 : /************************************************************************/
2073 : /* CreateFileHandle() */
2074 : /************************************************************************/
2075 :
2076 : VSICurlStreamingHandle *
2077 3 : VSISwiftStreamingFSHandler::CreateFileHandle(const char *pszFilename,
2078 : const char *pszURL)
2079 : {
2080 : VSISwiftHandleHelper *poHandleHelper =
2081 3 : VSISwiftHandleHelper::BuildFromURI(pszURL, GetFSPrefix().c_str());
2082 3 : if (poHandleHelper)
2083 : {
2084 2 : return new VSIS3LikeStreamingHandle(this, pszFilename, poHandleHelper);
2085 : }
2086 1 : return nullptr;
2087 : }
2088 :
2089 : //! @endcond
2090 :
2091 : } /* namespace cpl */
2092 :
2093 : /************************************************************************/
2094 : /* VSIInstallCurlStreamingFileHandler() */
2095 : /************************************************************************/
2096 :
2097 : /*!
2098 : \brief Install /vsicurl_streaming/ HTTP/FTP file system handler (requires
2099 : libcurl).
2100 :
2101 : \verbatim embed:rst
2102 : See :ref:`/vsicurl_streaming/ documentation <vsicurl_streaming>`
2103 : \endverbatim
2104 :
2105 : @since GDAL 1.10
2106 : */
2107 1304 : void VSIInstallCurlStreamingFileHandler(void)
2108 : {
2109 1304 : VSIFileManager::InstallHandler("/vsicurl_streaming/",
2110 1304 : new cpl::VSICurlStreamingFSHandler);
2111 1304 : }
2112 :
2113 : /************************************************************************/
2114 : /* VSIInstallS3StreamingFileHandler() */
2115 : /************************************************************************/
2116 :
2117 : /*!
2118 : \brief Install /vsis3_streaming/ Amazon S3 file system handler (requires
2119 : libcurl).
2120 :
2121 : \verbatim embed:rst
2122 : See :ref:`/vsis3_streaming/ documentation <vsis3_streaming>`
2123 : \endverbatim
2124 :
2125 : @since GDAL 2.1
2126 : */
2127 1304 : void VSIInstallS3StreamingFileHandler(void)
2128 : {
2129 1304 : VSIFileManager::InstallHandler("/vsis3_streaming/",
2130 1304 : new cpl::VSIS3StreamingFSHandler);
2131 1304 : }
2132 :
2133 : /************************************************************************/
2134 : /* VSIInstallGSStreamingFileHandler() */
2135 : /************************************************************************/
2136 :
2137 : /*!
2138 : \brief Install /vsigs_streaming/ Google Cloud Storage file system handler
2139 : (requires libcurl)
2140 :
2141 : \verbatim embed:rst
2142 : See :ref:`/vsigs_streaming/ documentation <vsigs_streaming>`
2143 : \endverbatim
2144 :
2145 : @since GDAL 2.2
2146 : */
2147 :
2148 1304 : void VSIInstallGSStreamingFileHandler(void)
2149 : {
2150 1304 : VSIFileManager::InstallHandler("/vsigs_streaming/",
2151 1304 : new cpl::VSIGSStreamingFSHandler);
2152 1304 : }
2153 :
2154 : /************************************************************************/
2155 : /* VSIInstallAzureStreamingFileHandler() */
2156 : /************************************************************************/
2157 :
2158 : /*!
2159 : \brief Install /vsiaz_streaming/ Microsoft Azure Blob file system handler
2160 : (requires libcurl)
2161 :
2162 : \verbatim embed:rst
2163 : See :ref:`/vsiaz_streaming/ documentation <vsiaz_streaming>`
2164 : \endverbatim
2165 :
2166 : @since GDAL 2.3
2167 : */
2168 :
2169 1304 : void VSIInstallAzureStreamingFileHandler(void)
2170 : {
2171 1304 : VSIFileManager::InstallHandler("/vsiaz_streaming/",
2172 1304 : new cpl::VSIAzureStreamingFSHandler);
2173 1304 : }
2174 :
2175 : /************************************************************************/
2176 : /* VSIInstallOSSStreamingFileHandler() */
2177 : /************************************************************************/
2178 :
2179 : /*!
2180 : \brief Install /vsiaz_streaming/ Alibaba Cloud Object Storage Service (OSS)
2181 : (requires libcurl)
2182 :
2183 : \verbatim embed:rst
2184 : See :ref:`/vsioss_streaming/ documentation <vsioss_streaming>`
2185 : \endverbatim
2186 :
2187 : @since GDAL 2.3
2188 : */
2189 :
2190 1304 : void VSIInstallOSSStreamingFileHandler(void)
2191 : {
2192 1304 : VSIFileManager::InstallHandler("/vsioss_streaming/",
2193 1304 : new cpl::VSIOSSStreamingFSHandler);
2194 1304 : }
2195 :
2196 : /************************************************************************/
2197 : /* VSIInstallSwiftStreamingFileHandler() */
2198 : /************************************************************************/
2199 :
2200 : /*!
2201 : \brief Install /vsiswift_streaming/ OpenStack Swif Object Storage (Swift) file
2202 : system handler (requires libcurl)
2203 :
2204 : \verbatim embed:rst
2205 : See :ref:`/vsiswift_streaming/ documentation <vsiswift_streaming>`
2206 : \endverbatim
2207 :
2208 : @since GDAL 2.3
2209 : */
2210 :
2211 1304 : void VSIInstallSwiftStreamingFileHandler(void)
2212 : {
2213 1304 : VSIFileManager::InstallHandler("/vsiswift_streaming/",
2214 1304 : new cpl::VSISwiftStreamingFSHandler);
2215 1304 : }
2216 :
2217 : //! @cond Doxygen_Suppress
2218 :
2219 : /************************************************************************/
2220 : /* VSICurlStreamingClearCache() */
2221 : /************************************************************************/
2222 :
2223 299 : void VSICurlStreamingClearCache(void)
2224 : {
2225 : // FIXME ? Currently we have different filesystem instances for
2226 : // vsicurl/, /vsis3/, /vsigs/ . So each one has its own cache of regions.
2227 : // File properties cache are now shared
2228 299 : char **papszPrefix = VSIFileManager::GetPrefixes();
2229 8372 : for (size_t i = 0; papszPrefix && papszPrefix[i]; ++i)
2230 : {
2231 0 : auto poFSHandler = dynamic_cast<cpl::VSICurlStreamingFSHandler *>(
2232 8073 : VSIFileManager::GetHandler(papszPrefix[i]));
2233 :
2234 8073 : if (poFSHandler)
2235 1794 : poFSHandler->ClearCache();
2236 : }
2237 299 : CSLDestroy(papszPrefix);
2238 299 : }
2239 :
2240 : //! @endcond
2241 :
2242 : #undef ENABLE_DEBUG
2243 :
2244 : #endif // !defined(HAVE_CURL) || defined(CPL_MULTIPROC_STUB)
|