LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gmlas - ogrgmlasxlinkresolver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 108 125 86.4 %
Date: 2024-05-06 11:10:03 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * Project:  OGR
       3             :  * Purpose:  OGRGMLASDriver implementation
       4             :  * Author:   Even Rouault, <even dot rouault at spatialys dot com>
       5             :  *
       6             :  * Initial development funded by the European Earth observation programme
       7             :  * Copernicus
       8             :  *
       9             :  ******************************************************************************
      10             :  * Copyright (c) 2016, Even Rouault, <even dot rouault at spatialys dot com>
      11             :  *
      12             :  * Permission is hereby granted, free of charge, to any person obtaining a
      13             :  * copy of this software and associated documentation files (the "Software"),
      14             :  * to deal in the Software without restriction, including without limitation
      15             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16             :  * and/or sell copies of the Software, and to permit persons to whom the
      17             :  * Software is furnished to do so, subject to the following conditions:
      18             :  *
      19             :  * The above copyright notice and this permission notice shall be included
      20             :  * in all copies or substantial portions of the Software.
      21             :  *
      22             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      23             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      25             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      28             :  * DEALINGS IN THE SOFTWARE.
      29             :  ****************************************************************************/
      30             : 
      31             : #include "ogr_gmlas.h"
      32             : 
      33             : #include "cpl_http.h"
      34             : 
      35             : #include <time.h>
      36             : 
      37             : /************************************************************************/
      38             : /*                         GMLASXLinkResolver()                         */
      39             : /************************************************************************/
      40             : 
      41         187 : GMLASXLinkResolver::GMLASXLinkResolver()
      42             :     : m_nMaxRAMCacheSize(
      43         187 :           atoi(CPLGetConfigOption("GMLAS_XLINK_RAM_CACHE_SIZE", "10000000")))
      44             : {
      45         187 : }
      46             : 
      47             : /************************************************************************/
      48             : /*                             SetConf()                                */
      49             : /************************************************************************/
      50             : 
      51         171 : void GMLASXLinkResolver::SetConf(const GMLASXLinkResolutionConf &oConf)
      52             : {
      53         171 :     m_oConf = oConf;
      54         171 :     SetCacheDirectory(m_oConf.m_osCacheDirectory);
      55         171 : }
      56             : 
      57             : /************************************************************************/
      58             : /*                          FetchRawContent()                           */
      59             : /************************************************************************/
      60             : 
      61          10 : CPLString GMLASXLinkResolver::FetchRawContent(const CPLString &osURL,
      62             :                                               const char *pszHeaders)
      63             : {
      64          10 :     char **papszOptions = nullptr;
      65          10 :     if (m_oConf.m_nMaxGlobalResolutionTime > 0 &&
      66           0 :         m_nGlobalResolutionTime > m_oConf.m_nMaxGlobalResolutionTime)
      67             :     {
      68           0 :         CPLError(CE_Failure, CPLE_AppDefined,
      69             :                  "Maximum global resolution time has been reached. "
      70             :                  "No remote resource will be fetched");
      71           0 :         return CPLString();
      72             :     }
      73          10 :     if (m_oConf.m_nTimeOut > 0 || m_oConf.m_nMaxGlobalResolutionTime > 0)
      74             :     {
      75           0 :         int nTimeout = m_oConf.m_nTimeOut;
      76           0 :         if (m_oConf.m_nTimeOut > 0 && m_oConf.m_nMaxGlobalResolutionTime > 0)
      77             :         {
      78             :             // Select the minimum between the individual timeout and the
      79             :             // remaining time granted by the max global resolution time.
      80           0 :             int nRemaining =
      81           0 :                 m_oConf.m_nMaxGlobalResolutionTime - m_nGlobalResolutionTime;
      82           0 :             if (nRemaining < nTimeout)
      83           0 :                 nTimeout = nRemaining;
      84             :         }
      85           0 :         else if (m_oConf.m_nMaxGlobalResolutionTime > 0)
      86             :         {
      87           0 :             nTimeout =
      88           0 :                 m_oConf.m_nMaxGlobalResolutionTime - m_nGlobalResolutionTime;
      89             :         }
      90           0 :         papszOptions = CSLSetNameValue(papszOptions, "TIMEOUT",
      91             :                                        CPLSPrintf("%d", nTimeout));
      92             :     }
      93          10 :     if (m_oConf.m_nMaxFileSize > 0)
      94             :     {
      95             :         papszOptions =
      96          10 :             CSLSetNameValue(papszOptions, "MAX_FILE_SIZE",
      97             :                             CPLSPrintf("%d", m_oConf.m_nMaxFileSize));
      98             :     }
      99          10 :     if (!m_oConf.m_osProxyServerPort.empty())
     100             :     {
     101             :         papszOptions =
     102           0 :             CSLSetNameValue(papszOptions, "PROXY", m_oConf.m_osProxyServerPort);
     103             :     }
     104          10 :     if (!m_oConf.m_osProxyUserPassword.empty())
     105             :     {
     106           0 :         papszOptions = CSLSetNameValue(papszOptions, "PROXYUSERPWD",
     107             :                                        m_oConf.m_osProxyUserPassword);
     108             :     }
     109          10 :     if (!m_oConf.m_osProxyAuth.empty())
     110             :     {
     111             :         papszOptions =
     112           0 :             CSLSetNameValue(papszOptions, "PROXYAUTH", m_oConf.m_osProxyAuth);
     113             :     }
     114          10 :     if (pszHeaders != nullptr)
     115             :     {
     116           1 :         papszOptions = CSLSetNameValue(papszOptions, "HEADERS", pszHeaders);
     117             :     }
     118          10 :     time_t nTimeStart = time(nullptr);
     119          10 :     CPLHTTPResult *psResult = CPLHTTPFetch(osURL, papszOptions);
     120          10 :     time_t nTimeStop = time(nullptr);
     121          10 :     m_nGlobalResolutionTime += static_cast<int>(nTimeStop - nTimeStart);
     122          10 :     CSLDestroy(papszOptions);
     123          10 :     if (psResult == nullptr)
     124           0 :         return CPLString();
     125             : 
     126          10 :     if (psResult->nStatus != 0 || psResult->pabyData == nullptr)
     127             :     {
     128           3 :         CPLHTTPDestroyResult(psResult);
     129           3 :         return CPLString();
     130             :     }
     131             : 
     132          14 :     CPLString osResult;
     133           7 :     osResult.assign(reinterpret_cast<char *>(psResult->pabyData),
     134           7 :                     psResult->nDataLen);
     135           7 :     CPLHTTPDestroyResult(psResult);
     136           7 :     return osResult;
     137             : }
     138             : 
     139             : /************************************************************************/
     140             : /*                           GetRawContent()                            */
     141             : /************************************************************************/
     142             : 
     143          17 : CPLString GMLASXLinkResolver::GetRawContent(const CPLString &osURL,
     144             :                                             const char *pszHeaders,
     145             :                                             bool bAllowRemoteDownload,
     146             :                                             bool bCacheResults)
     147             : {
     148          17 :     bool bDiskCacheAvailable = false;
     149          17 :     if (!m_osCacheDirectory.empty() && RecursivelyCreateDirectoryIfNeeded())
     150             :     {
     151          17 :         bDiskCacheAvailable = true;
     152             : 
     153          17 :         CPLString osCachedFileName(GetCachedFilename(osURL));
     154          17 :         VSILFILE *fp = nullptr;
     155          18 :         if (!m_bRefresh || m_aoSetRefreshedFiles.find(osCachedFileName) !=
     156          18 :                                m_aoSetRefreshedFiles.end())
     157             :         {
     158          16 :             fp = VSIFOpenL(osCachedFileName, "rb");
     159             :         }
     160          17 :         if (fp != nullptr)
     161             :         {
     162           4 :             CPLDebug("GMLAS", "Use cached %s", osCachedFileName.c_str());
     163           4 :             GByte *pabyRet = nullptr;
     164           4 :             vsi_l_offset nSize = 0;
     165           8 :             CPLString osContent;
     166           4 :             if (VSIIngestFile(fp, nullptr, &pabyRet, &nSize, -1))
     167             :             {
     168             :                 osContent.assign(reinterpret_cast<const char *>(pabyRet),
     169           4 :                                  static_cast<size_t>(nSize));
     170             :             }
     171           4 :             VSIFree(pabyRet);
     172           4 :             VSIFCloseL(fp);
     173           4 :             return osContent;
     174             :         }
     175          13 :         else if (bAllowRemoteDownload)
     176             :         {
     177          12 :             if (m_bRefresh)
     178           1 :                 m_aoSetRefreshedFiles.insert(osCachedFileName);
     179             :         }
     180             :         else
     181             :         {
     182           1 :             CPLDebug("GMLAS",
     183             :                      "Could not find locally cached %s, and not allowed to"
     184             :                      "download it",
     185             :                      osURL.c_str());
     186           1 :             return CPLString();
     187             :         }
     188             :     }
     189             : 
     190             :     // Check memory cache first
     191             :     {
     192          12 :         const auto oIter = m_oMapURLToContent.find(osURL);
     193          12 :         if (oIter != m_oMapURLToContent.end())
     194           2 :             return oIter->second;
     195             :     }
     196             : 
     197          20 :     const CPLString osContent(FetchRawContent(osURL, pszHeaders));
     198             :     // Cache to disk if possible
     199          10 :     if (bDiskCacheAvailable && bCacheResults && !osContent.empty())
     200             :     {
     201          10 :         CPLString osCachedFileName(GetCachedFilename(osURL));
     202          10 :         CPLString osTmpfilename(osCachedFileName + ".tmp");
     203           5 :         VSILFILE *fpTemp = VSIFOpenL(osTmpfilename, "wb");
     204           5 :         if (fpTemp != nullptr)
     205             :         {
     206             :             const bool bSuccess =
     207           5 :                 VSIFWriteL(osContent.data(), osContent.size(), 1, fpTemp) == 1;
     208           5 :             VSIFCloseL(fpTemp);
     209           5 :             if (bSuccess)
     210           5 :                 VSIRename(osTmpfilename, osCachedFileName);
     211             :         }
     212             :     }
     213             :     // Otherwise to RAM
     214           5 :     else if (!osContent.empty() && osContent.size() < m_nMaxRAMCacheSize)
     215             :     {
     216             :         // If cache is going to be saturated, evict larger objects first
     217           3 :         while (osContent.size() + m_nCurrentRAMCacheSize > m_nMaxRAMCacheSize)
     218             :         {
     219             :             std::map<size_t, std::vector<CPLString>>::reverse_iterator oIter =
     220           1 :                 m_oMapFileSizeToURLs.rbegin();
     221           1 :             const size_t nSizeToEvict = oIter->first;
     222           1 :             m_nCurrentRAMCacheSize -= nSizeToEvict;
     223           2 :             const CPLString osURLToEvict(oIter->second.front());
     224           1 :             m_oMapURLToContent.erase(osURLToEvict);
     225           1 :             oIter->second.erase(oIter->second.begin());
     226           1 :             if (oIter->second.empty())
     227           1 :                 m_oMapFileSizeToURLs.erase(nSizeToEvict);
     228             :         }
     229           2 :         m_oMapURLToContent[osURL] = osContent;
     230           2 :         m_oMapFileSizeToURLs[osContent.size()].push_back(osURL);
     231           2 :         m_nCurrentRAMCacheSize += osContent.size();
     232             :     }
     233          10 :     return osContent;
     234             : }
     235             : 
     236             : /************************************************************************/
     237             : /*                     IsRawContentResolutionEnabled()                  */
     238             : /************************************************************************/
     239             : 
     240          21 : bool GMLASXLinkResolver::IsRawContentResolutionEnabled() const
     241             : {
     242          32 :     return m_oConf.m_bDefaultResolutionEnabled &&
     243          11 :            m_oConf.m_eDefaultResolutionMode ==
     244          21 :                GMLASXLinkResolutionConf::RawContent;
     245             : }
     246             : 
     247             : /************************************************************************/
     248             : /*                      GetMatchingResolutionRule()                      */
     249             : /************************************************************************/
     250             : 
     251          35 : int GMLASXLinkResolver::GetMatchingResolutionRule(const CPLString &osURL) const
     252             : {
     253          45 :     for (size_t i = 0; i < m_oConf.m_aoURLSpecificRules.size(); ++i)
     254             :     {
     255          24 :         if (osURL.compare(0,
     256          24 :                           m_oConf.m_aoURLSpecificRules[i].m_osURLPrefix.size(),
     257          48 :                           m_oConf.m_aoURLSpecificRules[i].m_osURLPrefix) == 0)
     258             :         {
     259          14 :             return static_cast<int>(i);
     260             :         }
     261             :     }
     262             : 
     263             :     // No match
     264          21 :     return -1;
     265             : }
     266             : 
     267             : /************************************************************************/
     268             : /*                           GetRawContent()                            */
     269             : /************************************************************************/
     270             : 
     271          11 : CPLString GMLASXLinkResolver::GetRawContent(const CPLString &osURL)
     272             : {
     273          11 :     return GetRawContent(osURL, nullptr, m_oConf.m_bDefaultAllowRemoteDownload,
     274          11 :                          m_oConf.m_bDefaultCacheResults);
     275             : }
     276             : 
     277             : /************************************************************************/
     278             : /*                         GetRawContentForRule()                       */
     279             : /************************************************************************/
     280             : 
     281           6 : CPLString GMLASXLinkResolver::GetRawContentForRule(const CPLString &osURL,
     282             :                                                    int nIdxRule)
     283             : {
     284             :     const GMLASXLinkResolutionConf::URLSpecificResolution &oRule(
     285           6 :         m_oConf.m_aoURLSpecificRules[nIdxRule]);
     286             : 
     287          12 :     CPLString osHeaders;
     288          12 :     for (size_t i = 0; i < oRule.m_aosNameValueHTTPHeaders.size(); ++i)
     289             :     {
     290           6 :         if (!osHeaders.empty())
     291           3 :             osHeaders += "\r\n";
     292           6 :         osHeaders += oRule.m_aosNameValueHTTPHeaders[i].first;
     293           6 :         osHeaders += ": ";
     294           6 :         osHeaders += oRule.m_aosNameValueHTTPHeaders[i].second;
     295             :     }
     296           3 :     return GetRawContent(osURL, osHeaders.empty() ? nullptr : osHeaders.c_str(),
     297          15 :                          oRule.m_bAllowRemoteDownload, oRule.m_bCacheResults);
     298             : }

Generated by: LCOV version 1.14