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