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