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