Coverage Report

Created: 2026-02-14 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasxlinkresolver.cpp
Line
Count
Source
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
 * SPDX-License-Identifier: MIT
13
 ****************************************************************************/
14
15
#include "ogr_gmlas.h"
16
17
#include "cpl_http.h"
18
19
#include <time.h>
20
21
/************************************************************************/
22
/*                         GMLASXLinkResolver()                         */
23
/************************************************************************/
24
25
GMLASXLinkResolver::GMLASXLinkResolver()
26
    : m_nMaxRAMCacheSize(
27
766
          atoi(CPLGetConfigOption("GMLAS_XLINK_RAM_CACHE_SIZE", "10000000")))
28
766
{
29
766
}
30
31
/************************************************************************/
32
/*                              SetConf()                               */
33
/************************************************************************/
34
35
void GMLASXLinkResolver::SetConf(const GMLASXLinkResolutionConf &oConf)
36
0
{
37
0
    m_oConf = oConf;
38
0
    SetCacheDirectory(m_oConf.m_osCacheDirectory);
39
0
}
40
41
/************************************************************************/
42
/*                          FetchRawContent()                           */
43
/************************************************************************/
44
45
CPLString GMLASXLinkResolver::FetchRawContent(const CPLString &osURL,
46
                                              const char *pszHeaders)
47
0
{
48
0
    char **papszOptions = nullptr;
49
0
    if (m_oConf.m_nMaxGlobalResolutionTime > 0 &&
50
0
        m_nGlobalResolutionTime > m_oConf.m_nMaxGlobalResolutionTime)
51
0
    {
52
0
        CPLError(CE_Failure, CPLE_AppDefined,
53
0
                 "Maximum global resolution time has been reached. "
54
0
                 "No remote resource will be fetched");
55
0
        return CPLString();
56
0
    }
57
0
    if (m_oConf.m_nTimeOut > 0 || m_oConf.m_nMaxGlobalResolutionTime > 0)
58
0
    {
59
0
        int nTimeout = m_oConf.m_nTimeOut;
60
0
        if (m_oConf.m_nTimeOut > 0 && m_oConf.m_nMaxGlobalResolutionTime > 0)
61
0
        {
62
            // Select the minimum between the individual timeout and the
63
            // remaining time granted by the max global resolution time.
64
0
            int nRemaining =
65
0
                m_oConf.m_nMaxGlobalResolutionTime - m_nGlobalResolutionTime;
66
0
            if (nRemaining < nTimeout)
67
0
                nTimeout = nRemaining;
68
0
        }
69
0
        else if (m_oConf.m_nMaxGlobalResolutionTime > 0)
70
0
        {
71
0
            nTimeout =
72
0
                m_oConf.m_nMaxGlobalResolutionTime - m_nGlobalResolutionTime;
73
0
        }
74
0
        papszOptions = CSLSetNameValue(papszOptions, "TIMEOUT",
75
0
                                       CPLSPrintf("%d", nTimeout));
76
0
    }
77
0
    if (m_oConf.m_nMaxFileSize > 0)
78
0
    {
79
0
        papszOptions =
80
0
            CSLSetNameValue(papszOptions, "MAX_FILE_SIZE",
81
0
                            CPLSPrintf("%d", m_oConf.m_nMaxFileSize));
82
0
    }
83
0
    if (!m_oConf.m_osProxyServerPort.empty())
84
0
    {
85
0
        papszOptions =
86
0
            CSLSetNameValue(papszOptions, "PROXY", m_oConf.m_osProxyServerPort);
87
0
    }
88
0
    if (!m_oConf.m_osProxyUserPassword.empty())
89
0
    {
90
0
        papszOptions = CSLSetNameValue(papszOptions, "PROXYUSERPWD",
91
0
                                       m_oConf.m_osProxyUserPassword);
92
0
    }
93
0
    if (!m_oConf.m_osProxyAuth.empty())
94
0
    {
95
0
        papszOptions =
96
0
            CSLSetNameValue(papszOptions, "PROXYAUTH", m_oConf.m_osProxyAuth);
97
0
    }
98
0
    if (pszHeaders != nullptr)
99
0
    {
100
0
        papszOptions = CSLSetNameValue(papszOptions, "HEADERS", pszHeaders);
101
0
    }
102
0
    time_t nTimeStart = time(nullptr);
103
0
    CPLHTTPResult *psResult = CPLHTTPFetch(osURL, papszOptions);
104
0
    time_t nTimeStop = time(nullptr);
105
0
    m_nGlobalResolutionTime += static_cast<int>(nTimeStop - nTimeStart);
106
0
    CSLDestroy(papszOptions);
107
0
    if (psResult == nullptr)
108
0
        return CPLString();
109
110
0
    if (psResult->nStatus != 0 || psResult->pabyData == nullptr)
111
0
    {
112
0
        CPLHTTPDestroyResult(psResult);
113
0
        return CPLString();
114
0
    }
115
116
0
    CPLString osResult;
117
0
    osResult.assign(reinterpret_cast<char *>(psResult->pabyData),
118
0
                    psResult->nDataLen);
119
0
    CPLHTTPDestroyResult(psResult);
120
0
    return osResult;
121
0
}
122
123
/************************************************************************/
124
/*                           GetRawContent()                            */
125
/************************************************************************/
126
127
CPLString GMLASXLinkResolver::GetRawContent(const CPLString &osURL,
128
                                            const char *pszHeaders,
129
                                            bool bAllowRemoteDownload,
130
                                            bool bCacheResults)
