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