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