LCOV - code coverage report
Current view: top level - port - cpl_vsil_webhdfs.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 470 518 90.7 %
Date: 2024-11-25 23:50:41 Functions: 29 33 87.9 %

          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 */

Generated by: LCOV version 1.14