diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 5b4bfc4ec8..be211e809a 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -3324,14 +3324,18 @@ bool DatabaseContext::lookForGridInfo( url.clear(); openLicense = false; directDownload = false; - - fullFilename.resize(2048); - int errno_before = proj_context_errno(ctxt); - gridAvailable = NS_PROJ::FileManager::open_resource_file( - ctxt, projFilename.c_str(), &fullFilename[0], - fullFilename.size() - 1) != nullptr; - proj_context_errno_set(ctxt, errno_before); - fullFilename.resize(strlen(fullFilename.c_str())); + gridAvailable = false; + + const auto resolveFullFilename = [ctxt, &fullFilename, &projFilename]() { + fullFilename.resize(2048); + const int errno_before = proj_context_errno(ctxt); + bool lGridAvailable = NS_PROJ::FileManager::open_resource_file( + ctxt, projFilename.c_str(), &fullFilename[0], + fullFilename.size() - 1) != nullptr; + proj_context_errno_set(ctxt, errno_before); + fullFilename.resize(strlen(fullFilename.c_str())); + return lGridAvailable; + }; auto res = d->run("SELECT " @@ -3363,7 +3367,7 @@ bool DatabaseContext::lookForGridInfo( old_proj_grid_name == projFilename) { std::string fullFilenameNewName; fullFilenameNewName.resize(2048); - errno_before = proj_context_errno(ctxt); + const int errno_before = proj_context_errno(ctxt); bool gridAvailableWithNewName = pj_find_file(ctxt, proj_grid_name.c_str(), &fullFilenameNewName[0], @@ -3376,8 +3380,17 @@ bool DatabaseContext::lookForGridInfo( } } - if (considerKnownGridsAsAvailable && + if (!gridAvailable && considerKnownGridsAsAvailable && (!packageName.empty() || (!url.empty() && openLicense))) { + + // In considerKnownGridsAsAvailable mode, try to fetch the local + // file name if it exists, but do not attempt network access. + const auto network_was_enabled = + proj_context_is_network_enabled(ctxt); + proj_context_set_enable_network(ctxt, false); + (void)resolveFullFilename(); + proj_context_set_enable_network(ctxt, network_was_enabled); + gridAvailable = true; } @@ -3391,7 +3404,13 @@ bool DatabaseContext::lookForGridInfo( } info.directDownload = directDownload; info.openLicense = openLicense; + + if (!gridAvailable) { + gridAvailable = resolveFullFilename(); + } } else { + gridAvailable = resolveFullFilename(); + if (starts_with(fullFilename, "http://") || starts_with(fullFilename, "https://")) { url = fullFilename; diff --git a/test/unit/test_network.cpp b/test/unit/test_network.cpp index e5a3946f62..8eaeb3da80 100644 --- a/test/unit/test_network.cpp +++ b/test/unit/test_network.cpp @@ -1264,26 +1264,28 @@ TEST(networking, network_endpoint_api) { static PROJ_NETWORK_HANDLE *dummy_open_cbk(PJ_CONTEXT *, const char *, unsigned long long, size_t, void *, - size_t *, size_t, char *, void *) { - assert(false); + size_t *, size_t, char *, + void *pUserData) { + *static_cast(pUserData) = true; return nullptr; } -static void dummy_close_cbk(PJ_CONTEXT *, PROJ_NETWORK_HANDLE *, void *) { - assert(false); +static void dummy_close_cbk(PJ_CONTEXT *, PROJ_NETWORK_HANDLE *, + void *pUserData) { + *static_cast(pUserData) = true; } static const char *dummy_get_header_value_cbk(PJ_CONTEXT *, PROJ_NETWORK_HANDLE *, - const char *, void *) { - assert(false); + const char *, void *pUserData) { + *static_cast(pUserData) = true; return nullptr; } static size_t dummy_read_range_cbk(PJ_CONTEXT *, PROJ_NETWORK_HANDLE *, unsigned long long, size_t, void *, size_t, - char *, void *) { - assert(false); + char *, void *pUserData) { + *static_cast(pUserData) = true; return 0; } @@ -1332,12 +1334,14 @@ TEST(networking, cache_basic) { proj_cleanup(); // Check that a second access doesn't trigger any network activity + bool networkActivity = false; ASSERT_TRUE(proj_context_set_network_callbacks( ctx, dummy_open_cbk, dummy_close_cbk, dummy_get_header_value_cbk, - dummy_read_range_cbk, nullptr)); + dummy_read_range_cbk, &networkActivity)); P = proj_create(ctx, pipeline); ASSERT_NE(P, nullptr); proj_destroy(P); + EXPECT_FALSE(networkActivity); proj_context_destroy(ctx); } @@ -2020,4 +2024,50 @@ TEST(networking, pyproj_issue_1192) { #endif +// --------------------------------------------------------------------------- + +#ifdef CURL_ENABLED + +TEST(networking, do_not_attempt_network_access_known_available_network_on) { + + // Check that proj_create_operations() itself does not trigger network + // activity in enable_network == true and + // PROJ_GRID_AVAILABILITY_KNOWN_AVAILABLE mode when all grids are known. + + const auto doTest = [](PJ_CONTEXT *ctxt) { + auto factory_context = + proj_create_operation_factory_context(ctxt, nullptr); + proj_operation_factory_context_set_grid_availability_use( + ctxt, factory_context, PROJ_GRID_AVAILABILITY_KNOWN_AVAILABLE); + proj_operation_factory_context_set_spatial_criterion( + ctxt, factory_context, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION); + auto from = proj_create(ctxt, "EPSG:4326"); + auto to = proj_create(ctxt, "EPSG:4267"); + auto pj_operations = + proj_create_operations(ctxt, from, to, factory_context); + proj_destroy(from); + proj_destroy(to); + auto num_operations = proj_list_get_count(pj_operations); + EXPECT_GE(num_operations, 10); + proj_operation_factory_context_destroy(factory_context); + proj_list_destroy(pj_operations); + }; + + auto ctx = proj_context_create(); + proj_context_set_enable_network(ctx, true); + + // Check that we don't trigger any network activity + bool networkActivity = false; + ASSERT_TRUE(proj_context_set_network_callbacks( + ctx, dummy_open_cbk, dummy_close_cbk, dummy_get_header_value_cbk, + dummy_read_range_cbk, &networkActivity)); + + doTest(ctx); + EXPECT_FALSE(networkActivity); + + proj_context_destroy(ctx); +} + +#endif + } // namespace