diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2019-12-25 20:13:03 +0100 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2019-12-27 11:14:16 +0100 |
| commit | 2093aca0720949303410280912b61efd791d2f01 (patch) | |
| tree | a69c5392ed6689a88d9d959ddb41107413efacc6 | |
| parent | c4589fbe42e5fea07a03919d3484164f5fb70dd3 (diff) | |
| download | PROJ-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.am | 4 | ||||
| -rw-r--r-- | data/proj.ini | 10 | ||||
| -rw-r--r-- | docs/source/resource_files.rst | 24 | ||||
| -rw-r--r-- | docs/source/usage/environmentvars.rst | 9 | ||||
| -rw-r--r-- | scripts/reference_exported_symbols.txt | 1 | ||||
| -rw-r--r-- | src/filemanager.cpp | 25 | ||||
| -rw-r--r-- | src/open_lib.cpp | 113 | ||||
| -rw-r--r-- | src/proj.h | 2 | ||||
| -rw-r--r-- | src/proj_internal.h | 7 | ||||
| -rw-r--r-- | test/unit/CMakeLists.txt | 9 | ||||
| -rw-r--r-- | test/unit/test_network.cpp | 55 |
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); + } +} @@ -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 |
