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 1392 : explicit VSIWebHDFSFSHandler(const char *pszPrefix) : m_osPrefix(pszPrefix)
77 : {
78 1392 : }
79 :
80 1882 : ~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 : virtual ~VSIWebHDFSWriteHandle();
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 :
595 2 : CURL *hCurlHandle = curl_easy_init();
596 :
597 : struct curl_slist *headers =
598 2 : VSICurlSetOptions(hCurlHandle, osURL.c_str(), nullptr);
599 :
600 2 : WriteFuncStruct sWriteFuncData;
601 2 : VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
602 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
603 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
604 : VSICurlHandleWriteFunc);
605 :
606 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
607 :
608 2 : VSICURLMultiPerform(hCurlMultiHandle, hCurlHandle);
609 :
610 2 : VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
611 :
612 2 : curl_slist_free_all(headers);
613 :
614 2 : NetworkStatisticsLogger::LogGET(sWriteFuncData.nSize);
615 :
616 2 : long response_code = 0;
617 2 : curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
618 :
619 4 : CPLStringList aosList;
620 2 : bool bOK = false;
621 2 : if (response_code == 200 && sWriteFuncData.pBuffer)
622 : {
623 2 : CPLJSONDocument oDoc;
624 1 : if (oDoc.LoadMemory(
625 1 : reinterpret_cast<const GByte *>(sWriteFuncData.pBuffer)))
626 : {
627 : CPLJSONArray oFileStatus =
628 3 : oDoc.GetRoot().GetArray("FileStatuses/FileStatus");
629 1 : bOK = oFileStatus.IsValid();
630 3 : for (int i = 0; i < oFileStatus.Size(); i++)
631 : {
632 4 : CPLJSONObject oItem = oFileStatus[i];
633 2 : vsi_l_offset fileSize = oItem.GetLong("length");
634 : size_t mTime = static_cast<size_t>(
635 2 : oItem.GetLong("modificationTime") / 1000);
636 2 : bool bIsDirectory = oItem.GetString("type") == "DIRECTORY";
637 6 : std::string osName = oItem.GetString("pathSuffix");
638 : // can be empty if we for example ask to list a file: in that
639 : // case the file entry is reported but with an empty pathSuffix
640 2 : if (!osName.empty())
641 : {
642 2 : aosList.AddString(osName.c_str());
643 :
644 4 : FileProp prop;
645 2 : prop.eExists = EXIST_YES;
646 2 : prop.bIsDirectory = bIsDirectory;
647 2 : prop.bHasComputedFileSize = true;
648 2 : prop.fileSize = fileSize;
649 2 : prop.mTime = mTime;
650 4 : std::string osCachedFilename(osBaseURL + osName);
651 : #if DEBUG_VERBOSE
652 : CPLDebug("WEBHDFS", "Cache %s", osCachedFilename.c_str());
653 : #endif
654 2 : SetCachedFileProp(osCachedFilename.c_str(), prop);
655 : }
656 : }
657 : }
658 : }
659 :
660 2 : *pbGotFileList = bOK;
661 :
662 2 : CPLFree(sWriteFuncData.pBuffer);
663 2 : curl_easy_cleanup(hCurlHandle);
664 :
665 2 : if (bOK)
666 1 : return aosList.StealList();
667 : else
668 1 : return nullptr;
669 : }
670 :
671 : /************************************************************************/
672 : /* Unlink() */
673 : /************************************************************************/
674 :
675 7 : int VSIWebHDFSFSHandler::Unlink(const char *pszFilename)
676 : {
677 7 : if (!STARTS_WITH_CI(pszFilename, GetFSPrefix().c_str()))
678 1 : return -1;
679 :
680 12 : NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
681 12 : NetworkStatisticsAction oContextAction("Unlink");
682 :
683 18 : std::string osBaseURL = GetURLFromFilename(pszFilename);
684 :
685 6 : CURLM *hCurlMultiHandle = GetCurlMultiHandleFor(osBaseURL);
686 :
687 : std::string osUsernameParam =
688 12 : VSIGetPathSpecificOption(pszFilename, "WEBHDFS_USERNAME", "");
689 6 : if (!osUsernameParam.empty())
690 1 : osUsernameParam = "&user.name=" + osUsernameParam;
691 : std::string osDelegationParam =
692 12 : VSIGetPathSpecificOption(pszFilename, "WEBHDFS_DELEGATION", "");
693 6 : if (!osDelegationParam.empty())
694 1 : osDelegationParam = "&delegation=" + osDelegationParam;
695 : std::string osURL =
696 18 : osBaseURL + "?op=DELETE" + osUsernameParam + osDelegationParam;
697 :
698 6 : CURL *hCurlHandle = curl_easy_init();
699 :
700 6 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "DELETE");
701 :
702 : struct curl_slist *headers =
703 6 : VSICurlSetOptions(hCurlHandle, osURL.c_str(), nullptr);
704 :
705 6 : WriteFuncStruct sWriteFuncData;
706 6 : VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
707 6 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
708 6 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
709 : VSICurlHandleWriteFunc);
710 :
711 6 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
712 :
713 6 : VSICURLMultiPerform(hCurlMultiHandle, hCurlHandle);
714 :
715 6 : VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
716 :
717 6 : curl_slist_free_all(headers);
718 :
719 6 : NetworkStatisticsLogger::LogDELETE();
720 :
721 6 : long response_code = 0;
722 6 : curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
723 :
724 6 : CPLStringList aosList;
725 6 : bool bOK = false;
726 6 : if (response_code == 200 && sWriteFuncData.pBuffer)
727 : {
728 8 : CPLJSONDocument oDoc;
729 4 : if (oDoc.LoadMemory(
730 4 : reinterpret_cast<const GByte *>(sWriteFuncData.pBuffer)))
731 : {
732 4 : bOK = oDoc.GetRoot().GetBool("boolean");
733 : }
734 : }
735 6 : if (bOK)
736 : {
737 3 : InvalidateCachedData(osBaseURL.c_str());
738 :
739 3 : std::string osFilenameWithoutSlash(pszFilename);
740 6 : if (!osFilenameWithoutSlash.empty() &&
741 3 : osFilenameWithoutSlash.back() == '/')
742 0 : osFilenameWithoutSlash.pop_back();
743 :
744 3 : InvalidateDirContent(CPLGetDirnameSafe(osFilenameWithoutSlash.c_str()));
745 : }
746 : else
747 : {
748 3 : CPLDebug("WEBHDFS", "%s",
749 3 : sWriteFuncData.pBuffer ? sWriteFuncData.pBuffer : "(null)");
750 : }
751 :
752 6 : CPLFree(sWriteFuncData.pBuffer);
753 6 : curl_easy_cleanup(hCurlHandle);
754 :
755 6 : return bOK ? 0 : -1;
756 : }
757 :
758 : /************************************************************************/
759 : /* Rmdir() */
760 : /************************************************************************/
761 :
762 3 : int VSIWebHDFSFSHandler::Rmdir(const char *pszFilename)
763 : {
764 6 : NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
765 6 : NetworkStatisticsAction oContextAction("Rmdir");
766 :
767 6 : return Unlink(pszFilename);
768 : }
769 :
770 : /************************************************************************/
771 : /* Mkdir() */
772 : /************************************************************************/
773 :
774 5 : int VSIWebHDFSFSHandler::Mkdir(const char *pszDirname, long nMode)
775 : {
776 5 : if (!STARTS_WITH_CI(pszDirname, GetFSPrefix().c_str()))
777 1 : return -1;
778 :
779 8 : std::string osDirnameWithoutEndSlash(pszDirname);
780 8 : if (!osDirnameWithoutEndSlash.empty() &&
781 4 : osDirnameWithoutEndSlash.back() == '/')
782 : {
783 2 : osDirnameWithoutEndSlash.pop_back();
784 : }
785 :
786 4 : if (osDirnameWithoutEndSlash.find("/webhdfs/v1") ==
787 5 : osDirnameWithoutEndSlash.size() - strlen("/webhdfs/v1") &&
788 1 : std::count(osDirnameWithoutEndSlash.begin(),
789 5 : osDirnameWithoutEndSlash.end(), '/') == 6)
790 : {
791 : // The server does weird things (creating a webhdfs/v1 subfolder)
792 : // if we provide the root directory like
793 : // /vsiwebhdfs/http://localhost:50070/webhdfs/v1
794 1 : return -1;
795 : }
796 :
797 6 : NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
798 6 : NetworkStatisticsAction oContextAction("Mkdir");
799 :
800 : std::string osBaseURL =
801 9 : GetURLFromFilename(osDirnameWithoutEndSlash.c_str());
802 :
803 3 : CURLM *hCurlMultiHandle = GetCurlMultiHandleFor(osBaseURL);
804 :
805 : std::string osUsernameParam =
806 6 : VSIGetPathSpecificOption(pszDirname, "WEBHDFS_USERNAME", "");
807 3 : if (!osUsernameParam.empty())
808 1 : osUsernameParam = "&user.name=" + osUsernameParam;
809 : std::string osDelegationParam =
810 6 : VSIGetPathSpecificOption(pszDirname, "WEBHDFS_DELEGATION", "");
811 3 : if (!osDelegationParam.empty())
812 1 : osDelegationParam = "&delegation=" + osDelegationParam;
813 : std::string osURL =
814 9 : osBaseURL + "?op=MKDIRS" + osUsernameParam + osDelegationParam;
815 3 : if (nMode)
816 : {
817 1 : osURL += "&permission=";
818 1 : osURL += CPLSPrintf("%o", static_cast<int>(nMode));
819 : }
820 :
821 3 : CURL *hCurlHandle = curl_easy_init();
822 :
823 3 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "PUT");
824 :
825 : struct curl_slist *headers =
826 3 : VSICurlSetOptions(hCurlHandle, osURL.c_str(), nullptr);
827 :
828 3 : WriteFuncStruct sWriteFuncData;
829 3 : VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
830 3 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
831 3 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
832 : VSICurlHandleWriteFunc);
833 :
834 3 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
835 :
836 3 : VSICURLMultiPerform(hCurlMultiHandle, hCurlHandle);
837 :
838 3 : VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
839 :
840 3 : curl_slist_free_all(headers);
841 :
842 3 : NetworkStatisticsLogger::LogPUT(0);
843 :
844 3 : long response_code = 0;
845 3 : curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
846 :
847 3 : CPLStringList aosList;
848 3 : bool bOK = false;
849 3 : if (response_code == 200 && sWriteFuncData.pBuffer)
850 : {
851 4 : CPLJSONDocument oDoc;
852 2 : if (oDoc.LoadMemory(
853 2 : reinterpret_cast<const GByte *>(sWriteFuncData.pBuffer)))
854 : {
855 2 : bOK = oDoc.GetRoot().GetBool("boolean");
856 : }
857 : }
858 3 : if (bOK)
859 : {
860 2 : InvalidateDirContent(
861 4 : CPLGetDirnameSafe(osDirnameWithoutEndSlash.c_str()));
862 :
863 4 : FileProp cachedFileProp;
864 2 : cachedFileProp.eExists = EXIST_YES;
865 2 : cachedFileProp.bIsDirectory = true;
866 2 : cachedFileProp.bHasComputedFileSize = true;
867 2 : SetCachedFileProp(
868 4 : GetURLFromFilename(osDirnameWithoutEndSlash.c_str()).c_str(),
869 : cachedFileProp);
870 :
871 2 : RegisterEmptyDir(osDirnameWithoutEndSlash);
872 : }
873 : else
874 : {
875 1 : CPLDebug("WEBHDFS", "%s",
876 1 : sWriteFuncData.pBuffer ? sWriteFuncData.pBuffer : "(null)");
877 : }
878 :
879 3 : CPLFree(sWriteFuncData.pBuffer);
880 3 : curl_easy_cleanup(hCurlHandle);
881 :
882 3 : return bOK ? 0 : -1;
883 : }
884 :
885 : /************************************************************************/
886 : /* VSIWebHDFSHandle() */
887 : /************************************************************************/
888 :
889 7 : VSIWebHDFSHandle::VSIWebHDFSHandle(VSIWebHDFSFSHandler *poFSIn,
890 7 : const char *pszFilename, const char *pszURL)
891 : : VSICurlHandle(poFSIn, pszFilename, pszURL),
892 7 : m_osDataNodeHost(GetWebHDFSDataNodeHost(pszFilename))
893 : {
894 : // cppcheck-suppress useInitializationList
895 : m_osUsernameParam =
896 7 : VSIGetPathSpecificOption(pszFilename, "WEBHDFS_USERNAME", "");
897 7 : if (!m_osUsernameParam.empty())
898 1 : m_osUsernameParam = "&user.name=" + m_osUsernameParam;
899 : m_osDelegationParam =
900 7 : VSIGetPathSpecificOption(pszFilename, "WEBHDFS_DELEGATION", "");
901 7 : if (!m_osDelegationParam.empty())
902 1 : m_osDelegationParam = "&delegation=" + m_osDelegationParam;
903 7 : }
904 :
905 : /************************************************************************/
906 : /* GetFileSize() */
907 : /************************************************************************/
908 :
909 4 : vsi_l_offset VSIWebHDFSHandle::GetFileSize(bool bSetError)
910 : {
911 4 : if (oFileProp.bHasComputedFileSize)
912 2 : return oFileProp.fileSize;
913 :
914 4 : NetworkStatisticsFileSystem oContextFS(poFS->GetFSPrefix().c_str());
915 4 : NetworkStatisticsFile oContextFile(m_osFilename.c_str());
916 4 : NetworkStatisticsAction oContextAction("GetFileSize");
917 :
918 2 : oFileProp.bHasComputedFileSize = true;
919 :
920 2 : CURLM *hCurlMultiHandle = poFS->GetCurlMultiHandleFor(m_pszURL);
921 :
922 2 : std::string osURL(m_pszURL);
923 :
924 2 : if (osURL.size() > strlen("/webhdfs/v1") &&
925 2 : osURL.find("/webhdfs/v1") == osURL.size() - strlen("/webhdfs/v1") &&
926 2 : std::count(osURL.begin(), osURL.end(), '/') == 4)
927 : {
928 : // If this is the root directory, add a trailing slash
929 0 : osURL += "/";
930 : }
931 :
932 2 : osURL += "?op=GETFILESTATUS" + m_osUsernameParam + m_osDelegationParam;
933 :
934 2 : CURL *hCurlHandle = curl_easy_init();
935 :
936 : struct curl_slist *headers =
937 2 : VSICurlSetOptions(hCurlHandle, osURL.c_str(), m_aosHTTPOptions.List());
938 :
939 2 : WriteFuncStruct sWriteFuncData;
940 2 : VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
941 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
942 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
943 : VSICurlHandleWriteFunc);
944 :
945 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
946 :
947 2 : char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
948 2 : szCurlErrBuf[0] = '\0';
949 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf);
950 :
951 2 : VSICURLMultiPerform(hCurlMultiHandle, hCurlHandle);
952 :
953 2 : VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
954 :
955 2 : curl_slist_free_all(headers);
956 :
957 2 : NetworkStatisticsLogger::LogGET(sWriteFuncData.nSize);
958 :
959 2 : long response_code = 0;
960 2 : curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
961 :
962 2 : oFileProp.eExists = EXIST_NO;
963 2 : if (response_code == 200 && sWriteFuncData.pBuffer)
964 : {
965 2 : CPLJSONDocument oDoc;
966 1 : if (oDoc.LoadMemory(
967 1 : reinterpret_cast<const GByte *>(sWriteFuncData.pBuffer)))
968 : {
969 2 : CPLJSONObject oFileStatus = oDoc.GetRoot().GetObj("FileStatus");
970 1 : oFileProp.fileSize = oFileStatus.GetLong("length");
971 1 : oFileProp.mTime = static_cast<size_t>(
972 1 : oFileStatus.GetLong("modificationTime") / 1000);
973 1 : oFileProp.bIsDirectory =
974 1 : oFileStatus.GetString("type") == "DIRECTORY";
975 1 : oFileProp.eExists = EXIST_YES;
976 : }
977 : }
978 :
979 : // If there was no VSI error thrown in the process,
980 : // fail by reporting the HTTP response code.
981 2 : if (response_code != 200 && bSetError && VSIGetLastErrorNo() == 0)
982 : {
983 0 : if (strlen(szCurlErrBuf) > 0)
984 : {
985 0 : if (response_code == 0)
986 : {
987 0 : VSIError(VSIE_HttpError, "CURL error: %s", szCurlErrBuf);
988 : }
989 : else
990 : {
991 0 : VSIError(VSIE_HttpError, "HTTP response code: %d - %s",
992 : static_cast<int>(response_code), szCurlErrBuf);
993 : }
994 : }
995 : else
996 : {
997 0 : VSIError(VSIE_HttpError, "HTTP response code: %d",
998 : static_cast<int>(response_code));
999 : }
1000 : }
1001 :
1002 : if (ENABLE_DEBUG)
1003 : CPLDebug(
1004 : "WEBHDFS", "GetFileSize(%s)=" CPL_FRMT_GUIB " response_code=%d",
1005 : osURL.c_str(), oFileProp.fileSize, static_cast<int>(response_code));
1006 :
1007 2 : CPLFree(sWriteFuncData.pBuffer);
1008 2 : curl_easy_cleanup(hCurlHandle);
1009 :
1010 2 : oFileProp.bHasComputedFileSize = true;
1011 2 : poFS->SetCachedFileProp(m_pszURL, oFileProp);
1012 :
1013 2 : return oFileProp.fileSize;
1014 : }
1015 :
1016 : /************************************************************************/
1017 : /* DownloadRegion() */
1018 : /************************************************************************/
1019 :
1020 3 : std::string VSIWebHDFSHandle::DownloadRegion(const vsi_l_offset startOffset,
1021 : const int nBlocks)
1022 : {
1023 3 : if (bInterrupted && bStopOnInterruptUntilUninstall)
1024 0 : return std::string();
1025 :
1026 3 : poFS->GetCachedFileProp(m_pszURL, oFileProp);
1027 3 : if (oFileProp.eExists == EXIST_NO)
1028 0 : return std::string();
1029 :
1030 6 : NetworkStatisticsFileSystem oContextFS(poFS->GetFSPrefix().c_str());
1031 6 : NetworkStatisticsFile oContextFile(m_osFilename.c_str());
1032 6 : NetworkStatisticsAction oContextAction("Read");
1033 :
1034 3 : CURLM *hCurlMultiHandle = poFS->GetCurlMultiHandleFor(m_pszURL);
1035 :
1036 6 : std::string osURL(m_pszURL);
1037 :
1038 3 : WriteFuncStruct sWriteFuncData;
1039 6 : CPLHTTPRetryContext oRetryContext(m_oRetryParameters);
1040 3 : bool bInRedirect = false;
1041 : const vsi_l_offset nEndOffset =
1042 3 : startOffset +
1043 3 : static_cast<vsi_l_offset>(nBlocks) * VSICURLGetDownloadChunkSize() - 1;
1044 :
1045 4 : retry:
1046 4 : CURL *hCurlHandle = curl_easy_init();
1047 :
1048 4 : VSICURLInitWriteFuncStruct(&sWriteFuncData, this, pfnReadCbk,
1049 : pReadCbkUserData);
1050 4 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
1051 4 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
1052 : VSICurlHandleWriteFunc);
1053 :
1054 4 : if (!bInRedirect)
1055 : {
1056 3 : osURL += "?op=OPEN&offset=";
1057 3 : osURL += CPLSPrintf(CPL_FRMT_GUIB, startOffset);
1058 3 : osURL += "&length=";
1059 3 : osURL += CPLSPrintf(CPL_FRMT_GUIB, nEndOffset - startOffset + 1);
1060 3 : osURL += m_osUsernameParam + m_osDelegationParam;
1061 : }
1062 :
1063 : struct curl_slist *headers =
1064 4 : VSICurlSetOptions(hCurlHandle, osURL.c_str(), m_aosHTTPOptions.List());
1065 :
1066 4 : if (!m_osDataNodeHost.empty())
1067 : {
1068 2 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_FOLLOWLOCATION, 0);
1069 : }
1070 :
1071 : if (ENABLE_DEBUG)
1072 : CPLDebug("WEBHDFS", "Downloading %s...", osURL.c_str());
1073 :
1074 4 : char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
1075 4 : szCurlErrBuf[0] = '\0';
1076 4 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf);
1077 :
1078 4 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
1079 :
1080 4 : VSICURLMultiPerform(hCurlMultiHandle, hCurlHandle);
1081 :
1082 4 : VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
1083 :
1084 4 : curl_slist_free_all(headers);
1085 :
1086 4 : NetworkStatisticsLogger::LogGET(sWriteFuncData.nSize);
1087 :
1088 4 : if (sWriteFuncData.bInterrupted)
1089 : {
1090 0 : bInterrupted = true;
1091 :
1092 0 : CPLFree(sWriteFuncData.pBuffer);
1093 0 : curl_easy_cleanup(hCurlHandle);
1094 :
1095 0 : return std::string();
1096 : }
1097 :
1098 4 : long response_code = 0;
1099 4 : curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
1100 :
1101 : if (ENABLE_DEBUG)
1102 : CPLDebug("WEBHDFS", "Got response_code=%ld", response_code);
1103 :
1104 4 : if (!bInRedirect)
1105 : {
1106 3 : char *pszRedirectURL = nullptr;
1107 3 : curl_easy_getinfo(hCurlHandle, CURLINFO_REDIRECT_URL, &pszRedirectURL);
1108 3 : if (pszRedirectURL && strstr(pszRedirectURL, m_pszURL) == nullptr)
1109 : {
1110 1 : CPLDebug("WEBHDFS", "Redirect URL: %s", pszRedirectURL);
1111 :
1112 1 : bInRedirect = true;
1113 1 : osURL = pszRedirectURL;
1114 1 : if (!m_osDataNodeHost.empty())
1115 : {
1116 1 : osURL = PatchWebHDFSUrl(osURL, m_osDataNodeHost);
1117 : }
1118 :
1119 1 : CPLFree(sWriteFuncData.pBuffer);
1120 1 : curl_easy_cleanup(hCurlHandle);
1121 :
1122 1 : goto retry;
1123 : }
1124 : }
1125 :
1126 3 : if (response_code != 200)
1127 : {
1128 1 : if (oRetryContext.CanRetry(static_cast<int>(response_code), nullptr,
1129 : szCurlErrBuf))
1130 : {
1131 0 : CPLError(CE_Warning, CPLE_AppDefined,
1132 : "HTTP error code: %d - %s. "
1133 : "Retrying again in %.1f secs",
1134 : static_cast<int>(response_code), m_pszURL,
1135 : oRetryContext.GetCurrentDelay());
1136 0 : CPLSleep(oRetryContext.GetCurrentDelay());
1137 0 : CPLFree(sWriteFuncData.pBuffer);
1138 0 : curl_easy_cleanup(hCurlHandle);
1139 0 : goto retry;
1140 : }
1141 :
1142 1 : if (response_code >= 400 && szCurlErrBuf[0] != '\0')
1143 : {
1144 0 : CPLError(CE_Failure, CPLE_AppDefined, "%d: %s",
1145 : static_cast<int>(response_code), szCurlErrBuf);
1146 : }
1147 1 : if (!oFileProp.bHasComputedFileSize && startOffset == 0)
1148 : {
1149 1 : oFileProp.bHasComputedFileSize = true;
1150 1 : oFileProp.fileSize = 0;
1151 1 : oFileProp.eExists = EXIST_NO;
1152 1 : poFS->SetCachedFileProp(m_pszURL, oFileProp);
1153 : }
1154 1 : CPLFree(sWriteFuncData.pBuffer);
1155 1 : curl_easy_cleanup(hCurlHandle);
1156 1 : return std::string();
1157 : }
1158 :
1159 2 : oFileProp.eExists = EXIST_YES;
1160 2 : poFS->SetCachedFileProp(m_pszURL, oFileProp);
1161 :
1162 2 : DownloadRegionPostProcess(startOffset, nBlocks, sWriteFuncData.pBuffer,
1163 : sWriteFuncData.nSize);
1164 :
1165 4 : std::string osRet;
1166 2 : osRet.assign(sWriteFuncData.pBuffer, sWriteFuncData.nSize);
1167 :
1168 2 : CPLFree(sWriteFuncData.pBuffer);
1169 2 : curl_easy_cleanup(hCurlHandle);
1170 :
1171 2 : return osRet;
1172 : }
1173 :
1174 : } /* end of namespace cpl */
1175 :
1176 : #endif // DOXYGEN_SKIP
1177 : //! @endcond
1178 :
1179 : /************************************************************************/
1180 : /* VSIInstallWebHdfsHandler() */
1181 : /************************************************************************/
1182 :
1183 : /*!
1184 : \brief Install /vsiwebhdfs/ WebHDFS (Hadoop File System) REST API file
1185 : system handler (requires libcurl)
1186 :
1187 : \verbatim embed:rst
1188 : See :ref:`/vsiwebhdfs/ documentation <vsiwebhdfs>`
1189 : \endverbatim
1190 :
1191 : @since GDAL 2.4
1192 : */
1193 1392 : void VSIInstallWebHdfsHandler(void)
1194 : {
1195 1392 : VSIFileManager::InstallHandler(
1196 1392 : "/vsiwebhdfs/", new cpl::VSIWebHDFSFSHandler("/vsiwebhdfs/"));
1197 1392 : }
1198 :
1199 : #endif /* HAVE_CURL */
|