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