aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2019-12-25 20:13:03 +0100
committerEven Rouault <even.rouault@spatialys.com>2019-12-27 11:14:16 +0100
commit2093aca0720949303410280912b61efd791d2f01 (patch)
treea69c5392ed6689a88d9d959ddb41107413efacc6
parentc4589fbe42e5fea07a03919d3484164f5fb70dd3 (diff)
downloadPROJ-2093aca0720949303410280912b61efd791d2f01.tar.gz
PROJ-2093aca0720949303410280912b61efd791d2f01.zip
Network: make CDN endpoint configurable either in proj.ini, PROJ_NETWORK_ENDPOINT or proj_context_set_url_endpoint()
-rw-r--r--data/Makefile.am4
-rw-r--r--data/proj.ini10
-rw-r--r--docs/source/resource_files.rst24
-rw-r--r--docs/source/usage/environmentvars.rst9
-rw-r--r--scripts/reference_exported_symbols.txt1
-rw-r--r--src/filemanager.cpp25
-rw-r--r--src/open_lib.cpp113
-rw-r--r--src/proj.h2
-rw-r--r--src/proj_internal.h7
-rw-r--r--test/unit/CMakeLists.txt9
-rw-r--r--test/unit/test_network.cpp55
11 files changed, 245 insertions, 14 deletions
diff --git a/data/Makefile.am b/data/Makefile.am
index 3778a2be..09fa1989 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -1,6 +1,6 @@
DATAPATH = $(top_srcdir)/data
-pkgdata_DATA = GL27 nad.lst nad27 nad83 world other.extra \
+pkgdata_DATA = proj.ini GL27 nad.lst nad27 nad83 world other.extra \
CH null \
ITRF2000 ITRF2008 ITRF2014 proj.db \
projjson.schema.json
@@ -38,7 +38,7 @@ SQL_ORDERED_LIST = sql/begin.sql \
sql/customizations.sql \
sql/commit.sql
-EXTRA_DIST = GL27 nad.lst nad27 nad83 \
+EXTRA_DIST = proj.ini GL27 nad.lst nad27 nad83 \
world other.extra \
CH \
ITRF2000 ITRF2008 ITRF2014 \
diff --git a/data/proj.ini b/data/proj.ini
new file mode 100644
index 00000000..f42bc940
--- /dev/null
+++ b/data/proj.ini
@@ -0,0 +1,10 @@
+[general]
+; Lines starting by ; are commented lines.
+;
+
+; Network capabilities disabled by default.
+; Can be overriden with the PROJ_NETWORK=ON environment variable.
+; network = on
+
+; Can be overriden with the PROJ_NETWORK_ENDPOINT environment variable.
+cdn_endpoint = https://cdn.proj.org
diff --git a/docs/source/resource_files.rst b/docs/source/resource_files.rst
index 69cf2f95..ea02fd4b 100644
--- a/docs/source/resource_files.rst
+++ b/docs/source/resource_files.rst
@@ -54,6 +54,30 @@ A proj installation includes a SQLite database of transformation information
that must be accessible for the library to work properly. The library will
print an error if the database can't be found.
+proj.ini
+-------------------------------------------------------------------------------
+
+.. versionadded:: 7.0
+
+proj.ini is a text configuration file, mostly dedicated at setting up network
+related parameters.
+
+Its default content is:
+
+::
+
+ [general]
+ ; Lines starting by ; are commented lines.
+ ;
+
+ ; Network capabilities disabled by default.
+ ; Can be overriden with the PROJ_NETWORK=ON environment variable.
+ ; network = on
+
+ ; Can be overriden with the PROJ_NETWORK_ENDPOINT environment variable.
+ cdn_endpoint = https://cdn.proj.org
+
+
Transformation grids
-------------------------------------------------------------------------------
diff --git a/docs/source/usage/environmentvars.rst b/docs/source/usage/environmentvars.rst
index 00058716..24ae4a45 100644
--- a/docs/source/usage/environmentvars.rst
+++ b/docs/source/usage/environmentvars.rst
@@ -65,3 +65,12 @@ done by setting the variable with no content::
(Content Delivery Network) storage, when grids are not available locally.
Alternatively, the :c:func:`proj_context_set_enable_network` function can
be used.
+
+.. envvar:: PROJ_NETWORK_ENDPOINT
+
+ .. versionadded:: 7.0.0
+
+ Define the endpoint of the CDN storage. Normally defined through the proj.ini
+ configuration file locale in PROJ_LIB.
+ Alternatively, the :c:func:`proj_context_set_url_endpoint` function can
+ be used.
diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt
index d2724825..3e17a3c7 100644
--- a/scripts/reference_exported_symbols.txt
+++ b/scripts/reference_exported_symbols.txt
@@ -800,6 +800,7 @@ proj_context_set_file_finder
proj_context_set_network_callbacks
proj_context_set(PJconsts*, projCtx_t*)
proj_context_set_search_paths
+proj_context_set_url_endpoint
proj_context_use_proj4_init_rules
proj_convert_conversion_to_other_method
proj_coord
diff --git a/src/filemanager.cpp b/src/filemanager.cpp
index d9a02632..97d7369e 100644
--- a/src/filemanager.cpp
+++ b/src/filemanager.cpp
@@ -772,6 +772,9 @@ int proj_context_set_network_callbacks(
/** Enable or disable network access.
*
+* This overrides the default endpoint in the PROJ configuration file or with
+* the PROJ_NETWORK environment variable.
+*
* @param ctx PROJ context, or NULL
* @param enable TRUE if network access is allowed.
* @return TRUE if network access is possible. That is either libcurl is
@@ -781,6 +784,8 @@ int proj_context_set_enable_network(PJ_CONTEXT *ctx, int enable) {
if (ctx == nullptr) {
ctx = pj_get_default_ctx();
}
+ // Load ini file, now so as to override its network settings
+ pj_load_ini(ctx);
ctx->networking.enabled_env_variable_checked = true;
ctx->networking.enabled = enable != FALSE;
#ifdef CURL_ENABLED
@@ -793,6 +798,25 @@ int proj_context_set_enable_network(PJ_CONTEXT *ctx, int enable) {
// ---------------------------------------------------------------------------
+/** Define the URL endpoint to query for remote grids.
+*
+* This overrides the default endpoint in the PROJ configuration file or with
+* the PROJ_NETWORK_ENDPOINT environment variable.
+*
+* @param ctx PROJ context, or NULL
+* @param url Endpoint URL. Must NOT be NULL.
+*/
+void proj_context_set_url_endpoint(PJ_CONTEXT *ctx, const char *url) {
+ if (ctx == nullptr) {
+ ctx = pj_get_default_ctx();
+ }
+ // Load ini file, now so as to override its network settings
+ pj_load_ini(ctx);
+ ctx->endpoint = url;
+}
+
+// ---------------------------------------------------------------------------
+
//! @cond Doxygen_Suppress
bool pj_context_is_network_enabled(PJ_CONTEXT *ctx) {
@@ -808,6 +832,7 @@ bool pj_context_is_network_enabled(PJ_CONTEXT *ctx) {
ci_equal(enabled, "YES") ||
ci_equal(enabled, "TRUE");
}
+ pj_load_ini(ctx);
ctx->networking.enabled_env_variable_checked = true;
return ctx->networking.enabled;
}
diff --git a/src/open_lib.cpp b/src/open_lib.cpp
index 926505ba..cde5be7b 100644
--- a/src/open_lib.cpp
+++ b/src/open_lib.cpp
@@ -366,16 +366,21 @@ std::unique_ptr<NS_PROJ::File> NS_PROJ::FileManager::open_resource_file(
!starts_with(name, "http://") &&
!starts_with(name, "https://") &&
pj_context_is_network_enabled(ctx) ) {
- std::string remote_file("https://cdn.proj.org/");
- remote_file += name;
- auto pos = remote_file.rfind('.');
- if( pos + 4 == remote_file.size() ) {
- remote_file = remote_file.substr(0, pos) + ".tif";
- file = open(ctx, remote_file.c_str());
- if( file ) {
- pj_log( ctx, PJ_LOG_DEBUG_MAJOR,
- "Using %s", remote_file.c_str() );
- pj_ctx_set_errno( ctx, 0 );
+ std::string remote_file(pj_context_get_url_endpoint(ctx));
+ if( !remote_file.empty() ) {
+ if( remote_file.back() != '/' ) {
+ remote_file += '/';
+ }
+ remote_file += name;
+ auto pos = remote_file.rfind('.');
+ if( pos + 4 == remote_file.size() ) {
+ remote_file = remote_file.substr(0, pos) + ".tif";
+ file = open(ctx, remote_file.c_str());
+ if( file ) {
+ pj_log( ctx, PJ_LOG_DEBUG_MAJOR,
+ "Using %s", remote_file.c_str() );
+ pj_ctx_set_errno( ctx, 0 );
+ }
}
}
}
@@ -433,3 +438,91 @@ int pj_find_file(projCtx ctx, const char *short_filename,
}
return 0;
}
+
+/************************************************************************/
+/* pj_context_get_url_endpoint() */
+/************************************************************************/
+
+std::string pj_context_get_url_endpoint(PJ_CONTEXT* ctx)
+{
+ if( !ctx->endpoint.empty() ) {
+ return ctx->endpoint;
+ }
+ pj_load_ini(ctx);
+ return ctx->endpoint;
+}
+
+/************************************************************************/
+/* trim() */
+/************************************************************************/
+
+static std::string trim(const std::string& s) {
+ const auto first = s.find_first_not_of(' ');
+ const auto last = s.find_last_not_of(' ');
+ if( first == std::string::npos || last == std::string::npos ) {
+ return std::string();
+ }
+ return s.substr(first, last - first + 1);
+}
+
+/************************************************************************/
+/* pj_load_ini() */
+/************************************************************************/
+
+void pj_load_ini(projCtx ctx)
+{
+ if( ctx->iniFileLoaded )
+ return;
+
+ const char* endpoint_from_env = getenv("PROJ_NETWORK_ENDPOINT");
+ if( endpoint_from_env && endpoint_from_env[0] != '\0' ) {
+ ctx->endpoint = endpoint_from_env;
+ }
+
+ ctx->iniFileLoaded = true;
+ auto file = std::unique_ptr<NS_PROJ::File>(
+ reinterpret_cast<NS_PROJ::File*>(
+ pj_open_lib_internal(ctx, "proj.ini", "rb",
+ pj_open_file_with_manager,
+ nullptr, 0)));
+ if( !file )
+ return;
+ file->seek(0, SEEK_END);
+ const auto filesize = file->tell();
+ if( filesize == 0 || filesize > 100 * 1024U )
+ return;
+ file->seek(0, SEEK_SET);
+ std::string content;
+ content.resize(static_cast<size_t>(filesize));
+ const auto nread = file->read(&content[0], content.size());
+ if( nread != content.size() )
+ return;
+ content += '\n';
+ size_t pos = 0;
+ while( pos != std::string::npos ) {
+ const auto eol = content.find_first_of("\r\n", pos);
+ if( eol == std::string::npos ) {
+ break;
+ }
+
+ const auto equal = content.find('=', pos);
+ if( equal < eol )
+ {
+ const auto key = trim(content.substr(pos, equal-pos));
+ const auto value = trim(content.substr(equal + 1,
+ eol - (equal+1)));
+ if( ctx->endpoint.empty() && key == "cdn_endpoint" ) {
+ ctx->endpoint = value;
+ } else if ( key == "network" ) {
+ const char *enabled = getenv("PROJ_NETWORK");
+ if (enabled == nullptr || enabled[0] == '\0') {
+ ctx->networking.enabled = ci_equal(value, "ON") ||
+ ci_equal(value, "YES") ||
+ ci_equal(value, "TRUE");
+ }
+ }
+ }
+
+ pos = content.find_first_not_of("\r\n", eol);
+ }
+}
diff --git a/src/proj.h b/src/proj.h
index 7210318c..3f0879bb 100644
--- a/src/proj.h
+++ b/src/proj.h
@@ -424,6 +424,8 @@ int PROJ_DLL proj_context_set_network_callbacks(
int PROJ_DLL proj_context_set_enable_network(PJ_CONTEXT* ctx,
int enabled);
+void PROJ_DLL proj_context_set_url_endpoint(PJ_CONTEXT* ctx, const char* url);
+
/*! @cond Doxygen_Suppress */
/* Manage the transformation definition object PJ */
diff --git a/src/proj_internal.h b/src/proj_internal.h
index 983f1c07..669fd2b5 100644
--- a/src/proj_internal.h
+++ b/src/proj_internal.h
@@ -699,6 +699,9 @@ struct projCtx_t {
projNetworkCallbacksAndData networking{};
bool defer_grid_opening = false; // set by pj_obj_create()
+ bool iniFileLoaded = false;
+ std::string endpoint{};
+
int projStringParserCreateFromPROJStringRecursionCounter = 0; // to avoid potential infinite recursion in PROJStringParser::createFromPROJString()
projCtx_t() = default;
@@ -830,6 +833,10 @@ void pj_pipeline_assign_context_to_steps( PJ* P, PJ_CONTEXT* ctx );
// For use by projinfo
bool PROJ_DLL pj_context_is_network_enabled(PJ_CONTEXT* ctx);
+std::string pj_context_get_url_endpoint(PJ_CONTEXT* ctx);
+
+void pj_load_ini(PJ_CONTEXT* ctx);
+
/* classic public API */
#include "proj_api.h"
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 23d54663..0dcafc85 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -160,5 +160,10 @@ target_link_libraries(test_network
GTest::gtest
${PROJ_LIBRARIES})
add_test(NAME test_network COMMAND test_network)
-set_property(TEST test_network
- PROPERTY ENVIRONMENT "PROJ_LIB=${PROJECT_BINARY_DIR}/data;PROJ_SOURCE_DATA=${PROJECT_SOURCE_DIR}/data")
+if(MSVC)
+ set_property(TEST test_network
+ PROPERTY ENVIRONMENT "PROJ_LIB=${PROJECT_BINARY_DIR}/data\\;${PROJECT_SOURCE_DIR}/data;PROJ_SOURCE_DATA=${PROJECT_SOURCE_DIR}/data")
+else()
+ set_property(TEST test_network
+ PROPERTY ENVIRONMENT "PROJ_LIB=${PROJECT_BINARY_DIR}/data:${PROJECT_SOURCE_DIR}/data;PROJ_SOURCE_DATA=${PROJECT_SOURCE_DIR}/data")
+endif()
diff --git a/test/unit/test_network.cpp b/test/unit/test_network.cpp
index c3372ca9..3e64329e 100644
--- a/test/unit/test_network.cpp
+++ b/test/unit/test_network.cpp
@@ -684,4 +684,59 @@ TEST(networking, curl_vgridshift_vertcon) {
#endif
+// ---------------------------------------------------------------------------
+
+#ifdef CURL_ENABLED
+
+TEST(networking, network_endpoint_env_variable) {
+ putenv(const_cast<char *>("PROJ_NETWORK_ENDPOINT=http://0.0.0.0/"));
+ auto ctx = proj_context_create();
+ proj_context_set_enable_network(ctx, true);
+
+ // NAD83 to NAD83(HARN) in West-Virginia. Using wvhpgn.tif
+ auto P = proj_create_crs_to_crs(ctx, "EPSG:4269", "EPSG:4152", nullptr);
+ ASSERT_NE(P, nullptr);
+
+ PJ_COORD c;
+ c.xyz.x = 40; // lat
+ c.xyz.y = -80; // lon
+ c.xyz.z = 0;
+ c = proj_trans(P, PJ_FWD, c);
+ putenv(const_cast<char *>("PROJ_NETWORK_ENDPOINT="));
+
+ proj_destroy(P);
+ proj_context_destroy(ctx);
+
+ EXPECT_EQ(c.xyz.x, HUGE_VAL);
+}
+
+#endif
+
+// ---------------------------------------------------------------------------
+
+#ifdef CURL_ENABLED
+
+TEST(networking, network_endpoint_api) {
+ auto ctx = proj_context_create();
+ proj_context_set_enable_network(ctx, true);
+ proj_context_set_url_endpoint(ctx, "http://0.0.0.0");
+
+ // NAD83 to NAD83(HARN) in West-Virginia. Using wvhpgn.tif
+ auto P = proj_create_crs_to_crs(ctx, "EPSG:4269", "EPSG:4152", nullptr);
+ ASSERT_NE(P, nullptr);
+
+ PJ_COORD c;
+ c.xyz.x = 40; // lat
+ c.xyz.y = -80; // lon
+ c.xyz.z = 0;
+ c = proj_trans(P, PJ_FWD, c);
+
+ proj_destroy(P);
+ proj_context_destroy(ctx);
+
+ EXPECT_EQ(c.xyz.x, HUGE_VAL);
+}
+
+#endif
+
} // namespace