LCOV - code coverage report
Current view: top level - port - cpl_vsil_webhdfs.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 475 523 90.8 %
Date: 2025-07-09 17:50:03 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        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 */

Generated by: LCOV version 1.14