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