131
0
{
132
0
    bool bDiskCacheAvailable = false;
133
0
    if (!m_osCacheDirectory.empty() && RecursivelyCreateDirectoryIfNeeded())
134
0
    {
135
0
        bDiskCacheAvailable = true;
136
137
0
        CPLString osCachedFileName(GetCachedFilename(osURL));
138
0
        VSILFILE *fp = nullptr;
139
0
        if (!m_bRefresh || m_aoSetRefreshedFiles.find(osCachedFileName) !=
140
0
                               m_aoSetRefreshedFiles.end())
141
0
        {
142
0
            fp = VSIFOpenL(osCachedFileName, "rb");
143
0
        }
144
0
        if (fp != nullptr)
145
0
        {
146
0
            CPLDebug("GMLAS", "Use cached %s", osCachedFileName.c_str());
147
0
            GByte *pabyRet = nullptr;
148
0
            vsi_l_offset nSize = 0;
149
0
            CPLString osContent;
150
0
            if (VSIIngestFile(fp, nullptr, &pabyRet, &nSize, -1))
151
0
            {
152
0
                osContent.assign(reinterpret_cast<const char *>(pabyRet),
153
0
                                 static_cast<size_t>(nSize));
154
0
            }
155
0
            VSIFree(pabyRet);
156
0
            VSIFCloseL(fp);
157
0
            return osContent;
158
0
        }
159
0
        else if (bAllowRemoteDownload)
160
0
        {
161
0
            if (m_bRefresh)
162
0
                m_aoSetRefreshedFiles.insert(std::move(osCachedFileName));
163
0
        }
164
0
        else
165
0
        {
166
0
            CPLDebug("GMLAS",
167
0
                     "Could not find locally cached %s, and not allowed to"
168
0
                     "download it",
169
0
                     osURL.c_str());
170
0
            return CPLString();
171
0
        }
172
0
    }
173
174
    // Check memory cache first
175
0
    {
176
0
        const auto oIter = m_oMapURLToContent.find(osURL);
177
0
        if (oIter != m_oMapURLToContent.end())
178
0
            return oIter->second;
179
0
    }
180
181
0
    CPLString osContent(FetchRawContent(osURL, pszHeaders));
182
    // Cache to disk if possible
183
0
    if (bDiskCacheAvailable && bCacheResults && !osContent.empty())
184
0
    {
185
0
        CPLString osCachedFileName(GetCachedFilename(osURL));
186
0
        CPLString osTmpfilename(osCachedFileName + ".tmp");
187
0
        VSILFILE *fpTemp = VSIFOpenL(osTmpfilename, "wb");
188
0
        if (fpTemp != nullptr)
189
0
        {
190
0
            const bool bSuccess =
191
0
                VSIFWriteL(osContent.data(), osContent.size(), 1, fpTemp) == 1;
192
0
            VSIFCloseL(fpTemp);
193
0
            if (bSuccess)
194
0
                VSIRename(osTmpfilename, osCachedFileName);
195
0
        }
196
0
    }
197
    // Otherwise to RAM
198
0
    else if (!osContent.empty() && osContent.size() < m_nMaxRAMCacheSize)
199
0
    {
200
        // If cache is going to be saturated, evict larger objects first
201
0
        while (osContent.size() + m_nCurrentRAMCacheSize > m_nMaxRAMCacheSize)
202
0
        {
203
0
            std::map<size_t, std::vector<CPLString>>::reverse_iterator oIter =
204
0
                m_oMapFileSizeToURLs.rbegin();
205
0
            const size_t nSizeToEvict = oIter->first;
206
0
            m_nCurrentRAMCacheSize -= nSizeToEvict;
207
0
            const CPLString osURLToEvict(oIter->second.front());
208
0
            m_oMapURLToContent.erase(osURLToEvict);
209
0
            oIter->second.erase(oIter->second.begin());
210
0
            if (oIter->second.empty())
211
0
                m_oMapFileSizeToURLs.erase(nSizeToEvict);
212
0
        }
213
0
        m_oMapURLToContent[osURL] = osContent;
214
0
        m_oMapFileSizeToURLs[osContent.size()].push_back(osURL);
215
0
        m_nCurrentRAMCacheSize += osContent.size();
216
0
    }
217
0
    return osContent;
218
0
}
219
220
/************************************************************************/
221
/*                   IsRawContentResolutionEnabled()                    */
222
/************************************************************************/
223
224
bool GMLASXLinkResolver::IsRawContentResolutionEnabled() const
225
0
{
226
0
    return m_oConf.m_bDefaultResolutionEnabled &&
227
0
           m_oConf.m_eDefaultResolutionMode ==
228
0
               GMLASXLinkResolutionConf::RawContent;
229
0
}
230
231
/************************************************************************/
232
/*                     GetMatchingResolutionRule()                      */
233
/************************************************************************/
234
235
int GMLASXLinkResolver::GetMatchingResolutionRule(const CPLString &osURL) const
236
0
{
237
0
    for (size_t i = 0; i < m_oConf.m_aoURLSpecificRules.size(); ++i)
238
0
    {
239
0
        if (osURL.compare(0,
240
0
                          m_oConf.m_aoURLSpecificRules[i].m_osURLPrefix.size(),
241
0
                          m_oConf.m_aoURLSpecificRules[i].m_osURLPrefix) == 0)
242
0
        {
243
0
            return static_cast<int>(i);
244
0
        }
245
0
    }
246
247
    // No match
248
0
    return -1;
249
0
}
250
251
/************************************************************************/
252
/*                           GetRawContent()                            */
253
/************************************************************************/
254
255
CPLString GMLASXLinkResolver::GetRawContent(const CPLString &osURL)
256
0
{
257
0
    return GetRawContent(osURL, nullptr, m_oConf.m_bDefaultAllowRemoteDownload,
258
0
                         m_oConf.m_bDefaultCacheResults);
259
0
}
260
261
/************************************************************************/
262
/*                        GetRawContentForRule()                        */
263
/************************************************************************/
264
265
CPLString GMLASXLinkResolver::GetRawContentForRule(const CPLString &osURL,
266
                                                   int nIdxRule)
267
0
{
268
0
    const GMLASXLinkResolutionConf::URLSpecificResolution &oRule(
269
0
        m_oConf.m_aoURLSpecificRules[nIdxRule]);
270
271
0
    CPLString osHeaders;
272
0
    for (size_t i = 0; i < oRule.m_aosNameValueHTTPHeaders.size(); ++i)
273
0
    {
274
0
        if (!osHeaders.empty())
275
0
            osHeaders += "\r\n";
276
0
        osHeaders += oRule.m_aosNameValueHTTPHeaders[i].first;
277
0
        osHeaders += ": ";
278
0
        osHeaders += oRule.m_aosNameValueHTTPHeaders[i].second;
279
0
    }
280
0
    return GetRawContent(osURL, osHeaders.empty() ? nullptr : osHeaders.c_str(),
281
0
                         oRule.m_bAllowRemoteDownload, oRule.m_bCacheResults);
282
0
}