diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2019-12-19 21:24:53 +0100 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2019-12-19 22:31:37 +0100 |
| commit | a06f4a258f618dbad2ce01feadab6908db00bda5 (patch) | |
| tree | 71497daf091671b478abf90bda5a3a222c5e91eb /src | |
| parent | 2aeb122fb2720fa6809b9429aca5154f8eec5e6d (diff) | |
| download | PROJ-a06f4a258f618dbad2ce01feadab6908db00bda5.tar.gz PROJ-a06f4a258f618dbad2ce01feadab6908db00bda5.zip | |
Add proj_context_set_network_callbacks() with an empty implementation
Diffstat (limited to 'src')
| -rw-r--r-- | src/ctx.cpp | 3 | ||||
| -rw-r--r-- | src/filemanager.cpp | 164 | ||||
| -rw-r--r-- | src/filemanager.hpp | 6 | ||||
| -rw-r--r-- | src/open_lib.cpp | 6 | ||||
| -rw-r--r-- | src/proj.h | 76 | ||||
| -rw-r--r-- | src/proj_internal.h | 13 |
6 files changed, 267 insertions, 1 deletions
diff --git a/src/ctx.cpp b/src/ctx.cpp index da90d4d7..a7cf8cb3 100644 --- a/src/ctx.cpp +++ b/src/ctx.cpp @@ -33,6 +33,7 @@ #include "proj_experimental.h" #include "proj_internal.h" +#include "filemanager.hpp" /************************************************************************/ /* pj_get_ctx() */ @@ -96,6 +97,7 @@ projCtx_t projCtx_t::createDefault() ctx.debug_level = PJ_LOG_NONE; ctx.logger = pj_stderr_logger; ctx.fileapi_legacy = pj_get_default_fileapi(); + NS_PROJ::FileManager::fillDefaultNetworkInterface(&ctx); if( getenv("PROJ_DEBUG") != nullptr ) { @@ -139,6 +141,7 @@ projCtx_t::projCtx_t(const projCtx_t& other) file_finder = other.file_finder; file_finder_legacy = other.file_finder_legacy; file_finder_user_data = other.file_finder_user_data; + networking = other.networking; } /************************************************************************/ diff --git a/src/filemanager.cpp b/src/filemanager.cpp index b6164519..97f6a4dc 100644 --- a/src/filemanager.cpp +++ b/src/filemanager.cpp @@ -25,11 +25,21 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ +#ifndef FROM_PROJ_CPP +#define FROM_PROJ_CPP +#endif + #include "filemanager.hpp" +#include "proj.h" +#include "proj/internal/internal.hpp" #include "proj_internal.h" +//! @cond Doxygen_Suppress + NS_PROJ_START +using namespace internal; + // --------------------------------------------------------------------------- File::File() = default; @@ -160,6 +170,79 @@ std::unique_ptr<File> FileLegacyAdapter::open(PJ_CONTEXT *ctx, // --------------------------------------------------------------------------- +class NetworkFile : public File { + PJ_CONTEXT *m_ctx; + PROJ_NETWORK_HANDLE *m_handle; + unsigned long long m_pos = 0; + + NetworkFile(const NetworkFile &) = delete; + NetworkFile &operator=(const NetworkFile &) = delete; + + protected: + NetworkFile(PJ_CONTEXT *ctx, PROJ_NETWORK_HANDLE *handle) + : m_ctx(ctx), m_handle(handle) {} + + public: + ~NetworkFile() override; + + size_t read(void *buffer, size_t sizeBytes) override; + bool seek(unsigned long long offset, int whence) override; + unsigned long long tell() override; + + static std::unique_ptr<File> open(PJ_CONTEXT *ctx, const char *filename); +}; + +// --------------------------------------------------------------------------- + +std::unique_ptr<File> NetworkFile::open(PJ_CONTEXT *ctx, const char *filename) { + std::vector<unsigned char> buffer(16 * 1024); + size_t size_read = 0; + auto handle = ctx->networking.open(ctx, filename, buffer.size(), &buffer[0], + &size_read, ctx->networking.user_data); + return std::unique_ptr<File>(handle ? new NetworkFile(ctx, handle) + : nullptr); +} + +// --------------------------------------------------------------------------- + +size_t NetworkFile::read(void *buffer, size_t sizeBytes) { + size_t nRead = m_ctx->networking.read_range( + m_ctx, m_handle, m_pos, sizeBytes, buffer, m_ctx->networking.user_data); + m_pos += nRead; + return nRead; +} + +// --------------------------------------------------------------------------- + +bool NetworkFile::seek(unsigned long long offset, int whence) { + if (whence == SEEK_SET) { + m_pos = offset; + } else if (whence == SEEK_CUR) { + m_pos += offset; + } else { + if (offset != 0) + return false; + const auto filesize = m_ctx->networking.get_file_size( + m_ctx, m_handle, m_ctx->networking.user_data); + if (filesize == 0) + return false; + m_pos = filesize; + } + return true; +} + +// --------------------------------------------------------------------------- + +unsigned long long NetworkFile::tell() { return m_pos; } + +// --------------------------------------------------------------------------- + +NetworkFile::~NetworkFile() { + m_ctx->networking.close(m_ctx, m_handle, m_ctx->networking.user_data); +} + +// --------------------------------------------------------------------------- + std::unique_ptr<File> FileManager::open(PJ_CONTEXT *ctx, const char *filename) { #ifndef REMOVE_LEGACY_SUPPORT // If the user has specified a legacy fileapi, use it @@ -167,7 +250,88 @@ std::unique_ptr<File> FileManager::open(PJ_CONTEXT *ctx, const char *filename) { return FileLegacyAdapter::open(ctx, filename); } #endif + if (starts_with(filename, "http://") || starts_with(filename, "https://")) { + return NetworkFile::open(ctx, filename); + } return FileStdio::open(ctx, filename); } +// --------------------------------------------------------------------------- + +static PROJ_NETWORK_HANDLE * +no_op_network_open(PJ_CONTEXT *ctx, const char * /* url */, + size_t, /* size to read */ + void *, /* buffer to update with bytes read*/ + size_t *, /* output: size actually read */ + void * /*user_data*/) { + pj_log(ctx, PJ_LOG_DEBUG_MAJOR, "Network functionality not available"); + return nullptr; +} + +// --------------------------------------------------------------------------- + +static void no_op_network_close(PJ_CONTEXT *, PROJ_NETWORK_HANDLE *, + void * /*user_data*/) {} + +// --------------------------------------------------------------------------- + +static const char *no_op_network_get_last_error(PJ_CONTEXT *, + PROJ_NETWORK_HANDLE *, + void * /*user_data*/) { + return "Network functionality not available"; +} + +// --------------------------------------------------------------------------- + +void FileManager::fillDefaultNetworkInterface(PJ_CONTEXT *ctx) { + ctx->networking.open = no_op_network_open; + ctx->networking.close = no_op_network_close; + ctx->networking.get_last_error = no_op_network_get_last_error; +} + +// --------------------------------------------------------------------------- + NS_PROJ_END + +//! @endcond + +// --------------------------------------------------------------------------- + +/** Define a custom set of callbacks for network access. + * + * All callbacks should be provided (non NULL pointers). + * + * @param ctx PROJ context, or NULL + * @param open_cbk Callback to open a remote file given its URL + * @param close_cbk Callback to close a remote file. + * @param get_header_value_cbk Callback to get HTTP headers + * @param get_file_size_cbk Callback to get the size of the remote file. + * @param read_range_cbk Callback to read a range of bytes inside a remote file. + * @param get_last_error_cbk Callback to get last error message. + * @param user_data Arbitrary pointer provided by the user, and passed to the + * above callbacks. May be NULL. + * @return TRUE in case of success. + */ +int proj_context_set_network_callbacks( + PJ_CONTEXT *ctx, proj_network_open_cbk_type open_cbk, + proj_network_close_cbk_type close_cbk, + proj_network_get_header_value_cbk_type get_header_value_cbk, + proj_network_get_file_size_cbk_type get_file_size_cbk, + proj_network_read_range_type read_range_cbk, + proj_network_get_last_error_type get_last_error_cbk, void *user_data) { + if (ctx == nullptr) { + ctx = pj_get_default_ctx(); + } + if (!open_cbk || !close_cbk || !get_header_value_cbk || + !get_file_size_cbk || !read_range_cbk || !get_last_error_cbk) { + return false; + } + ctx->networking.open = open_cbk; + ctx->networking.close = close_cbk; + ctx->networking.get_header_value = get_header_value_cbk; + ctx->networking.get_file_size = get_file_size_cbk; + ctx->networking.read_range = read_range_cbk; + ctx->networking.get_last_error = get_last_error_cbk; + ctx->networking.user_data = user_data; + return true; +} diff --git a/src/filemanager.hpp b/src/filemanager.hpp index 19478c48..0e830450 100644 --- a/src/filemanager.hpp +++ b/src/filemanager.hpp @@ -35,6 +35,8 @@ NS_PROJ_START +//! @cond Doxygen_Suppress + class File; class FileManager { @@ -48,6 +50,8 @@ class FileManager { // "High-level" interface, honoring PROJ_LIB and the like. static std::unique_ptr<File> open_resource_file(PJ_CONTEXT *ctx, const char *name); + + static void fillDefaultNetworkInterface(PJ_CONTEXT *ctx); }; // --------------------------------------------------------------------------- @@ -63,6 +67,8 @@ class File { virtual unsigned long long tell() = 0; }; +//! @endcond Doxygen_Suppress + NS_PROJ_END #endif // FILEMANAGER_HPP_INCLUDED
\ No newline at end of file diff --git a/src/open_lib.cpp b/src/open_lib.cpp index e1572754..6bdd5510 100644 --- a/src/open_lib.cpp +++ b/src/open_lib.cpp @@ -53,6 +53,8 @@ PROJ_LIB; nullptr; #endif +using namespace NS_PROJ::internal; + /************************************************************************/ /* pj_set_finder() */ /************************************************************************/ @@ -238,7 +240,9 @@ pj_open_lib_internal(projCtx ctx, const char *name, const char *mode, else if (strchr(dir_chars,*name) || (*name == '.' && strchr(dir_chars,name[1])) || (!strncmp(name, "..", 2) && strchr(dir_chars,name[2])) - || (name[0] != '\0' && name[1] == ':' && strchr(dir_chars,name[2])) ) + || (name[0] != '\0' && name[1] == ':' && strchr(dir_chars,name[2])) + || starts_with(name, "http://") + || starts_with(name, "https://")) sysname = name; /* or try to use application provided file finder */ @@ -353,6 +353,82 @@ void PROJ_DLL proj_context_set_search_paths(PJ_CONTEXT *ctx, int count_paths, co void PROJ_DLL proj_context_use_proj4_init_rules(PJ_CONTEXT *ctx, int enable); int PROJ_DLL proj_context_get_use_proj4_init_rules(PJ_CONTEXT *ctx, int from_legacy_code_path); +/*! @endcond */ + +/** Opaque structure for PROJ. Implementations might cast it to their + * structure/class of choice. */ +typedef struct PROJ_NETWORK_HANDLE PROJ_NETWORK_HANDLE; + +/** Network access: open callback + * + * Should try to read the size_to_read first bytes of the file of URL url, + * and write them to buffer. *out_size_read should be updated with the actual + * amount of bytes read (== size_to_read if the file is larger than size_to_read). + * During this read, the implementation should make sure to store the HTTP + * headers from the server response to be able to respond to + * proj_network_get_header_value_cbk_type and proj_network_get_file_size_cbk_type + * callbacks. + * + * @return a non-NULL opaque handle in case of success. + */ +typedef PROJ_NETWORK_HANDLE* (*proj_network_open_cbk_type)( + PJ_CONTEXT* ctx, + const char* url, + size_t size_to_read, + void* buffer, + size_t* out_size_read, + void* user_data); + +/** Network access: close callback */ +typedef void (*proj_network_close_cbk_type)(PJ_CONTEXT* ctx, + PROJ_NETWORK_HANDLE* handle, + void* user_data); + +/** Network access: get HTTP headers */ +typedef const char* (*proj_network_get_header_value_cbk_type)( + PJ_CONTEXT* ctx, + PROJ_NETWORK_HANDLE* handle, + const char* header_name, + void* user_data); + +/** Network access: get file size */ +typedef unsigned long long (*proj_network_get_file_size_cbk_type)( + PJ_CONTEXT* ctx, + PROJ_NETWORK_HANDLE* handle, + void* user_data); + +/** Network access: read range + * + * Read size_to_read bytes from handle, starting at offset, into + * buffer. + * @return the number of bytes actually read (0 in case of error) + */ +typedef size_t (*proj_network_read_range_type)( + PJ_CONTEXT* ctx, + PROJ_NETWORK_HANDLE* handle, + unsigned long long offset, + size_t size_to_read, + void* buffer, + void* user_data); + +/** Network access: get last error message */ +typedef const char* (*proj_network_get_last_error_type)( + PJ_CONTEXT* ctx, + PROJ_NETWORK_HANDLE*, + void* user_data); + +int PROJ_DLL proj_context_set_network_callbacks( + PJ_CONTEXT* ctx, + proj_network_open_cbk_type open_cbk, + proj_network_close_cbk_type close_cbk, + proj_network_get_header_value_cbk_type get_header_value_cbk, + proj_network_get_file_size_cbk_type get_file_size_cbk, + proj_network_read_range_type read_range_cbk, + proj_network_get_last_error_type get_last_error_cbk, + void* user_data); + +/*! @cond Doxygen_Suppress */ + /* Manage the transformation definition object PJ */ PJ PROJ_DLL *proj_create (PJ_CONTEXT *ctx, const char *definition); PJ PROJ_DLL *proj_create_argv (PJ_CONTEXT *ctx, int argc, char **argv); diff --git a/src/proj_internal.h b/src/proj_internal.h index 93134946..aefeea39 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -666,6 +666,17 @@ struct projFileAPI_t; struct projCppContext; +struct projNetworkCallbacksAndData +{ + proj_network_open_cbk_type open = nullptr; + proj_network_close_cbk_type close = nullptr; + proj_network_get_header_value_cbk_type get_header_value = nullptr; + proj_network_get_file_size_cbk_type get_file_size = nullptr; + proj_network_read_range_type read_range = nullptr; + proj_network_get_last_error_type get_last_error = nullptr; + void* user_data = nullptr; +}; + /* proj thread context */ struct projCtx_t { int last_errno = 0; @@ -684,6 +695,8 @@ struct projCtx_t { const char* (*file_finder) (PJ_CONTEXT *, const char*, void* user_data) = nullptr; void* file_finder_user_data = nullptr; + projNetworkCallbacksAndData networking{}; + int projStringParserCreateFromPROJStringRecursionCounter = 0; // to avoid potential infinite recursion in PROJStringParser::createFromPROJString() projCtx_t() = default; |
