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