Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: CPL - Common Portability Library
4 : * Purpose: Implement VSI large file api for WebHDFS REST API
5 : * Author: Even Rouault, even.rouault at spatialys.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2018, Even Rouault <even.rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "cpl_http.h"
15 : #include "cpl_json.h"
16 : #include "cpl_vsil_curl_priv.h"
17 : #include "cpl_vsil_curl_class.h"
18 :
19 : #include <errno.h>
20 :
21 : #include <algorithm>
22 : #include <set>
23 : #include <map>
24 : #include <memory>
25 :
26 : #include "cpl_alibaba_oss.h"
27 :
28 : #ifndef HAVE_CURL
29 :
30 : void VSIInstallWebHdfsHandler(void)
31 : {
32 : // Not supported
33 : }
34 :
35 : #else
36 :
37 : //! @cond Doxygen_Suppress
38 : #ifndef DOXYGEN_SKIP
39 :
40 : #define ENABLE_DEBUG 0
41 :
42 : #define unchecked_curl_easy_setopt(handle, opt, param) \
43 : CPL_IGNORE_RET_VAL(curl_easy_setopt(handle, opt, param))
44 :
45 : namespace cpl
46 : {
47 :
48 : /************************************************************************/
49 : /* VSIWebHDFSFSHandler */
50 : /************************************************************************/
51 :
52 : class VSIWebHDFSFSHandler final : public VSICurlFilesystemHandlerBaseWritable
53 : {
54 : const std::string m_osPrefix;
55 : CPL_DISALLOW_COPY_ASSIGN(VSIWebHDFSFSHandler)
56 :
57 : protected:
58 : VSICurlHandle *CreateFileHandle(const char *pszFilename) override;
59 :
60 0 : int HasOptimizedReadMultiRange(const char * /* pszPath */) override
61 : {
62 0 : return false;
63 : }
64 :
65 : char **GetFileList(const char *pszFilename, int nMaxFiles,
66 : bool *pbGotFileList) override;
67 :
68 : std::string
69 : GetURLFromFilename(const std::string &osFilename) const override;
70 :
71 : VSIVirtualHandleUniquePtr
72 : CreateWriteHandle(const char *pszFilename,
73 : CSLConstList papszOptions) override;
74 :
75 : public:
76 2101 : explicit VSIWebHDFSFSHandler(const char *pszPrefix) : m_osPrefix(pszPrefix)
77 : {
78 2101 : }
79 :
80 1303 : ~VSIWebHDFSFSHandler() override = default;
81 :
82 : int Unlink(const char *pszFilename) override;
83 : int Rmdir(const char *pszFilename) override;
84 : int Mkdir(const char *pszDirname, long nMode) override;
85 :
86 1 : const char *GetDebugKey() const override
87 : {
88 1 : return "VSIWEBHDFS";
89 : }
90 :
91 162 : std::string GetFSPrefix() const override
92 : {
93 162 : return m_osPrefix;
94 : }
95 :
96 : const char *GetOptions() override;
97 :
98 : std::string
99 0 : GetStreamingFilename(const std::string &osFilename) const override
100 : {
101 0 : return osFilename;
102 : }
103 :
104 0 : VSIFilesystemHandler *Duplicate(const char *pszPrefix) override
105 : {
106 0 : return new VSIWebHDFSFSHandler(pszPrefix);
107 : }
108 : };
109 :
110 : /************************************************************************/
111 : /* VSIWebHDFSHandle */
112 : /************************************************************************/
113 :
114 : class VSIWebHDFSHandle final : public VSICurlHandle
115 : {
116 : CPL_DISALLOW_COPY_ASSIGN(VSIWebHDFSHandle)
117 :
118 : std::string m_osDataNodeHost{};
119 : std::string m_osUsernameParam{};
120 : std::string m_osDelegationParam{};
121 :
122 : std::string DownloadRegion(vsi_l_offset startOffset, int nBlocks) override;
123 :
124 : public:
125 : VSIWebHDFSHandle(VSIWebHDFSFSHandler *poFS, const char *pszFilename,
126 : const char *pszURL);
127 14 : ~VSIWebHDFSHandle() override = default;
128 :
129 0 : int ReadMultiRange(int nRanges, void **ppData,
130 : const vsi_l_offset *panOffsets,
131 : const size_t *panSizes) override
132 : {
133 0 : return VSIVirtualHandle::ReadMultiRange(nRanges, ppData, panOffsets,
134 0 : panSizes);
135 : }
136 :
137 : vsi_l_offset GetFileSize(bool bSetError) override;
138 : };
139 :
140 : /************************************************************************/
141 : /* PatchWebHDFSUrl() */
142 : /************************************************************************/
143 :
144 6 : static std::string PatchWebHDFSUrl(const std::string &osURLIn,
145 : const std::string &osNewHost)
146 : {
147 6 : std::string osURL(osURLIn);
148 6 : size_t nStart = 0;
149 6 : if (STARTS_WITH(osURL.c_str(), "http://"))
150 6 : nStart = strlen("http://");
151 0 : else if (STARTS_WITH(osURL.c_str(), "https://"))
152 0 : nStart = strlen("https://");
153 6 : if (nStart)
154 : {
155 6 : size_t nHostEnd = osURL.find(':', nStart);
156 6 : if (nHostEnd != std::string::npos)
157 : {
158 : osURL =
159 6 : osURL.substr(0, nStart) + osNewHost + osURL.substr(nHostEnd);
160 : }
161 : }
162 6 : return osURL;
163 : }
164 :
165 : /************************************************************************/
166 : /* GetWebHDFSDataNodeHost() */
167 : /************************************************************************/
168 :
169 13 : static std::string GetWebHDFSDataNodeHost(const char *pszFilename)
170 : {
171 : return std::string(
172 13 : VSIGetPathSpecificOption(pszFilename, "WEBHDFS_DATANODE_HOST", ""));
173 : }
174 :
175 : /************************************************************************/
176 : /* VSIWebHDFSWriteHandle */
177 : /************************************************************************/
178 :
179 : class VSIWebHDFSWriteHandle final : public VSIAppendWriteHandle
180 : {
181 : CPL_DISALLOW_COPY_ASSIGN(VSIWebHDFSWriteHandle)
182 :
183 : std::string m_osURL{};
184 : std::string m_osDataNodeHost{};
185 : std::string m_osUsernameParam{};
186 : std::string m_osDelegationParam{};
187 : CPLStringList m_aosHTTPOptions{};
188 : RunThreadUser m_threadUser;
189 :
190 : bool Send(bool bIsLastBlock) override;
191 : bool CreateFile();
192 : bool Append();
193 :
194 : void InvalidateParentDirectory();
195 :
196 : public:
197 : VSIWebHDFSWriteHandle(VSIWebHDFSFSHandler *poFS, const char *pszFilename);
198 : ~VSIWebHDFSWriteHandle() override;
199 : };
200 :
201 : /************************************************************************/
202 : /* GetWebHDFSBufferSize() */
203 : /************************************************************************/
204 :
205 6 : static int GetWebHDFSBufferSize()
206 : {
207 : int nBufferSize;
208 6 : int nChunkSizeMB = atoi(CPLGetConfigOption("VSIWEBHDFS_SIZE", "4"));
209 6 : if (nChunkSizeMB <= 0 || nChunkSizeMB > 1000)
210 0 : nBufferSize = 4 * 1024 * 1024;
211 : else
212 6 : nBufferSize = nChunkSizeMB * 1024 * 1024;
213 :
214 : // For testing only !
215 : const char *pszChunkSizeBytes =
216 6 : CPLGetConfigOption("VSIWEBHDFS_SIZE_BYTES", nullptr);
217 6 : if (pszChunkSizeBytes)
218 0 : nBufferSize = atoi(pszChunkSizeBytes);
219 6 : if (nBufferSize <= 0 || nBufferSize > 1000 * 1024 * 1024)
220 0 : nBufferSize = 4 * 1024 * 1024;
221 6 : return nBufferSize;
222 : }
223 :
224 : /************************************************************************/
225 : /* VSIWebHDFSWriteHandle() */
226 : /************************************************************************/
227 :
228 6 : VSIWebHDFSWriteHandle::VSIWebHDFSWriteHandle(VSIWebHDFSFSHandler *poFS,
229 6 : const char *pszFilename)
230 6 : : VSIAppendWriteHandle(poFS, poFS->GetFSPrefix().c_str(), pszFilename,
231 : GetWebHDFSBufferSize()),
232 12 : m_osURL(pszFilename + poFS->GetFSPrefix().size()),
233 : m_osDataNodeHost(GetWebHDFSDataNodeHost(pszFilename)),
234 : m_aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszFilename)),
235 24 : m_threadUser(*poFS)
236 : {
237 : // cppcheck-suppress useInitializationList
238 : m_osUsernameParam =
239 6 : VSIGetPathSpecificOption(pszFilename, "WEBHDFS_USERNAME", "");
240 6 : if (!m_osUsernameParam.empty())
241 5 : m_osUsernameParam = "&user.name=" + m_osUsernameParam;
242 : m_osDelegationParam =
243 6 : VSIGetPathSpecificOption(pszFilename, "WEBHDFS_DELEGATION", "");
244 6 : if (!m_osDelegationParam.empty())
245 0 : m_osDelegationParam = "&delegation=" + m_osDelegationParam;
246 :
247 6 : if (m_pabyBuffer != nullptr && !CreateFile())
248 : {
249 3 : CPLFree(m_pabyBuffer);
250 3 : m_pabyBuffer = nullptr;
251 : }
252 6 : }
253 :
254 : /************************************************************************/
255 : /* ~VSIWebHDFSWriteHandle() */
256 : /************************************************************************/
257 :
258 12 : VSIWebHDFSWriteHandle::~VSIWebHDFSWriteHandle()
259 : {
260 6 : Close();
261 12 : }
262 :
263 : /************************************************************************/
264 : /* InvalidateParentDirectory() */
265 : /************************************************************************/
266 :
267 3 : void VSIWebHDFSWriteHandle::InvalidateParentDirectory()
268 : {
269 3 : m_poFS->InvalidateCachedData(m_osURL.c_str());
270 :
271 3 : std::string osFilenameWithoutSlash(m_osFilename);
272 3 : if (!osFilenameWithoutSlash.empty() && osFilenameWithoutSlash.back() == '/')
273 0 : osFilenameWithoutSlash.pop_back();
274 3 : m_poFS->InvalidateDirContent(
275 6 : CPLGetDirnameSafe(osFilenameWithoutSlash.c_str()));
276 3 : }
277 :
278 : /************************************************************************/
279 : /* Send() */
280 : /************************************************************************/
281 :
282 6 : bool VSIWebHDFSWriteHandle::Send(bool /* bIsLastBlock */)
283 : {
284 6 : if (m_nCurOffset > 0)
285 2 : return Append();
286 4 : return true;
287 : }
288 :
289 : /************************************************************************/
290 : /* CreateFile() */
291 : /************************************************************************/
292 :
293 6 : bool VSIWebHDFSWriteHandle::CreateFile()
294 : {
295 6 : if (m_osUsernameParam.empty() && m_osDelegationParam.empty())
296 : {
297 1 : CPLError(CE_Failure, CPLE_AppDefined,
298 : "Configuration option WEBHDFS_USERNAME or WEBHDFS_DELEGATION "
299 : "should be defined");
300 1 : return false;
301 : }
302 :
303 10 : NetworkStatisticsFileSystem oContextFS(m_poFS->GetFSPrefix().c_str());
304 10 : NetworkStatisticsFile oContextFile(m_osFilename.c_str());
305 10 : NetworkStatisticsAction oContextAction("Write");
306 :
307 10 : std::string osURL = m_osURL + "?op=CREATE&overwrite=true" +
308 15 : m_osUsernameParam + m_osDelegationParam;
309 :
310 : std::string osPermission = VSIGetPathSpecificOption(
311 10 : m_osFilename.c_str(), "WEBHDFS_PERMISSION", "");
312 5 : if (!osPermission.empty())
313 0 : osURL += "&permission=" + osPermission;
314 :
315 : std::string osReplication = VSIGetPathSpecificOption(
316 10 : m_osFilename.c_str(), "WEBHDFS_REPLICATION", "");
317 5 : if (!osReplication.empty())
318 0 : osURL += "&replication=" + osReplication;
319 :
320 5 : bool bInRedirect = false;
321 :
322 5 : RunThreadUser runThreadUser(*m_poFS);
323 8 : retry:
324 8 : CURL *hCurlHandle = curl_easy_init();
325 :
326 : struct curl_slist *headers = static_cast<struct curl_slist *>(
327 8 : CPLHTTPSetOptions(hCurlHandle, osURL.c_str(), m_aosHTTPOptions.List()));
328 :
329 8 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "PUT");
330 8 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_INFILESIZE, 0);
331 :
332 8 : if (!m_osDataNodeHost.empty())
333 : {
334 7 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_FOLLOWLOCATION, 0);
335 : }
336 :
337 8 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
338 :
339 8 : WriteFuncStruct sWriteFuncData;
340 8 : VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
341 8 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
342 8 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
343 : VSICurlHandleWriteFunc);
344 :
345 8 : m_poFS->Perform(hCurlHandle);
346 :
347 8 : curl_slist_free_all(headers);
348 :
349 8 : NetworkStatisticsLogger::LogPUT(0);
350 :
351 8 : long response_code = 0;
352 8 : curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
353 :
354 8 : if (!bInRedirect)
355 : {
356 5 : char *pszRedirectURL = nullptr;
357 5 : curl_easy_getinfo(hCurlHandle, CURLINFO_REDIRECT_URL, &pszRedirectURL);
358 5 : if (pszRedirectURL && strstr(pszRedirectURL, osURL.c_str()) == nullptr)
359 : {
360 3 : CPLDebug("WEBHDFS", "Redirect URL: %s", pszRedirectURL);
361 :
362 3 : bInRedirect = true;
363 3 : osURL = pszRedirectURL;
364 3 : if (!m_osDataNodeHost.empty())
365 : {
366 3 : osURL = PatchWebHDFSUrl(osURL, m_osDataNodeHost);
367 : }
368 :
369 3 : curl_easy_cleanup(hCurlHandle);
370 3 : CPLFree(sWriteFuncData.pBuffer);
371 :
372 3 : goto retry;
373 : }
374 : }
375 :
376 5 : curl_easy_cleanup(hCurlHandle);
377 :
378 5 : if (response_code == 201)
379 : {
380 3 : InvalidateParentDirectory();
381 : }
382 : else
383 : {
384 2 : CPLDebug("WEBHDFS", "%s",
385 2 : sWriteFuncData.pBuffer ? sWriteFuncData.pBuffer : "(null)");
386 2 : CPLError(CE_Failure, CPLE_AppDefined, "PUT of %s failed",
387 : m_osURL.c_str());
388 : }
389 5 : CPLFree(sWriteFuncData.pBuffer);
390 :
391 5 : return response_code == 201;
392 : }
393 :
394 : /************************************************************************/
395 : /* Append() */
396 : /************************************************************************/
397 :
398 2 : bool VSIWebHDFSWriteHandle::Append()
399 : {
400 4 : RunThreadUser threadUser(*m_poFS);
401 :
402 4 : NetworkStatisticsFileSystem oContextFS(m_poFS->GetFSPrefix().c_str());
403 4 : NetworkStatisticsFile oContextFile(m_osFilename.c_str());
404 4 : NetworkStatisticsAction oContextAction("Write");
405 :
406 : std::string osURL =
407 6 : m_osURL + "?op=APPEND" + m_osUsernameParam + m_osDelegationParam;
408 :
409 2 : CURL *hCurlHandle = curl_easy_init();
410 :
411 : struct curl_slist *headers = static_cast<struct curl_slist *>(
412 2 : CPLHTTPSetOptions(hCurlHandle, osURL.c_str(), m_aosHTTPOptions.List()));
413 :
414 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "POST");
415 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_FOLLOWLOCATION, 0);
416 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
417 :
418 2 : WriteFuncStruct sWriteFuncData;
419 2 : VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
420 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
421 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
422 : VSICurlHandleWriteFunc);
423 :
424 2 : m_poFS->Perform(hCurlHandle);
425 :
426 2 : curl_slist_free_all(headers);
427 :
428 2 : NetworkStatisticsLogger::LogPOST(0, 0);
429 :
430 2 : long response_code = 0;
431 2 : curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
432 :
433 2 : if (response_code != 307)
434 : {
435 0 : CPLDebug("WEBHDFS", "%s",
436 0 : sWriteFuncData.pBuffer ? sWriteFuncData.pBuffer : "(null)");
437 0 : CPLError(CE_Failure, CPLE_AppDefined, "POST of %s failed",
438 : m_osURL.c_str());
439 0 : curl_easy_cleanup(hCurlHandle);
440 0 : CPLFree(sWriteFuncData.pBuffer);
441 0 : return false;
442 : }
443 :
444 2 : char *pszRedirectURL = nullptr;
445 2 : curl_easy_getinfo(hCurlHandle, CURLINFO_REDIRECT_URL, &pszRedirectURL);
446 2 : if (pszRedirectURL == nullptr)
447 : {
448 0 : curl_easy_cleanup(hCurlHandle);
449 0 : CPLFree(sWriteFuncData.pBuffer);
450 0 : return false;
451 : }
452 2 : CPLDebug("WEBHDFS", "Redirect URL: %s", pszRedirectURL);
453 :
454 2 : osURL = pszRedirectURL;
455 2 : if (!m_osDataNodeHost.empty())
456 : {
457 2 : osURL = PatchWebHDFSUrl(osURL, m_osDataNodeHost);
458 : }
459 :
460 2 : curl_easy_cleanup(hCurlHandle);
461 2 : CPLFree(sWriteFuncData.pBuffer);
462 :
463 : // After redirection
464 :
465 2 : hCurlHandle = curl_easy_init();
466 :
467 : headers = static_cast<struct curl_slist *>(
468 2 : CPLHTTPSetOptions(hCurlHandle, osURL.c_str(), m_aosHTTPOptions.List()));
469 : headers =
470 2 : curl_slist_append(headers, "Content-Type: application/octet-stream");
471 :
472 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_POSTFIELDS, m_pabyBuffer);
473 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_POSTFIELDSIZE,
474 : m_nBufferOff);
475 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_FOLLOWLOCATION, 0);
476 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
477 :
478 2 : VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
479 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
480 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
481 : VSICurlHandleWriteFunc);
482 :
483 2 : m_poFS->Perform(hCurlHandle);
484 :
485 2 : curl_slist_free_all(headers);
486 :
487 2 : NetworkStatisticsLogger::LogPOST(m_nBufferOff, 0);
488 :
489 2 : response_code = 0;
490 2 : curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
491 :
492 2 : curl_easy_cleanup(hCurlHandle);
493 :
494 2 : if (response_code != 200)
495 : {
496 1 : CPLDebug("WEBHDFS", "%s",
497 1 : sWriteFuncData.pBuffer ? sWriteFuncData.pBuffer : "(null)");
498 1 : CPLError(CE_Failure, CPLE_AppDefined, "POST of %s failed",
499 : m_osURL.c_str());
500 : }
501 2 : CPLFree(sWriteFuncData.pBuffer);
502 :
503 2 : return response_code == 200;
504 : }
505 :
506 : /************************************************************************/
507 : /* CreateWriteHandle() */
508 : /************************************************************************/
509 :
510 : VSIVirtualHandleUniquePtr
511 6 : VSIWebHDFSFSHandler::CreateWriteHandle(const char *pszFilename,
512 : CSLConstList /*papszOptions*/)
513 : {
514 12 : auto poHandle = std::make_unique<VSIWebHDFSWriteHandle>(this, pszFilename);
515 6 : if (!poHandle->IsOK())
516 : {
517 3 : return nullptr;
518 : }
519 3 : return VSIVirtualHandleUniquePtr(poHandle.release());
520 : }
521 :
522 : /************************************************************************/
523 : /* GetOptions() */
524 : /************************************************************************/
525 :
526 1 : const char *VSIWebHDFSFSHandler::GetOptions()
527 : {
528 : static std::string osOptions(
529 2 : std::string("<Options>") +
530 : " <Option name='WEBHDFS_USERNAME' type='string' "
531 : "description='username (when security is off)'/>"
532 : " <Option name='WEBHDFS_DELEGATION' type='string' "
533 : "description='Hadoop delegation token (when security is on)'/>"
534 : " <Option name='WEBHDFS_DATANODE_HOST' type='string' "
535 : "description='For APIs using redirect, substitute the redirection "
536 : "hostname with the one provided by this option (normally resolvable "
537 : "hostname should be rewritten by a proxy)'/>"
538 : " <Option name='WEBHDFS_REPLICATION' type='integer' "
539 : "description='Replication value used when creating a file'/>"
540 : " <Option name='WEBHDFS_PERMISSION' type='integer' "
541 : "description='Permission mask (to provide as decimal number) when "
542 3 : "creating a file or directory'/>" +
543 2 : VSICurlFilesystemHandlerBase::GetOptionsStatic() + "</Options>");
544 1 : return osOptions.c_str();
545 : }
546 :
547 : /************************************************************************/
548 : /* CreateFileHandle() */
549 : /************************************************************************/
550 :
551 7 : VSICurlHandle *VSIWebHDFSFSHandler::CreateFileHandle(const char *pszFilename)
552 : {
553 : return new VSIWebHDFSHandle(this, pszFilename,
554 7 : pszFilename + GetFSPrefix().size());
555 : }
556 :
557 : /************************************************************************/
558 : /* GetURLFromFilename() */
559 : /************************************************************************/
560 :
561 : std::string
562 20 : VSIWebHDFSFSHandler::GetURLFromFilename(const std::string &osFilename) const
563 : {
564 40 : return osFilename.substr(GetFSPrefix().size());
565 : }
566 :
567 : /************************************************************************/
568 : /* GetFileList() */
569 : /************************************************************************/
570 :
571 2 : char **VSIWebHDFSFSHandler::GetFileList(const char *pszDirname,
572 : int /*nMaxFiles*/, bool *pbGotFileList)
573 : {
574 : if (ENABLE_DEBUG)
575 : CPLDebug("WEBHDFS", "GetFileList(%s)", pszDirname);
576 2 : *pbGotFileList = false;
577 :
578 4 : NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
579 4 : NetworkStatisticsAction oContextAction("ListBucket");
580 :
581 2 : CPLAssert(strlen(pszDirname) >= GetFSPrefix().size());
582 :
583 6 : std::string osBaseURL = pszDirname + GetFSPrefix().size();
584 2 : if (!osBaseURL.empty() && osBaseURL.back() != '/')
585 2 : osBaseURL += '/';
586 :
587 : //RAII for CURL run thread.
588 4 : RunThreadUser threadUser(*this);
589 :
590 : std::string osUsernameParam =
591 4 : VSIGetPathSpecificOption(pszDirname, "WEBHDFS_USERNAME", "");
592 2 : if (!osUsernameParam.empty())
593 0 : osUsernameParam = "&user.name=" + osUsernameParam;
594 : std::string osDelegationParam =
595 4 : VSIGetPathSpecificOption(pszDirname, "WEBHDFS_DELEGATION", "");
596 2 : if (!osDelegationParam.empty())
597 0 : osDelegationParam = "&delegation=" + osDelegationParam;
598 : std::string osURL =
599 6 : osBaseURL + "?op=LISTSTATUS" + osUsernameParam + osDelegationParam;
600 4 : const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszDirname));
601 :
602 2 : CURL *hCurlHandle = curl_easy_init();
603 :
604 : struct curl_slist *headers =
605 2 : SetOptions(hCurlHandle, osURL.c_str(), aosHTTPOptions.List());
606 :
607 2 : WriteFuncStruct sWriteFuncData;
608 2 : VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
609 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
610 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
611 : VSICurlHandleWriteFunc);
612 :
613 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
614 :
615 2 : Perform(hCurlHandle);
616 :
617 2 : VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
618 :
619 2 : curl_slist_free_all(headers);
620 :
621 2 : NetworkStatisticsLogger::LogGET(sWriteFuncData.nSize);
622 :
623 2 : long response_code = 0;
624 2 : curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
625 :
626 4 : CPLStringList aosList;
627 2 : bool bOK = false;
628 2 : if (response_code == 200 && sWriteFuncData.pBuffer)
629 : {
630 2 : CPLJSONDocument oDoc;
631 1 : if (oDoc.LoadMemory(
632 1 : reinterpret_cast<const GByte *>(sWriteFuncData.pBuffer)))
633 : {
634 : CPLJSONArray oFileStatus =
635 3 : oDoc.GetRoot().GetArray("FileStatuses/FileStatus");
636 1 : bOK = oFileStatus.IsValid();
637 3 : for (int i = 0; i < oFileStatus.Size(); i++)
638 : {
639 2 : CPLJSONObject oItem = oFileStatus[i];
640 2 : vsi_l_offset fileSize = oItem.GetLong("length");
641 : size_t mTime = static_cast<size_t>(
642 2 : oItem.GetLong("modificationTime") / 1000);
643 2 : bool bIsDirectory = oItem.GetString("type") == "DIRECTORY";
644 4 : std::string osName = oItem.GetString("pathSuffix");
645 : // can be empty if we for example ask to list a file: in that
646 : // case the file entry is reported but with an empty pathSuffix
647 2 : if (!osName.empty())
648 : {
649 2 : if (CPLHasUnbalancedPathTraversal(osName.c_str()))
650 : {
651 0 : CPLError(CE_Warning, CPLE_AppDefined,
652 : "Ignoring pathSuffix '%s' that has a path "
653 : "traversal pattern",
654 : osName.c_str());
655 0 : continue;
656 : }
657 2 : aosList.AddString(osName.c_str());
658 :
659 4 : FileProp prop;
660 2 : prop.eExists = EXIST_YES;
661 2 : prop.bIsDirectory = bIsDirectory;
662 2 : prop.bHasComputedFileSize = true;
663 2 : prop.fileSize = fileSize;
664 2 : prop.mTime = mTime;
665 4 : std::string osCachedFilename(osBaseURL + osName);
666 : #if DEBUG_VERBOSE
667 : CPLDebug("WEBHDFS", "Cache %s", osCachedFilename.c_str());
668 : #endif
669 2 : SetCachedFileProp(osCachedFilename.c_str(), prop);
670 : }
671 : }
672 : }
673 : }
674 :
675 2 : *pbGotFileList = bOK;
676 :
677 2 : CPLFree(sWriteFuncData.pBuffer);
678 2 : curl_easy_cleanup(hCurlHandle);
679 :
680 2 : if (bOK)
681 1 : return aosList.StealList();
682 : else
683 1 : return nullptr;
684 : }
685 :
686 : /************************************************************************/
687 : /* Unlink() */
688 : /************************************************************************/
689 :
690 7 : int VSIWebHDFSFSHandler::Unlink(const char *pszFilename)
691 : {
692 7 : if (!STARTS_WITH_CI(pszFilename, GetFSPrefix().c_str()))
693 1 : return -1;
694 :
695 12 : NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
696 12 : NetworkStatisticsAction oContextAction("Unlink");
697 :
698 18 : std::string osBaseURL = GetURLFromFilename(pszFilename);
699 :
700 12 : RunThreadUser threadUser(*this);
701 :
702 : std::string osUsernameParam =
703 12 : VSIGetPathSpecificOption(pszFilename, "WEBHDFS_USERNAME", "");
704 6 : if (!osUsernameParam.empty())
705 1 : osUsernameParam = "&user.name=" + osUsernameParam;
706 : std::string osDelegationParam =
707 12 : VSIGetPathSpecificOption(pszFilename, "WEBHDFS_DELEGATION", "");
708 6 : if (!osDelegationParam.empty())
709 1 : osDelegationParam = "&delegation=" + osDelegationParam;
710 : std::string osURL =
711 18 : osBaseURL + "?op=DELETE" + osUsernameParam + osDelegationParam;
712 12 : const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszFilename));
713 :
714 6 : CURL *hCurlHandle = curl_easy_init();
715 :
716 6 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "DELETE");
717 :
718 : struct curl_slist *headers =
719 6 : SetOptions(hCurlHandle, osURL.c_str(), aosHTTPOptions.List());
720 :
721 6 : WriteFuncStruct sWriteFuncData;
722 6 : VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
723 6 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
724 6 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
725 : VSICurlHandleWriteFunc);
726 :
727 6 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
728 :
729 6 : Perform(hCurlHandle);
730 :
731 6 : VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
732 :
733 6 : curl_slist_free_all(headers);
734 :
735 6 : NetworkStatisticsLogger::LogDELETE();
736 :
737 6 : long response_code = 0;
738 6 : curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
739 :
740 6 : CPLStringList aosList;
741 6 : bool bOK = false;
742 6 : if (response_code == 200 && sWriteFuncData.pBuffer)
743 : {
744 8 : CPLJSONDocument oDoc;
745 4 : if (oDoc.LoadMemory(
746 4 : reinterpret_cast<const GByte *>(sWriteFuncData.pBuffer)))
747 : {
748 4 : bOK = oDoc.GetRoot().GetBool("boolean");
749 : }
750 : }
751 6 : if (bOK)
752 : {
753 3 : InvalidateCachedData(osBaseURL.c_str());
754 :
755 3 : std::string osFilenameWithoutSlash(pszFilename);
756 6 : if (!osFilenameWithoutSlash.empty() &&
757 3 : osFilenameWithoutSlash.back() == '/')
758 0 : osFilenameWithoutSlash.pop_back();
759 :
760 3 : InvalidateDirContent(CPLGetDirnameSafe(osFilenameWithoutSlash.c_str()));
761 : }
762 : else
763 : {
764 3 : CPLDebug("WEBHDFS", "%s",
765 3 : sWriteFuncData.pBuffer ? sWriteFuncData.pBuffer : "(null)");
766 : }
767 :
768 6 : CPLFree(sWriteFuncData.pBuffer);
769 6 : curl_easy_cleanup(hCurlHandle);
770 :
771 6 : return bOK ? 0 : -1;
772 : }
773 :
774 : /************************************************************************/
775 : /* Rmdir() */
776 : /************************************************************************/
777 :
778 3 : int VSIWebHDFSFSHandler::Rmdir(const char *pszFilename)
779 : {
780 6 : NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
781 6 : NetworkStatisticsAction oContextAction("Rmdir");
782 :
783 6 : return Unlink(pszFilename);
784 : }
785 :
786 : /************************************************************************/
787 : /* Mkdir() */
788 : /************************************************************************/
789 :
790 5 : int VSIWebHDFSFSHandler::Mkdir(const char *pszDirname, long nMode)
791 : {
792 5 : if (!STARTS_WITH_CI(pszDirname, GetFSPrefix().c_str()))
793 1 : return -1;
794 :
795 8 : std::string osDirnameWithoutEndSlash(pszDirname);
796 8 : if (!osDirnameWithoutEndSlash.empty() &&
797 4 : osDirnameWithoutEndSlash.back() == '/')
798 : {
799 2 : osDirnameWithoutEndSlash.pop_back();
800 : }
801 :
802 4 : if (osDirnameWithoutEndSlash.find("/webhdfs/v1") ==
803 5 : osDirnameWithoutEndSlash.size() - strlen("/webhdfs/v1") &&
804 1 : std::count(osDirnameWithoutEndSlash.begin(),
805 5 : osDirnameWithoutEndSlash.end(), '/') == 6)
806 : {
807 : // The server does weird things (creating a webhdfs/v1 subfolder)
808 : // if we provide the root directory like
809 : // /vsiwebhdfs/http://localhost:50070/webhdfs/v1
810 1 : return -1;
811 : }
812 :
813 6 : NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
814 6 : NetworkStatisticsAction oContextAction("Mkdir");
815 :
816 : std::string osBaseURL =
817 9 : GetURLFromFilename(osDirnameWithoutEndSlash.c_str());
818 :
819 : // RAII for CURL perform thread.
820 6 : RunThreadUser threadUser(*this);
821 :
822 : std::string osUsernameParam =
823 6 : VSIGetPathSpecificOption(pszDirname, "WEBHDFS_USERNAME", "");
824 3 : if (!osUsernameParam.empty())
825 1 : osUsernameParam = "&user.name=" + osUsernameParam;
826 : std::string osDelegationParam =
827 6 : VSIGetPathSpecificOption(pszDirname, "WEBHDFS_DELEGATION", "");
828 3 : if (!osDelegationParam.empty())
829 1 : osDelegationParam = "&delegation=" + osDelegationParam;
830 : std::string osURL =
831 9 : osBaseURL + "?op=MKDIRS" + osUsernameParam + osDelegationParam;
832 3 : if (nMode)
833 : {
834 1 : osURL += "&permission=";
835 1 : osURL += CPLSPrintf("%o", static_cast<int>(nMode));
836 : }
837 6 : const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszDirname));
838 :
839 3 : CURL *hCurlHandle = curl_easy_init();
840 :
841 3 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "PUT");
842 :
843 : struct curl_slist *headers =
844 3 : SetOptions(hCurlHandle, osURL.c_str(), aosHTTPOptions.List());
845 :
846 3 : WriteFuncStruct sWriteFuncData;
847 3 : VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
848 3 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
849 3 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
850 : VSICurlHandleWriteFunc);
851 :
852 3 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
853 :
854 3 : Perform(hCurlHandle);
855 :
856 3 : VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
857 :
858 3 : curl_slist_free_all(headers);
859 :
860 3 : NetworkStatisticsLogger::LogPUT(0);
861 :
862 3 : long response_code = 0;
863 3 : curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
864 :
865 3 : CPLStringList aosList;
866 3 : bool bOK = false;
867 3 : if (response_code == 200 && sWriteFuncData.pBuffer)
868 : {
869 4 : CPLJSONDocument oDoc;
870 2 : if (oDoc.LoadMemory(
871 2 : reinterpret_cast<const GByte *>(sWriteFuncData.pBuffer)))
872 : {
873 2 : bOK = oDoc.GetRoot().GetBool("boolean");
874 : }
875 : }
876 3 : if (bOK)
877 : {
878 2 : InvalidateDirContent(
879 4 : CPLGetDirnameSafe(osDirnameWithoutEndSlash.c_str()));
880 :
881 4 : FileProp cachedFileProp;
882 2 : cachedFileProp.eExists = EXIST_YES;
883 2 : cachedFileProp.bIsDirectory = true;
884 2 : cachedFileProp.bHasComputedFileSize = true;
885 2 : SetCachedFileProp(
886 4 : GetURLFromFilename(osDirnameWithoutEndSlash.c_str()).c_str(),
887 : cachedFileProp);
888 :
889 2 : RegisterEmptyDir(osDirnameWithoutEndSlash);
890 : }
891 : else
892 : {
893 1 : CPLDebug("WEBHDFS", "%s",
894 1 : sWriteFuncData.pBuffer ? sWriteFuncData.pBuffer : "(null)");
895 : }
896 :
897 3 : CPLFree(sWriteFuncData.pBuffer);
898 3 : curl_easy_cleanup(hCurlHandle);
899 :
900 3 : return bOK ? 0 : -1;
901 : }
902 :
903 : /************************************************************************/
904 : /* VSIWebHDFSHandle() */
905 : /************************************************************************/
906 :
907 7 : VSIWebHDFSHandle::VSIWebHDFSHandle(VSIWebHDFSFSHandler *poFSIn,
908 7 : const char *pszFilename, const char *pszURL)
909 : : VSICurlHandle(poFSIn, pszFilename, pszURL),
910 7 : m_osDataNodeHost(GetWebHDFSDataNodeHost(pszFilename))
911 : {
912 : // cppcheck-suppress useInitializationList
913 : m_osUsernameParam =
914 7 : VSIGetPathSpecificOption(pszFilename, "WEBHDFS_USERNAME", "");
915 7 : if (!m_osUsernameParam.empty())
916 1 : m_osUsernameParam = "&user.name=" + m_osUsernameParam;
917 : m_osDelegationParam =
918 7 : VSIGetPathSpecificOption(pszFilename, "WEBHDFS_DELEGATION", "");
919 7 : if (!m_osDelegationParam.empty())
920 1 : m_osDelegationParam = "&delegation=" + m_osDelegationParam;
921 7 : }
922 :
923 : /************************************************************************/
924 : /* GetFileSize() */
925 : /************************************************************************/
926 :
927 4 : vsi_l_offset VSIWebHDFSHandle::GetFileSize(bool bSetError)
928 : {
929 4 : if (oFileProp.bHasComputedFileSize)
930 2 : return oFileProp.fileSize;
931 :
932 4 : NetworkStatisticsFileSystem oContextFS(poFS->GetFSPrefix().c_str());
933 4 : NetworkStatisticsFile oContextFile(m_osFilename.c_str());
934 4 : NetworkStatisticsAction oContextAction("GetFileSize");
935 :
936 2 : oFileProp.bHasComputedFileSize = true;
937 :
938 2 : std::string osURL(m_pszURL);
939 :
940 2 : if (osURL.size() > strlen("/webhdfs/v1") &&
941 2 : osURL.find("/webhdfs/v1") == osURL.size() - strlen("/webhdfs/v1") &&
942 2 : std::count(osURL.begin(), osURL.end(), '/') == 4)
943 : {
944 : // If this is the root directory, add a trailing slash
945 0 : osURL += "/";
946 : }
947 :
948 2 : osURL += "?op=GETFILESTATUS" + m_osUsernameParam + m_osDelegationParam;
949 :
950 2 : CURL *hCurlHandle = curl_easy_init();
951 :
952 : struct curl_slist *headers =
953 2 : poFS->SetOptions(hCurlHandle, osURL.c_str(), m_aosHTTPOptions.List());
954 :
955 2 : WriteFuncStruct sWriteFuncData;
956 2 : VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
957 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
958 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
959 : VSICurlHandleWriteFunc);
960 :
961 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
962 :
963 2 : char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
964 2 : szCurlErrBuf[0] = '\0';
965 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf);
966 :
967 2 : poFS->Perform(hCurlHandle);
968 :
969 2 : VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
970 :
971 2 : curl_slist_free_all(headers);
972 :
973 2 : NetworkStatisticsLogger::LogGET(sWriteFuncData.nSize);
974 :
975 2 : long response_code = 0;
976 2 : curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
977 :
978 2 : oFileProp.eExists = EXIST_NO;
979 2 : if (response_code == 200 && sWriteFuncData.pBuffer)
980 : {
981 2 : CPLJSONDocument oDoc;
982 1 : if (oDoc.LoadMemory(
983 1 : reinterpret_cast<const GByte *>(sWriteFuncData.pBuffer)))
984 : {
985 2 : CPLJSONObject oFileStatus = oDoc.GetRoot().GetObj("FileStatus");
986 1 : oFileProp.fileSize = oFileStatus.GetLong("length");
987 1 : oFileProp.mTime = static_cast<size_t>(
988 1 : oFileStatus.GetLong("modificationTime") / 1000);
989 1 : oFileProp.bIsDirectory =
990 1 : oFileStatus.GetString("type") == "DIRECTORY";
991 1 : oFileProp.eExists = EXIST_YES;
992 : }
993 : }
994 :
995 : // If there was no VSI error thrown in the process,
996 : // fail by reporting the HTTP response code.
997 2 : if (response_code != 200 && bSetError && VSIGetLastErrorNo() == 0)
998 : {
999 0 : if (strlen(szCurlErrBuf) > 0)
1000 : {
1001 0 : if (response_code == 0)
1002 : {
1003 0 : VSIError(VSIE_HttpError, "CURL error: %s", szCurlErrBuf);
1004 : }
1005 : else
1006 : {
1007 0 : VSIError(VSIE_HttpError, "HTTP response code: %d - %s",
1008 : static_cast<int>(response_code), szCurlErrBuf);
1009 : }
1010 : }
1011 : else
1012 : {
1013 0 : VSIError(VSIE_HttpError, "HTTP response code: %d",
1014 : static_cast<int>(response_code));
1015 : }
1016 : }
1017 :
1018 : if (ENABLE_DEBUG)
1019 : CPLDebug(
1020 : "WEBHDFS", "GetFileSize(%s)=" CPL_FRMT_GUIB " response_code=%d",
1021 : osURL.c_str(), oFileProp.fileSize, static_cast<int>(response_code));
1022 :
1023 2 : CPLFree(sWriteFuncData.pBuffer);
1024 2 : curl_easy_cleanup(hCurlHandle);
1025 :
1026 2 : oFileProp.bHasComputedFileSize = true;
1027 2 : poFS->SetCachedFileProp(m_pszURL, oFileProp);
1028 :
1029 2 : return oFileProp.fileSize;
1030 : }
1031 :
1032 : /************************************************************************/
1033 : /* DownloadRegion() */
1034 : /************************************************************************/
1035 :
1036 3 : std::string VSIWebHDFSHandle::DownloadRegion(const vsi_l_offset startOffset,
1037 : const int nBlocks)
1038 : {
1039 3 : if (bInterrupted && bStopOnInterruptUntilUninstall)
1040 0 : return std::string();
1041 :
1042 3 : poFS->GetCachedFileProp(m_pszURL, oFileProp);
1043 3 : if (oFileProp.eExists == EXIST_NO)
1044 0 : return std::string();
1045 :
1046 6 : NetworkStatisticsFileSystem oContextFS(poFS->GetFSPrefix().c_str());
1047 6 : NetworkStatisticsFile oContextFile(m_osFilename.c_str());
1048 6 : NetworkStatisticsAction oContextAction("Read");
1049 :
1050 6 : std::string osURL(m_pszURL);
1051 :
1052 3 : WriteFuncStruct sWriteFuncData;
1053 6 : CPLHTTPRetryContext oRetryContext(m_oRetryParameters);
1054 3 : bool bInRedirect = false;
1055 : const vsi_l_offset nEndOffset =
1056 3 : startOffset +
1057 3 : static_cast<vsi_l_offset>(nBlocks) * VSICURLGetDownloadChunkSize() - 1;
1058 :
1059 4 : retry:
1060 4 : CURL *hCurlHandle = curl_easy_init();
1061 :
1062 4 : VSICURLInitWriteFuncStruct(&sWriteFuncData, this, pfnReadCbk,
1063 : pReadCbkUserData);
1064 4 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
1065 4 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
1066 : VSICurlHandleWriteFunc);
1067 :
1068 4 : if (!bInRedirect)
1069 : {
1070 3 : osURL += "?op=OPEN&offset=";
1071 3 : osURL += CPLSPrintf(CPL_FRMT_GUIB, startOffset);
1072 3 : osURL += "&length=";
1073 3 : osURL += CPLSPrintf(CPL_FRMT_GUIB, nEndOffset - startOffset + 1);
1074 3 : osURL += m_osUsernameParam + m_osDelegationParam;
1075 : }
1076 :
1077 : struct curl_slist *headers =
1078 4 : poFS->SetOptions(hCurlHandle, osURL.c_str(), m_aosHTTPOptions.List());
1079 :
1080 4 : if (!m_osDataNodeHost.empty())
1081 : {
1082 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_FOLLOWLOCATION, 0);
1083 : }
1084 :
1085 : if (ENABLE_DEBUG)
1086 : CPLDebug("WEBHDFS", "Downloading %s...", osURL.c_str());
1087 :
1088 4 : char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
1089 4 : szCurlErrBuf[0] = '\0';
1090 4 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf);
1091 :
1092 4 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
1093 :
1094 4 : poFS->Perform(hCurlHandle);
1095 :
1096 4 : VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
1097 :
1098 4 : curl_slist_free_all(headers);
1099 :
1100 4 : NetworkStatisticsLogger::LogGET(sWriteFuncData.nSize);
1101 :
1102 4 : if (sWriteFuncData.bInterrupted)
1103 : {
1104 0 : bInterrupted = true;
1105 :
1106 0 : CPLFree(sWriteFuncData.pBuffer);
1107 0 : curl_easy_cleanup(hCurlHandle);
1108 :
1109 0 : return std::string();
1110 : }
1111 :
1112 4 : long response_code = 0;
1113 4 : curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
1114 :
1115 : if (ENABLE_DEBUG)
1116 : CPLDebug("WEBHDFS", "Got response_code=%ld", response_code);
1117 :
1118 4 : if (!bInRedirect)
1119 : {
1120 3 : char *pszRedirectURL = nullptr;
1121 3 : curl_easy_getinfo(hCurlHandle, CURLINFO_REDIRECT_URL, &pszRedirectURL);
1122 3 : if (pszRedirectURL && strstr(pszRedirectURL, m_pszURL) == nullptr)
1123 : {
1124 1 : CPLDebug("WEBHDFS", "Redirect URL: %s", pszRedirectURL);
1125 :
1126 1 : bInRedirect = true;
1127 1 : osURL = pszRedirectURL;
1128 1 : if (!m_osDataNodeHost.empty())
1129 : {
1130 1 : osURL = PatchWebHDFSUrl(osURL, m_osDataNodeHost);
1131 : }
1132 :
1133 1 : CPLFree(sWriteFuncData.pBuffer);
1134 1 : curl_easy_cleanup(hCurlHandle);
1135 :
1136 1 : goto retry;
1137 : }
1138 : }
1139 :
1140 3 : if (response_code != 200)
1141 : {
1142 1 : if (oRetryContext.CanRetry(static_cast<int>(response_code), nullptr,
1143 : szCurlErrBuf))
1144 : {
1145 0 : CPLError(CE_Warning, CPLE_AppDefined,
1146 : "HTTP error code: %d - %s. "
1147 : "Retrying again in %.1f secs",
1148 : static_cast<int>(response_code), m_pszURL,
1149 : oRetryContext.GetCurrentDelay());
1150 0 : CPLSleep(oRetryContext.GetCurrentDelay());
1151 0 : CPLFree(sWriteFuncData.pBuffer);
1152 0 : curl_easy_cleanup(hCurlHandle);
1153 0 : goto retry;
1154 : }
1155 :
1156 1 : if (response_code >= 400 && szCurlErrBuf[0] != '\0')
1157 : {
1158 0 : CPLError(CE_Failure, CPLE_AppDefined, "%d: %s",
1159 : static_cast<int>(response_code), szCurlErrBuf);
1160 : }
1161 1 : if (!oFileProp.bHasComputedFileSize && startOffset == 0)
1162 : {
1163 1 : oFileProp.bHasComputedFileSize = true;
1164 1 : oFileProp.fileSize = 0;
1165 1 : oFileProp.eExists = EXIST_NO;
1166 1 : poFS->SetCachedFileProp(m_pszURL, oFileProp);
1167 : }
1168 1 : CPLFree(sWriteFuncData.pBuffer);
1169 1 : curl_easy_cleanup(hCurlHandle);
1170 1 : return std::string();
1171 : }
1172 :
1173 2 : oFileProp.eExists = EXIST_YES;
1174 2 : poFS->SetCachedFileProp(m_pszURL, oFileProp);
1175 :
1176 2 : DownloadRegionPostProcess(startOffset, nBlocks, sWriteFuncData.pBuffer,
1177 : sWriteFuncData.nSize);
1178 :
1179 4 : std::string osRet;
1180 2 : osRet.assign(sWriteFuncData.pBuffer, sWriteFuncData.nSize);
1181 :
1182 2 : CPLFree(sWriteFuncData.pBuffer);
1183 2 : curl_easy_cleanup(hCurlHandle);
1184 :
1185 2 : return osRet;
1186 : }
1187 :
1188 : } /* end of namespace cpl */
1189 :
1190 : #endif // DOXYGEN_SKIP
1191 : //! @endcond
1192 :
1193 : /************************************************************************/
1194 : /* VSIInstallWebHdfsHandler() */
1195 : /************************************************************************/
1196 :
1197 : /*!
1198 : \brief Install /vsiwebhdfs/ WebHDFS (Hadoop File System) REST API file
1199 : system handler (requires libcurl)
1200 :
1201 : \verbatim embed:rst
1202 : See :ref:`/vsiwebhdfs/ documentation <vsiwebhdfs>`
1203 : \endverbatim
1204 :
1205 : */
1206 2101 : void VSIInstallWebHdfsHandler(void)
1207 : {
1208 2101 : VSIFileManager::InstallHandler(
1209 : "/vsiwebhdfs/",
1210 4202 : std::make_shared<cpl::VSIWebHDFSFSHandler>("/vsiwebhdfs/"));
1211 2101 : }
1212 :
1213 : #endif /* HAVE_CURL */
|