diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..b8f9cff --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,24 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: 'bug' +assignees: '' + +--- + +**Description** +A clear and concise description of what the bug is. + +**Steps To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..8ddbe0a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when \[...\] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.gitignore b/.gitignore index 7921596..ddc4140 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -/build/ /.vscode/ +/build/ +/builddir/ /subprojects/* !/subprojects/*.wrap \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 5746be7..5ebb129 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,13 +19,17 @@ addons: - python3-setuptools - python3-wheel - ninja-build + - lcov before_install: - sudo pip3 install --system meson before_script: - - meson build + - meson -Db_coverage=true build script: - cd build - - ninja \ No newline at end of file + - meson test + +after_success: + - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..6b5cffd --- /dev/null +++ b/AUTHORS @@ -0,0 +1,7 @@ +# This is the list of webfuse-provider authors for copyright purposes. +# +# This does not necessarily list everyone who has contributed code, since in +# some cases, their employer may be the copyright holder. To see the full list +# of contributors, see the revision history in source control. +Falk Werner +Osama El Hosami diff --git a/LICENSE b/COPYING.LESSER similarity index 100% rename from LICENSE rename to COPYING.LESSER diff --git a/README.md b/README.md index 6074533..cdc0c15 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ [![Build Status](https://travis-ci.org/falk-werner/webfuse-provider.svg?branch=master)](https://travis-ci.org/falk-werner/webfuse-provider) +[![codecov](https://codecov.io/gh/falk-werner/webfuse-provider/branch/master/graph/badge.svg)](https://codecov.io/gh/falk-werner/webfuse-provider) # webfuse-provider -Reference implementation of webfuse provider +webfuse combines libwebsockets and libfuse. It allows ot attach a remote filesystem via websockets. ## Build and run @@ -13,10 +14,12 @@ To install dependecies, see below. ninja ./webfuse-provider -## Dependencies +## Fellow Repositories -- [webfuse](https://github.com/falk-werner/webfuse) - - [libwebsockets](https://libwebsockets.org/) - - [jansson](https://github.com/akheron/jansson) +- **[webfuse-example](https://github.com/falk-werner/webfuse-example)**: Example of webfuse +- **[webfuse](https://github.com/falk-werner/webfuse)**: webfuse adapter library +- **[webfused](https://github.com/falk-werner/webfused)**: Reference implementation of webfuse daemon -All dependencies are bundled using meson wrap files. +## Further Documentation + +- [Webfuse Protocol](https://github.com/falk-werner/webfuse/blob/master/doc/protocol.md) diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..f6cfe1f --- /dev/null +++ b/codecov.yml @@ -0,0 +1,11 @@ +ignore: + - test + - examples + +parsers: + gcov: + branch_detection: + conditional: yes + loop: yes + method: yes + macro: yes \ No newline at end of file diff --git a/src/main.c b/examples/provider/main.c similarity index 95% rename from src/main.c rename to examples/provider/main.c index 261fb9b..97e41a5 100644 --- a/src/main.c +++ b/examples/provider/main.c @@ -9,7 +9,7 @@ #include #include -#include "webfuse_provider.h" +#include "webfuse_provider/webfuse_provider.h" struct config { @@ -193,7 +193,7 @@ static void fs_lookup( } else { - wfp_respond_error(request, WF_BAD_NOENTRY); + wfp_respond_error(request, WFP_BAD_NOENTRY); } } @@ -215,7 +215,7 @@ static void fs_getattr( } else { - wfp_respond_error(request, WF_BAD_NOENTRY); + wfp_respond_error(request, WFP_BAD_NOENTRY); } } @@ -247,7 +247,7 @@ static void fs_readdir( } else { - wfp_respond_error(request, WF_BAD_NOENTRY); + wfp_respond_error(request, WFP_BAD_NOENTRY); } } @@ -268,12 +268,12 @@ static void fs_open( } else { - wfp_respond_error(request, WF_BAD_ACCESS_DENIED); + wfp_respond_error(request, WFP_BAD_ACCESS_DENIED); } } else { - wfp_respond_error(request, WF_BAD_NOENTRY); + wfp_respond_error(request, WFP_BAD_NOENTRY); } } @@ -305,12 +305,12 @@ static void fs_read( } else { - wfp_respond_error(request, WF_BAD); + wfp_respond_error(request, WFP_BAD); } } else { - wfp_respond_error(request, WF_BAD_NOENTRY); + wfp_respond_error(request, WFP_BAD_NOENTRY); } } diff --git a/src/static_filesystem/main.c b/examples/static_filesystem/main.c similarity index 98% rename from src/static_filesystem/main.c rename to examples/static_filesystem/main.c index 6596af4..297028d 100644 --- a/src/static_filesystem/main.c +++ b/examples/static_filesystem/main.c @@ -4,7 +4,7 @@ #include #include -#include "webfuse_provider.h" +#include "webfuse_provider/webfuse_provider.h" #include "static_filesystem.h" struct args diff --git a/src/static_filesystem/path.c b/examples/static_filesystem/path.c similarity index 100% rename from src/static_filesystem/path.c rename to examples/static_filesystem/path.c diff --git a/src/static_filesystem/path.h b/examples/static_filesystem/path.h similarity index 100% rename from src/static_filesystem/path.h rename to examples/static_filesystem/path.h diff --git a/src/static_filesystem/static_filesystem.c b/examples/static_filesystem/static_filesystem.c similarity index 96% rename from src/static_filesystem/static_filesystem.c rename to examples/static_filesystem/static_filesystem.c index 142f515..87b5829 100644 --- a/src/static_filesystem/static_filesystem.c +++ b/examples/static_filesystem/static_filesystem.c @@ -1,8 +1,8 @@ #include "static_filesystem.h" -#include "webfuse/provider/client_config.h" -#include "webfuse/provider/dirbuffer.h" -#include "webfuse/provider/operation/error.h" +#include "webfuse_provider/client_config.h" +#include "webfuse_provider/dirbuffer.h" +#include "webfuse_provider/operation/error.h" #include "path.h" @@ -255,7 +255,7 @@ static void static_filesystem_lookup( } else { - wfp_respond_error(request, WF_BAD_NOENTRY); + wfp_respond_error(request, WFP_BAD_NOENTRY); } } @@ -276,7 +276,7 @@ static void static_filesystem_getattr( } else { - wfp_respond_error(request, WF_BAD_NOENTRY); + wfp_respond_error(request, WFP_BAD_NOENTRY); } } @@ -308,7 +308,7 @@ static void static_filesystem_readdir( } else { - wfp_respond_error(request, WF_BAD_NOENTRY); + wfp_respond_error(request, WFP_BAD_NOENTRY); } } @@ -329,12 +329,12 @@ static void static_filesystem_open( } else { - wfp_respond_error(request, WF_BAD_ACCESS_DENIED); + wfp_respond_error(request, WFP_BAD_ACCESS_DENIED); } } else { - wfp_respond_error(request, WF_BAD_NOENTRY); + wfp_respond_error(request, WFP_BAD_NOENTRY); } } @@ -361,7 +361,7 @@ static void static_filesystem_read( } else { - wfp_respond_error(request, WF_BAD_NOENTRY); + wfp_respond_error(request, WFP_BAD_NOENTRY); } } diff --git a/src/static_filesystem/static_filesystem.h b/examples/static_filesystem/static_filesystem.h similarity index 100% rename from src/static_filesystem/static_filesystem.h rename to examples/static_filesystem/static_filesystem.h diff --git a/include/webfuse_provider/api.h b/include/webfuse_provider/api.h new file mode 100644 index 0000000..9ff0ea7 --- /dev/null +++ b/include/webfuse_provider/api.h @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/api.h +/// \brief API define for webfuse provider. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_API_H +#define WFP_API_H + +//------------------------------------------------------------------------------ +/// \def WFP_API +/// \brief Marks public symbols of libwebfuse_provider. +//------------------------------------------------------------------------------ +#ifndef WFP_API +#define WFP_API +#endif + +//------------------------------------------------------------------------------ +/// \def WFP_EXPORT +/// \brief Marks exported symbols as visible. +/// +/// Set WFP_API to WFP_EXPORT when building libwebfuse_provider.so to export +/// public symbols. +//------------------------------------------------------------------------------ +#ifndef WFP_EXPORT +#ifdef __GNUC__ +#define WFP_EXPORT __attribute__ ((visibility ("default"))) +#else +#define WFP_EXPORT +#endif +#endif + +#endif diff --git a/include/webfuse_provider/client.h b/include/webfuse_provider/client.h new file mode 100644 index 0000000..500293f --- /dev/null +++ b/include/webfuse_provider/client.h @@ -0,0 +1,105 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/client.h +/// \brief Webfuse provider client. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_CLIENT_H +#define WFP_CLIENT_H + +#include "webfuse_provider/api.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +//------------------------------------------------------------------------------ +/// \struct wfp_client +/// \brief Webfuse provider client. +//------------------------------------------------------------------------------ +struct wfp_client; + +struct wfp_client_config; + +//------------------------------------------------------------------------------ +/// \brief Creates a webfuse provider client. +/// +/// \note Client configuration is not managed by the client. +/// +/// \param config pointer to client configuration. +/// \return newly created client or NULL in case of an error. +//------------------------------------------------------------------------------ +extern WFP_API struct wfp_client * wfp_client_create( + struct wfp_client_config * config); + +//------------------------------------------------------------------------------ +/// \brief Connects the client to a remote webfuse adapter server. +/// +/// \note This call starts to establish a connection. A callback is invoked, +/// when the connection is estanlished. +/// +/// \param client pointer to client +/// \param url URL of remote webfuse adapter server +/// +/// \see wfp_connected_fn +/// \see wfp_client_config_set_onconnected +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_connect( + struct wfp_client * client, + char const * url); + +//------------------------------------------------------------------------------ +/// \brief Disconnects a connected client. +/// +/// \note This call starts to disconnect the connection. A callback is invoked +/// when conntection is disconnected. +/// +/// \param client pointer to client +/// +/// \see wfp_disconnected_fn +/// \see wfp_client_config_set_ondisconnected +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_disconnect( + struct wfp_client * client); + +//------------------------------------------------------------------------------ +/// \brief Disposes a client. +/// +/// \note Client configuration is not managed by client. +/// +/// \param client pointer to client +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_dispose( + struct wfp_client * client); + +//------------------------------------------------------------------------------ +/// \brief Triggers the client. +/// +/// This function must be invoked in a loop while the client is running. It +/// makes the server wait for the next event and processes it. +/// +/// \param client pointer to client +/// +/// \see wfp_client_interrupt +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_service( + struct wfp_client * client); + +//------------------------------------------------------------------------------ +/// \brief interrupt wfp_client_service +/// +/// This function can be called from another thread. +/// +/// \param client pointer to client +/// +/// \see wfp_client_service +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_interrupt( + struct wfp_client * client); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/include/webfuse_provider/client_config.h b/include/webfuse_provider/client_config.h new file mode 100644 index 0000000..2345e62 --- /dev/null +++ b/include/webfuse_provider/client_config.h @@ -0,0 +1,240 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/client_config.h +/// \brief Client configuration of webfuse provider. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_CLIENT_CONFIG_H +#define WFP_CLIENT_CONFIG_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +//------------------------------------------------------------------------------ +/// \struct wfp_client_config +/// \brief Provider client configuration object. +/// +/// Holds configuration of webfuse provider client. +//------------------------------------------------------------------------------ +struct wfp_client_config; + +//------------------------------------------------------------------------------ +/// \brief Callback to signal when the client's connection is established. +/// +/// \param user_data user defined context +//------------------------------------------------------------------------------ +typedef void wfp_connected_fn( + void * user_data); + +//------------------------------------------------------------------------------ +/// \brief Callback to signal when a client's connection is disconnected. +/// +/// \param user_data user defined context +//------------------------------------------------------------------------------ +typedef void wfp_disconnected_fn( + void * user_data); + +//------------------------------------------------------------------------------ +/// \brief Creates a new client configuration. +/// +/// \return newly created client configuration +//------------------------------------------------------------------------------ +extern WFP_API struct wfp_client_config * wfp_client_config_create(void); + +//------------------------------------------------------------------------------ +/// \brief Disposes a client configuration. +/// +/// \note The user defined context is not managed by the client configuration. +/// +/// \param config pointer to client configuration +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_config_dispose( + struct wfp_client_config * config); + +//------------------------------------------------------------------------------ +/// \brief Sets a user defined context. +/// +/// \note The user is responsible to manage the lifetime of user data. +/// +/// \param config pointer to client configuration +/// \param user_data user defined context +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_config_set_userdata( + struct wfp_client_config * config, + void * user_data); + +//------------------------------------------------------------------------------ +/// \brief Sets the path to clients private key. +/// +/// \note To enable TLS both, private key and certificate, must be specified. +/// Otherwise, TLS is not used. +/// +/// \param config pointer to client configuration +/// \param key_path path of clients private key (pem file) +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_config_set_keypath( + struct wfp_client_config * config, + char const * key_path); + +//------------------------------------------------------------------------------ +/// \brief Sets the path of clients certificate. +/// +/// \note To enable TLS both, private key and certificate, must be specified. +/// Otherwise, TLS is not used. +/// +/// \param config pointer to client configuration +/// \param cert_path path of the clients certificate (pem file) +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_config_set_certpath( + struct wfp_client_config * config, + char const * cert_path); + +//------------------------------------------------------------------------------ +/// \brief Sets the path of ca file to verify servers. +/// +/// \note To enable TLS both, private key and certificate, must be specified. +/// Otherwise, TLS is not used. +/// +/// \param config pointer to client configuration +/// \param ca_filepath path of the ca file (pem file) +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_config_set_ca_filepath( + struct wfp_client_config * config, + char const * ca_filepath); + +//------------------------------------------------------------------------------ +/// \brief Sets the onconnected handler. +/// +/// The handler is invoked, when the client's conntection is established. +/// +/// \param config pointer to client configuration +/// \param handler pointer to handler +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_config_set_onconnected( + struct wfp_client_config * config, + wfp_connected_fn * handler); + +//------------------------------------------------------------------------------ +/// \brief Sets ondisconnected handler +/// +/// The handler is invoked, when the client's conntection is lost. +/// +/// \param config pointer to client configuration +/// \param handler pointer to handler +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_config_set_ondisconnected( + struct wfp_client_config * config, + wfp_disconnected_fn * handler); + +//------------------------------------------------------------------------------ +/// \brief Sets onlookup handler. +/// +/// The handler is invoked, when the identifier of a file is requested. +/// +/// \param config pointer to client configuration +/// \param handler pointer to handler +/// +/// \see wfp_lookup_fn +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_config_set_onlookup( + struct wfp_client_config * config, + wfp_lookup_fn * handler); + +//------------------------------------------------------------------------------ +/// \brief Sets ongetattr handler. +/// +/// The handler is invoked, when attributes of a file are requested. +/// +/// \param config pointer to client configuration +/// \param handler pointer to handler +/// +/// \see wfp_getattr_fn +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_config_set_ongetattr( + struct wfp_client_config * config, + wfp_getattr_fn * handler); + +//------------------------------------------------------------------------------ +/// \brief Sets onreaddir handler. +/// +/// The handler is invoked, when the contents of directory are requested- +/// +/// \param config pointer to client configuration +/// \param handler pointer to handler +/// +/// \see wfp_readdir_fn +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_config_set_onreaddir( + struct wfp_client_config * config, + wfp_readdir_fn * handler); + +//------------------------------------------------------------------------------ +/// \brief Sets onopen handler. +/// +/// The handler is invoked, whe a file should be opened. +/// +/// \param config pointer to client configuration +/// \param handler pointer to handler +/// +/// \see wfp_open_fn +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_config_set_onopen( + struct wfp_client_config * config, + wfp_open_fn * handler); + +//------------------------------------------------------------------------------ +/// \brief Sets onclose handler. +/// +/// The handler is invoked, when a file is closed. +/// +/// \param config pointer to client configuration +/// \param handler pointer to handler +/// +/// \see wfp_close_fn +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_config_set_onclose( + struct wfp_client_config * config, + wfp_close_fn * handler); + +//------------------------------------------------------------------------------ +/// \brief Sets onread handler. +/// +/// The handler is invoked, when a files content is requested. +/// +/// \param config pointer to client configuration +/// \param handler pointer to handler +/// +/// \see wfp_read_fn +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_config_set_onread( + struct wfp_client_config * config, + wfp_read_fn * handler); + +//------------------------------------------------------------------------------ +/// \brief Enabled authentication. +/// +/// \param config pointer to client configuration +/// \param get_credentials pointer to function providing credentials when +// needed. +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_config_enable_authentication( + struct wfp_client_config * config, + wfp_get_credentials_fn * get_credentials); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/include/webfuse_provider/client_protocol.h b/include/webfuse_provider/client_protocol.h new file mode 100644 index 0000000..66fa5c2 --- /dev/null +++ b/include/webfuse_provider/client_protocol.h @@ -0,0 +1,117 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/client_protocol.h +/// \brief Provides low level access to libwebsockets protocol. +/// +/// By default, libwebfuse encapsulates libwebsockets protocol by \ref +/// wfp_client. But sometimes it might come in handy to have access to +/// libwebsockets protocol. This allows to integrate libwebfuse in existing +/// libwebsockets applications. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_CLIENT_PROTOCOL_H +#define WFP_CLIENT_PROTOCOL_H + +#include "webfuse_provider/api.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +//------------------------------------------------------------------------------ +/// \struct wfp_client_protocol +/// \brief Opaque webfuse client protocol.. +//------------------------------------------------------------------------------ +struct wfp_client_protocol; + +//------------------------------------------------------------------------------ +/// \struct lws_protocols +/// \brief Forward declaration of libwebsockets protocols structure. +//------------------------------------------------------------------------------ +struct lws_protocols; + +//------------------------------------------------------------------------------ +/// \struct lws_context +/// \brief Forward declaration of libwebsockets context structure. +//------------------------------------------------------------------------------ +struct lws_context; + +//------------------------------------------------------------------------------ +/// \struct wfp_client_config +/// \copydoc wfp_client_config +//------------------------------------------------------------------------------ +struct wfp_client_config; + +//------------------------------------------------------------------------------ +/// \brief Creates a new webfuse provider client protocol. +/// +/// \note The user is responsible to manage lifetime of \arg config. +/// +/// \note TLS configuration is ignored, since TLS is managed by libwebsockets. +/// +/// \param config pointer to client config +/// \return newly created protocol +//------------------------------------------------------------------------------ +extern WFP_API struct wfp_client_protocol * wfp_client_protocol_create( + struct wfp_client_config const * config); + +//------------------------------------------------------------------------------ +/// \brief Disposes a protocol. +/// +/// \note The user defined context is not managed by the protocol. +/// +/// \param protocol pointer to protocol. +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_protocol_dispose( + struct wfp_client_protocol * protocol); + +//------------------------------------------------------------------------------ +/// \brief Initialized libwebsockets protocol structure. +/// +/// \param protocol pointer to protocol +/// \param lws_protocol pointer to libwebsockets protocol structure. +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_protocol_init_lws( + struct wfp_client_protocol * protocol, + struct lws_protocols * lws_protocol); + + +//------------------------------------------------------------------------------ +/// \brief Connects the protocol to a remote webfuse adapter server. +/// +/// \note This call starts to establish a connection. A callback is invoked, +/// when the connection is estanlished. +/// +/// \param protocol pointer to protocol +/// \param context lws context +/// \param url URL of remote webfuse adapter server +/// +/// \see wfp_connected_fn +/// \see wfp_client_config_set_onconnected +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_protocol_connect( + struct wfp_client_protocol * protocol, + struct lws_context * context, + char const * url); + +//------------------------------------------------------------------------------ +/// \brief Disconnects the protocol from a remote webfuse adapter server. +/// +/// \note This call starts to disconnect. A callback is invoked, +/// when the connection is estanlished. +/// +/// \param protocol pointer to protocol +/// +/// \see wfp_connected_fn +/// \see wfp_client_config_set_ondisconnected +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_protocol_disconnect( + struct wfp_client_protocol * protocol); + +#ifdef __cplusplus +} +#endif + + + +#endif diff --git a/include/webfuse_provider/credentials.h b/include/webfuse_provider/credentials.h new file mode 100644 index 0000000..666925b --- /dev/null +++ b/include/webfuse_provider/credentials.h @@ -0,0 +1,30 @@ +#ifndef WFP_CREDENTIALS_H +#define WFP_CREDENTIALS_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_credentials; + +typedef void wfp_get_credentials_fn( + struct wfp_credentials * credentials, + void * user_data); + +extern WFP_API void wfp_credentials_set_type( + struct wfp_credentials * credentials, + char const * type); + +extern WFP_API void wfp_credentials_add( + struct wfp_credentials * credentials, + char const * key, + char const * value); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/webfuse_provider/dirbuffer.h b/include/webfuse_provider/dirbuffer.h new file mode 100644 index 0000000..8b43eeb --- /dev/null +++ b/include/webfuse_provider/dirbuffer.h @@ -0,0 +1,60 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/dirbuffer.h +/// \brief Buffer used for directory listing. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_DIRBUFFER_H +#define WFP_DIRBUFFER_H + +#include +#include +#include + +#include "webfuse_provider/api.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +//------------------------------------------------------------------------------ +/// \struct wfp_dirbuffer +/// \brief Buffer used for directory listing. +/// +/// \see wfp_respond_readdir +//------------------------------------------------------------------------------ +struct wfp_dirbuffer; + +//------------------------------------------------------------------------------ +/// \brief Creates a new dir buffer. +/// +/// \return newly created dir buffer. +//------------------------------------------------------------------------------ +extern WFP_API struct wfp_dirbuffer * wfp_dirbuffer_create(void); + +//------------------------------------------------------------------------------ +/// \brief Disposes a dir buffer. +/// +/// \param buffer pointer to dir buffer +//------------------------------------------------------------------------------ +extern WFP_API void wfp_dirbuffer_dispose( + struct wfp_dirbuffer * buffer); + +//------------------------------------------------------------------------------ +/// \brief Adds an entry to dir buffer. +/// +/// \param buffer pointer to dir buffer +/// \param name name of the entry (file or directory) +/// \param inode inode of the entry +//------------------------------------------------------------------------------ +extern WFP_API void wfp_dirbuffer_add( + struct wfp_dirbuffer * buffer, + char const * name, + ino_t inode); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/include/webfuse_provider/operation/close.h b/include/webfuse_provider/operation/close.h new file mode 100644 index 0000000..5aba355 --- /dev/null +++ b/include/webfuse_provider/operation/close.h @@ -0,0 +1,46 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/operation/close.h +/// \brief Provider's close callback. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_OPERATION_CLOSE_H +#define WFP_OPERATION_CLOSE_H + +#ifndef __cplusplus +#include +#else +#include +#endif + +#include +#include +#include + +#include "webfuse_provider/api.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +//------------------------------------------------------------------------------ +/// \brief Callback invoked when a file is invoked. +/// +/// This function does not respond. +/// +/// \param inode inode of file to close +/// \param handle handle of file to close +/// \param flags file close flags +/// \param user_data user defined context +//------------------------------------------------------------------------------ +typedef void wfp_close_fn( + ino_t inode, + uint32_t handle, + int flags, + void * user_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/webfuse_provider/operation/error.h b/include/webfuse_provider/operation/error.h new file mode 100644 index 0000000..a5e223f --- /dev/null +++ b/include/webfuse_provider/operation/error.h @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/operation/error.h +/// \brief Respond with error code. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_OPERATION_ERROR_H +#define WFP_OPERATION_ERROR_H + +#include "webfuse_provider/api.h" +#include "webfuse_provider/status.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_request; + +//------------------------------------------------------------------------------ +/// \brief Respond to a request with an error. +/// +/// A client's callback must respond with exactly one responde, either with a +/// valid reponse regarding to the concrete request or with an error response. +/// +/// \param request pointer to request +/// \param status error code +//------------------------------------------------------------------------------ +extern WFP_API void wfp_respond_error( + struct wfp_request * request, + wfp_status status); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/webfuse_provider/operation/getattr.h b/include/webfuse_provider/operation/getattr.h new file mode 100644 index 0000000..4a3d86f --- /dev/null +++ b/include/webfuse_provider/operation/getattr.h @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/operation/getattr.h +/// \brief Get file attributes. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_OPERATION_GETATTR_H +#define WFP_OPERATION_GETATTR_H + +#include +#include +#include + +#include "webfuse_provider/api.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_request; + +//------------------------------------------------------------------------------ +/// \brief Get file attributes. +/// +/// \note After this function is called, exactly one response must be sent, +/// either via \ref wfp_respond_getattr or via \ref wfp_respond_error. +/// +/// \param request pointer to request +/// \param inode inode of file to get attributes +/// \param user_data user defined context +/// +/// \see wfp_respond_getattr +/// \see wfp_respond_error +//------------------------------------------------------------------------------ +typedef void wfp_getattr_fn( + struct wfp_request * request, + ino_t inode, + void * user_data); + +//------------------------------------------------------------------------------ +/// \brief Respond to a get attributes request. +/// +/// \param request pointer to request +/// \param stat file attributes +//------------------------------------------------------------------------------ +extern WFP_API void wfp_respond_getattr( + struct wfp_request * request, + struct stat const * stat); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/webfuse_provider/operation/lookup.h b/include/webfuse_provider/operation/lookup.h new file mode 100644 index 0000000..4b4c022 --- /dev/null +++ b/include/webfuse_provider/operation/lookup.h @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/operation/lookup.h +/// \brief Lookup file. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_OPERATION_LOOKUP_H +#define WFP_OPERATION_LOOKUP_H + +#include +#include +#include + +#include "webfuse_provider/api.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_request; + +//------------------------------------------------------------------------------ +/// \brief Lookup a file or directory. +/// +/// \note After this function is called, exactly one response must be sent, +/// either via \ref wfp_respond_lookup or via \ref wfp_respond_error. +/// +/// \param request pointer to request +/// \param parent inode of parent +/// \param name name of the filesystem object to lookup +/// \param user_data pointer to user defined context +/// +/// \see wfp_respond_lookup +/// \see wfp_respond_error +//------------------------------------------------------------------------------ +typedef void wfp_lookup_fn( + struct wfp_request * request, + ino_t parent, + char const * name, + void * user_data); + +//------------------------------------------------------------------------------ +/// \brief Respond to lookup request. +/// +/// \param request pointer to request +/// \param stat attributes of filesystem object +//------------------------------------------------------------------------------ +extern WFP_API void wfp_respond_lookup( + struct wfp_request * request, + struct stat const * stat); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/webfuse_provider/operation/open.h b/include/webfuse_provider/operation/open.h new file mode 100644 index 0000000..7e310d9 --- /dev/null +++ b/include/webfuse_provider/operation/open.h @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/operation/open.h +/// \brief Open a file. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_OPERATION_OPEN_H +#define WFP_OPERATION_OPEN_H + +#ifndef __cplusplus +#include +#else +#include +#endif + +#include +#include +#include + +#include "webfuse_provider/api.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_request; + +//------------------------------------------------------------------------------ +/// \brief Open a file. +/// +/// \note After this function is called, exactly one response must be sent, +/// either via \ref wfp_respond_open or via \ref wfp_respond_error. +/// +/// \param request pointer to request +/// \param inode inode of the file to open +/// \param flags file open flags +/// \param user_data user defined context +/// +/// \see wfp_respond_open +/// \see wfp_respond_error +//------------------------------------------------------------------------------ +typedef void wfp_open_fn( + struct wfp_request * request, + ino_t inode, + int flags, + void * user_data); + +//------------------------------------------------------------------------------ +/// \brief Respond to open file. +/// +/// \param request pointer to request +/// \param handle handle of the opened file +//------------------------------------------------------------------------------ +extern WFP_API void wfp_respond_open( + struct wfp_request * request, + uint32_t handle); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/webfuse_provider/operation/read.h b/include/webfuse_provider/operation/read.h new file mode 100644 index 0000000..74c1678 --- /dev/null +++ b/include/webfuse_provider/operation/read.h @@ -0,0 +1,76 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/operation/read.h +/// \brief Read contents of a file. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_OPERATION_READ_H +#define WFP_OPERATION_READ_H + +#ifndef __cplusplus +#include +#include +#else +#include +#include +using std::size_t; +#endif + +#include +#include +#include + +#include "webfuse_provider/api.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_request; + +//------------------------------------------------------------------------------ +/// \brief Requests content of a file. +/// +/// On success, up to \arg length bytes should be returned via \ref +/// wfp_respond_read. +/// +/// \note After this function is called, exactly one response must be sent, +/// either via \ref wfp_respond_read or via \ref wfp_respond_error. +/// +/// \param request pointer to request +/// \param inode inode of the file to read +/// \param handle handle of the file to read (returned by open) +/// \param offset offset within the file where to start reading +/// \param length amount of bytes to read +/// \param user_data used defined context +/// +/// \see wfp_respond_read +/// \see wfp_respond_error +//------------------------------------------------------------------------------ +typedef void wfp_read_fn( + struct wfp_request * request, + ino_t inode, + uint32_t handle, + size_t offset, + size_t length, + void * user_data); + +//------------------------------------------------------------------------------ +/// \brief Respond to read. +/// +/// \note The user is responsible to manage lifetime of \arg data. +/// +/// \param request pointer to request +/// \param data data read from file +/// \param length amount of bytes read +//------------------------------------------------------------------------------ +extern WFP_API void wfp_respond_read( + struct wfp_request * request, + char const * data, + size_t length); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/webfuse_provider/operation/readdir.h b/include/webfuse_provider/operation/readdir.h new file mode 100644 index 0000000..c42773b --- /dev/null +++ b/include/webfuse_provider/operation/readdir.h @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/operation/readdir.h +/// \brief List directory contents. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_OPERATION_READDIR_H +#define WFP_OPERATION_READDIR_H + +#include +#include +#include + +#include "webfuse_provider/api.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_dirbuffer; +struct wfp_request; + +//------------------------------------------------------------------------------ +/// \brief Requests the contents of a directory. +/// +/// \note After this function is called, exactly one response must be sent, +/// either via \ref wfp_respond_readdir or via \ref wfp_respond_error. +/// +/// \param request pointer to request +/// \param directory inode of directory to list +/// \param user_data user defined context +/// +/// \see wfp_respond_readdir +/// \see wfp_respond_error +//------------------------------------------------------------------------------ +typedef void wfp_readdir_fn( + struct wfp_request * request, + ino_t directory, + void * user_data); + +//------------------------------------------------------------------------------ +/// \brief Respond to list directory contents. +/// +/// \note The user is responsible to manage dirbuffe, p.e. to dispose +/// it after this function is called. +/// +/// \param request pointer to request +/// \param dirbuffer contains contents of directory +//------------------------------------------------------------------------------ +extern WFP_API void wfp_respond_readdir( + struct wfp_request * request, + struct wfp_dirbuffer * dirbuffer); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/webfuse_provider/protocol_names.h b/include/webfuse_provider/protocol_names.h new file mode 100644 index 0000000..7a3b251 --- /dev/null +++ b/include/webfuse_provider/protocol_names.h @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file protocol_names.h +/// \brief Names of websocket protocol. +//////////////////////////////////////////////////////////////////////////////// +#ifndef WFP_PROTOCOL_NAMES_H +#define WFP_PROTOCOL_NAMES_H + + +//------------------------------------------------------------------------------ +/// \def WFP_PROTOCOL_NAME_ADAPTER_SERVER +/// \brief Name of the websocket protocol an adapter server is running. +//------------------------------------------------------------------------------ +#define WFP_PROTOCOL_NAME_ADAPTER_SERVER ("webfuse-adapter-server") + +//------------------------------------------------------------------------------ +/// \def WFP_PROTOCOL_NAME_ADAPTER_CLIENT +/// \brief Name of the websocket protocol an adapter client is running. +//------------------------------------------------------------------------------ +#define WFP_PROTOCOL_NAME_ADAPTER_CLIENT ("webfuse-adapter-client") + +//------------------------------------------------------------------------------ +/// \def WFP_PROTOCOL_NAME_PROVIDER_CLIENT +/// \brief Name of the websocket protocol an provider client is running. +//------------------------------------------------------------------------------ +#define WFP_PROTOCOL_NAME_PROVIDER_CLIENT ("webfuse-provider-client") + +//------------------------------------------------------------------------------ +/// \def WFP_PROTOCOL_NAME_PROVIDER_SERVER +/// \brief Name of the websocket protocol an provider server is running. +//------------------------------------------------------------------------------ +#define WFP_PROTOCOL_NAME_PROVIDER_SERVER ("webfuse-provider-server") + +#endif diff --git a/include/webfuse_provider/status.h b/include/webfuse_provider/status.h new file mode 100644 index 0000000..094f13d --- /dev/null +++ b/include/webfuse_provider/status.h @@ -0,0 +1,23 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file status.h +/// \brief Generic status code. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_STATUS_H +#define WFP_STATUS_H + +#define WFP_GOOD 0 ///< Positive status code. +#define WFP_BAD 1 ///< Generic negative status code. + +#define WFP_BAD_NOTIMPLEMENTED 2 ///< The called function is not implemented (yet). +#define WFP_BAD_TIMEOUT 3 ///< A timeout occured. +#define WFP_BAD_BUSY 4 ///< Resource is busy, try again later. +#define WFP_BAD_FORMAT 5 ///< Invalid format. + +#define WFP_BAD_NOENTRY 101 ///< Entry not found. +#define WFP_BAD_ACCESS_DENIED 102 ///< Access is denied. + +/// Status code. +typedef int wfp_status; + +#endif diff --git a/include/webfuse_provider/webfuse_provider.h b/include/webfuse_provider/webfuse_provider.h new file mode 100644 index 0000000..b979cb7 --- /dev/null +++ b/include/webfuse_provider/webfuse_provider.h @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file webfuse_provider.h +/// \brief Convenience header to include all functionality of libfuse_provider. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_H +#define WFP_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/lib/webfuse_provider/api.c b/lib/webfuse_provider/api.c new file mode 100644 index 0000000..0155dd5 --- /dev/null +++ b/lib/webfuse_provider/api.c @@ -0,0 +1,281 @@ +#include "webfuse_provider/webfuse_provider.h" + +#include "webfuse_provider/impl/request.h" +#include "webfuse_provider/impl/operation/getattr.h" +#include "webfuse_provider/impl/operation/lookup.h" +#include "webfuse_provider/impl/operation/readdir.h" +#include "webfuse_provider/impl/operation/open.h" +#include "webfuse_provider/impl/operation/close.h" +#include "webfuse_provider/impl/operation/read.h" +#include "webfuse_provider/impl/client_protocol.h" +#include "webfuse_provider/impl/client_config.h" +#include "webfuse_provider/impl/client.h" +#include "webfuse_provider/impl/dirbuffer.h" +#include "webfuse_provider/impl/credentials.h" + +#include "webfuse_provider/impl/util/util.h" + +// respond + +void wfp_respond_error( + struct wfp_request * request, + wfp_status status) +{ + wfp_impl_respond_error(request, status); +} + +void wfp_respond_getattr( + struct wfp_request * request, + struct stat const * stat) +{ + wfp_impl_respond_getattr(request, stat); +} + +void wfp_respond_lookup( + struct wfp_request * request, + struct stat const * stat) +{ + wfp_impl_respond_lookup(request, stat); +} + +void wfp_respond_open( + struct wfp_request * request, + uint32_t handle) +{ + wfp_impl_respond_open(request, handle); +} + +void wfp_respond_read( + struct wfp_request * request, + char const * data, + size_t length) +{ + wfp_impl_respond_read(request, data, length); +} + +void wfp_respond_readdir( + struct wfp_request * request, + struct wfp_dirbuffer * dirbuffer) +{ + wfp_impl_respond_readdir(request, dirbuffer); +} + +// config + + +struct wfp_client_config * wfp_client_config_create(void) +{ + return wfp_impl_client_config_create(); +} + +void wfp_client_config_dispose( + struct wfp_client_config * config) +{ + wfp_impl_client_config_dispose(config); +} + +void wfp_client_config_set_userdata( + struct wfp_client_config * config, + void * user_data) +{ + wfp_impl_client_config_set_userdata(config, user_data); +} + +void wfp_client_config_set_keypath( + struct wfp_client_config * config, + char const * key_path) +{ + wfp_impl_client_config_set_keypath(config, key_path); +} + +void wfp_client_config_set_certpath( + struct wfp_client_config * config, + char const * cert_path) +{ + wfp_impl_client_config_set_certpath(config, cert_path); +} + +void wfp_client_config_set_ca_filepath( + struct wfp_client_config * config, + char const * ca_filepath) +{ + wfp_impl_client_config_set_ca_filepath(config, ca_filepath); +} + +void wfp_client_config_set_onconnected( + struct wfp_client_config * config, + wfp_connected_fn * handler) +{ + wfp_impl_client_config_set_onconnected(config, handler); +} + +void wfp_client_config_set_ondisconnected( + struct wfp_client_config * config, + wfp_disconnected_fn * handler) +{ + wfp_impl_client_config_set_ondisconnected(config, handler); +} + +void wfp_client_config_set_onlookup( + struct wfp_client_config * config, + wfp_lookup_fn * handler) +{ + wfp_impl_client_config_set_onlookup(config, handler); +} + +void wfp_client_config_set_ongetattr( + struct wfp_client_config * config, + wfp_getattr_fn * handler) +{ + wfp_impl_client_config_set_ongetattr(config, handler); +} + +void wfp_client_config_set_onreaddir( + struct wfp_client_config * config, + wfp_readdir_fn * handler) +{ + wfp_impl_client_config_set_onreaddir(config, handler); +} + +void wfp_client_config_set_onopen( + struct wfp_client_config * config, + wfp_open_fn * handler) +{ + wfp_impl_client_config_set_onopen(config, handler); +} + +void wfp_client_config_set_onclose( + struct wfp_client_config * config, + wfp_close_fn * handler) +{ + wfp_impl_client_config_set_onclose(config, handler); +} + +void wfp_client_config_set_onread( + struct wfp_client_config * config, + wfp_read_fn * handler) +{ + wfp_impl_client_config_set_onread(config, handler); +} + +void wfp_client_config_enable_authentication( + struct wfp_client_config * config, + wfp_get_credentials_fn * get_credentials) +{ + wfp_impl_client_config_enable_authentication(config, get_credentials); +} + +// protocol + + +struct wfp_client_protocol * wfp_client_protocol_create( + struct wfp_client_config const * config) +{ + return wfp_impl_client_protocol_create(config); +} + +void wfp_client_protocol_dispose( + struct wfp_client_protocol * protocol) +{ + wfp_impl_client_protocol_dispose(protocol); +} + +void wfp_client_protocol_init_lws( + struct wfp_client_protocol * protocol, + struct lws_protocols * lws_protocol) +{ + wfp_impl_client_protocol_init_lws(protocol, lws_protocol); +} + +void wfp_client_protocol_connect( + struct wfp_client_protocol * protocol, + struct lws_context * context, + char const * url) +{ + wfp_impl_client_protocol_connect(protocol, context, url); +} + +void wfp_client_protocol_disconnect( + struct wfp_client_protocol * protocol) +{ + wfp_impl_client_protocol_disconnect(protocol); +} + + +// client + +struct wfp_client * wfp_client_create( + struct wfp_client_config * config) +{ + return wfp_impl_client_create(config); +} + +void wfp_client_connect( + struct wfp_client * client, + char const * url) +{ + wfp_impl_client_connect(client, url); +} + +void wfp_client_disconnect( + struct wfp_client * client) +{ + wfp_impl_client_disconnect(client); +} + +void wfp_client_dispose( + struct wfp_client * client) +{ + wfp_impl_client_dispose(client); +} + +void wfp_client_service( + struct wfp_client * client) +{ + wfp_impl_client_service(client); +} + +void wfp_client_interrupt( + struct wfp_client * client) +{ + wfp_impl_client_interrupt(client); +} + + +// dirbuffer + +struct wfp_dirbuffer * wfp_dirbuffer_create(void) +{ + return wfp_impl_dirbuffer_create(); +} + +void wfp_dirbuffer_dispose( + struct wfp_dirbuffer * buffer) +{ + wfp_impl_dirbuffer_dispose(buffer); +} + +void wfp_dirbuffer_add( + struct wfp_dirbuffer * buffer, + char const * name, + ino_t inode) +{ + wfp_impl_dirbuffer_add(buffer, name, inode); +} + +// credentials + +void wfp_credentials_set_type( + struct wfp_credentials * credentials, + char const * type) +{ + wfp_impl_credentials_set_type(credentials, type); +} + +void wfp_credentials_add( + struct wfp_credentials * credentials, + char const * key, + char const * value) +{ + wfp_impl_credentials_add(credentials, key, value); +} diff --git a/lib/webfuse_provider/impl/client.c b/lib/webfuse_provider/impl/client.c new file mode 100644 index 0000000..596cbaf --- /dev/null +++ b/lib/webfuse_provider/impl/client.c @@ -0,0 +1,95 @@ +#include "webfuse_provider/impl/client.h" + +#include +#include +#include + +#include + +#include "webfuse_provider/impl/provider.h" +#include "webfuse_provider/impl/client_protocol.h" +#include "webfuse_provider/impl/client_config.h" +#include "webfuse_provider/impl/util/lws_log.h" + +#define WFP_CLIENT_PROTOCOL_COUNT 2 + +struct wfp_client +{ + struct wfp_client_protocol protocol; + struct lws_context_creation_info info; + struct lws_protocols protocols[WFP_CLIENT_PROTOCOL_COUNT]; + struct lws_context * context; + char * key_path; + char * cert_path; +}; + + +struct wfp_client * wfp_impl_client_create( + struct wfp_client_config * config) +{ + wfp_impl_lwslog_disable(); + + struct wfp_client * client = malloc(sizeof(struct wfp_client)); + wfp_impl_client_protocol_init(&client->protocol, &config->provider, config->user_data); + + memset(client->protocols, 0, sizeof(struct lws_protocols) * WFP_CLIENT_PROTOCOL_COUNT); + wfp_impl_client_protocol_init_lws(&client->protocol, &client->protocols[0]); + + memset(&client->info, 0, sizeof(struct lws_context_creation_info)); + client->info.port = CONTEXT_PORT_NO_LISTEN; + client->info.protocols = client->protocols; + client->info.uid = -1; + client->info.gid = -1; + + if ((NULL != config->cert_path) && (NULL != config->key_path)) + { + client->info.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS; + } + + client->context = lws_create_context(&client->info); + + if ((NULL != config->cert_path) && (NULL != config->key_path)) + { + struct lws_vhost * vhost = lws_create_vhost(client->context, &client->info); + client->info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + client->info.client_ssl_cert_filepath = config->cert_path; + client->info.client_ssl_private_key_filepath = config->key_path; + client->info.client_ssl_ca_filepath = config->ca_filepath; + lws_init_vhost_client_ssl(&client->info, vhost); + } + + return client; +} + +void wfp_impl_client_dispose( + struct wfp_client * client) +{ + lws_context_destroy(client->context); + wfp_impl_client_protocol_cleanup(&client->protocol); + free(client); +} + +void wfp_impl_client_connect( + struct wfp_client * client, + char const * url) +{ + wfp_impl_client_protocol_connect(&client->protocol, client->context, url); +} + +void wfp_impl_client_disconnect( + struct wfp_client * client) +{ + wfp_impl_client_protocol_disconnect(&client->protocol); +} + +void wfp_impl_client_service( + struct wfp_client * client) +{ + lws_service(client->context, 0); +} + +void wfp_impl_client_interrupt( + struct wfp_client * client) +{ + lws_cancel_service(client->context); +} diff --git a/lib/webfuse_provider/impl/client.h b/lib/webfuse_provider/impl/client.h new file mode 100644 index 0000000..f1bd504 --- /dev/null +++ b/lib/webfuse_provider/impl/client.h @@ -0,0 +1,48 @@ +#ifndef WFP_IMPL_CLIENT_H +#define WFP_IMPL_CLIENT_H + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_client; +struct wfp_client_config; + +extern struct wfp_client * wfp_impl_client_create( + struct wfp_client_config * config); + +extern void wfp_impl_client_set_keypath( + struct wfp_client * client, + char * key_path); + +extern void wfp_impl_client_set_certpath( + struct wfp_client * client, + char * cert_path); + +extern void wfp_impl_client_connect( + struct wfp_client * client, + char const * url); + +extern void wfp_impl_client_disconnect( + struct wfp_client * client); + +extern void wfp_impl_client_dispose( + struct wfp_client * client); + +extern void wfp_impl_client_service( + struct wfp_client * client); + +extern void wfp_impl_client_interrupt( + struct wfp_client * client); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/lib/webfuse_provider/impl/client_config.c b/lib/webfuse_provider/impl/client_config.c new file mode 100644 index 0000000..26e0df5 --- /dev/null +++ b/lib/webfuse_provider/impl/client_config.c @@ -0,0 +1,119 @@ +#include "webfuse_provider/impl/client_config.h" +#include "webfuse_provider/impl/provider.h" +#include +#include + +struct wfp_client_config * wfp_impl_client_config_create(void) +{ + struct wfp_client_config * config = malloc(sizeof(struct wfp_client_config)); + wfp_impl_provider_init(&config->provider); + config->user_data = NULL; + config->key_path = NULL; + config->cert_path = NULL; + config->ca_filepath = NULL; + + return config; +} + +void wfp_impl_client_config_dispose( + struct wfp_client_config * config) +{ + free(config->key_path); + free(config->cert_path); + free(config->ca_filepath); + free(config); +} + +void wfp_impl_client_config_set_userdata( + struct wfp_client_config * config, + void * user_data) +{ + config->user_data = user_data; +} + +void wfp_impl_client_config_set_keypath( + struct wfp_client_config * config, + char const * key_path) +{ + free(config->key_path); + config->key_path = strdup(key_path); +} + +void wfp_impl_client_config_set_certpath( + struct wfp_client_config * config, + char const * cert_path) +{ + free(config->cert_path); + config->cert_path = strdup(cert_path); +} + +void wfp_impl_client_config_set_ca_filepath( + struct wfp_client_config * config, + char const * ca_filepath) +{ + free(config->ca_filepath); + config->ca_filepath = strdup(ca_filepath); +} + +void wfp_impl_client_config_set_onconnected( + struct wfp_client_config * config, + wfp_connected_fn * handler) +{ + config->provider.connected = handler; +} + +void wfp_impl_client_config_set_ondisconnected( + struct wfp_client_config * config, + wfp_disconnected_fn * handler) +{ + config->provider.disconnected = handler; +} + +void wfp_impl_client_config_set_onlookup( + struct wfp_client_config * config, + wfp_lookup_fn * handler) +{ + config->provider.lookup = handler; +} + +void wfp_impl_client_config_set_ongetattr( + struct wfp_client_config * config, + wfp_getattr_fn * handler) +{ + config->provider.getattr = handler; +} + +void wfp_impl_client_config_set_onreaddir( + struct wfp_client_config * config, + wfp_readdir_fn * handler) +{ + config->provider.readdir = handler; +} + +void wfp_impl_client_config_set_onopen( + struct wfp_client_config * config, + wfp_open_fn * handler) +{ + config->provider.open = handler; +} + +void wfp_impl_client_config_set_onclose( + struct wfp_client_config * config, + wfp_close_fn * handler) +{ + config->provider.close = handler; +} + +void wfp_impl_client_config_set_onread( + struct wfp_client_config * config, + wfp_read_fn * handler) +{ + config->provider.read = handler; +} + +void wfp_impl_client_config_enable_authentication( + struct wfp_client_config * config, + wfp_get_credentials_fn * get_credentials) +{ + config->provider.get_credentials = get_credentials; +} diff --git a/lib/webfuse_provider/impl/client_config.h b/lib/webfuse_provider/impl/client_config.h new file mode 100644 index 0000000..49f565f --- /dev/null +++ b/lib/webfuse_provider/impl/client_config.h @@ -0,0 +1,82 @@ +#ifndef WFP_IMPL_CLIENT_CONFIG_H +#define WFP_IMPL_CLIENT_CONFIG_H + +#include "webfuse_provider/client_config.h" +#include "webfuse_provider/impl/provider.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_client_config +{ + struct wfp_provider provider; + void * user_data; + char * key_path; + char * cert_path; + char * ca_filepath; +}; + +extern struct wfp_client_config * wfp_impl_client_config_create(void); + +extern void wfp_impl_client_config_dispose( + struct wfp_client_config * config); + +extern void wfp_impl_client_config_set_userdata( + struct wfp_client_config * config, + void * user_data); + +extern void wfp_impl_client_config_set_keypath( + struct wfp_client_config * config, + char const * key_path); + +extern void wfp_impl_client_config_set_certpath( + struct wfp_client_config * config, + char const * cert_path); + +extern void wfp_impl_client_config_set_ca_filepath( + struct wfp_client_config * config, + char const * ca_filepath); + +extern void wfp_impl_client_config_set_onconnected( + struct wfp_client_config * config, + wfp_connected_fn * handler); + +extern void wfp_impl_client_config_set_ondisconnected( + struct wfp_client_config * config, + wfp_disconnected_fn * handler); + +extern void wfp_impl_client_config_set_onlookup( + struct wfp_client_config * config, + wfp_lookup_fn * handler); + +extern void wfp_impl_client_config_set_ongetattr( + struct wfp_client_config * config, + wfp_getattr_fn * handler); + +extern void wfp_impl_client_config_set_onreaddir( + struct wfp_client_config * config, + wfp_readdir_fn * handler); + +extern void wfp_impl_client_config_set_onopen( + struct wfp_client_config * config, + wfp_open_fn * handler); + +extern void wfp_impl_client_config_set_onclose( + struct wfp_client_config * config, + wfp_close_fn * handler); + +extern void wfp_impl_client_config_set_onread( + struct wfp_client_config * config, + wfp_read_fn * handler); + +extern void wfp_impl_client_config_enable_authentication( + struct wfp_client_config * config, + wfp_get_credentials_fn * get_credentials); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/client_protocol.c b/lib/webfuse_provider/impl/client_protocol.c new file mode 100644 index 0000000..3359058 --- /dev/null +++ b/lib/webfuse_provider/impl/client_protocol.c @@ -0,0 +1,332 @@ +#include "webfuse_provider/impl/client_protocol.h" + +#include +#include + +#include +#include + +#include "webfuse_provider/impl/client_config.h" +#include "webfuse_provider/impl/provider.h" +#include "webfuse_provider/impl/credentials.h" +#include "webfuse_provider/impl/util/util.h" +#include "webfuse_provider/impl/message.h" +#include "webfuse_provider/impl/message_queue.h" +#include "webfuse_provider/impl/util/container_of.h" +#include "webfuse_provider/impl/util/url.h" +#include "webfuse_provider/protocol_names.h" + +#include "webfuse_provider/impl/timer/manager.h" + +#include "webfuse_provider/impl/jsonrpc/response.h" +#include "webfuse_provider/impl/jsonrpc/request.h" +#include "webfuse_provider/impl/jsonrpc/proxy.h" + +#define WFP_DEFAULT_TIMEOUT (10 * 1000) + +static void wfp_impl_client_protocol_respond( + json_t * response, + void * user_data) +{ + struct wfp_client_protocol * protocol = (struct wfp_client_protocol *) user_data; + + struct wfp_message * message = wfp_message_create(response); + wfp_slist_append(&protocol->messages, &message->item); + lws_callback_on_writable(protocol->wsi); +} + +static void wfp_impl_client_protocol_process( + struct wfp_client_protocol * protocol, + char const * data, + size_t length) +{ + json_t * message = json_loadb(data, length, 0, NULL); + if (NULL != message) + { + if (wfp_jsonrpc_is_response(message)) + { + wfp_jsonrpc_proxy_onresult(protocol->proxy, message); + } + + if (wfp_jsonrpc_is_request(message)) + { + struct wfp_impl_invokation_context context = + { + .provider = &protocol->provider, + .user_data = protocol->user_data, + .request = &protocol->request + }; + + wfp_impl_provider_invoke(&context, message); + } + + json_decref(message); + } +} + +static void +wfp_impl_client_protocol_on_add_filesystem_finished( + void * user_data, + json_t const * result, + json_t const * WFP_UNUSED_PARAM(error)) +{ + struct wfp_client_protocol * protocol = user_data; + if (NULL == protocol->wsi) { return; } + + if (NULL != result) + { + protocol->is_connected = true; + protocol->provider.connected(protocol->user_data); + } + else + { + protocol->is_shutdown_requested = true; + lws_callback_on_writable(protocol->wsi); + } +} + +static void wfp_impl_client_protocol_add_filesystem( + struct wfp_client_protocol * protocol) +{ + wfp_jsonrpc_proxy_invoke( + protocol->proxy, + &wfp_impl_client_protocol_on_add_filesystem_finished, + protocol, + "add_filesystem", + "s", + "cprovider"); +} + +static void +wfp_impl_client_protocol_on_authenticate_finished( + void * user_data, + json_t const * result, + json_t const * WFP_UNUSED_PARAM(error)) +{ + struct wfp_client_protocol * protocol = user_data; + if (NULL == protocol->wsi) { return; } + + if (NULL != result) + { + wfp_impl_client_protocol_add_filesystem(protocol); + } + else + { + protocol->is_shutdown_requested = true; + lws_callback_on_writable(protocol->wsi); + } +} + +static void wfp_impl_client_protocol_authenticate( + struct wfp_client_protocol * protocol) +{ + struct wfp_credentials credentials; + wfp_impl_credentials_init(&credentials); + + protocol->provider.get_credentials(&credentials, protocol->user_data); + + char const * cred_type = wfp_impl_credentials_get_type(&credentials); + json_t * creds = wfp_impl_credentials_get(&credentials); + json_incref(creds); + + wfp_jsonrpc_proxy_invoke( + protocol->proxy, + &wfp_impl_client_protocol_on_authenticate_finished, + protocol, + "authenticate", + "sj", + cred_type, creds); + + wfp_impl_credentials_cleanup(&credentials); +} + +static void wfp_impl_client_protocol_handshake( + struct wfp_client_protocol * protocol) +{ + if (wfp_impl_provider_is_authentication_enabled(&protocol->provider)) + { + wfp_impl_client_protocol_authenticate(protocol); + } + else + { + wfp_impl_client_protocol_add_filesystem(protocol); + } +} + +static int wfp_impl_client_protocol_callback( + struct lws * wsi, + enum lws_callback_reasons reason, + void * WFP_UNUSED_PARAM(user), + void * in, + size_t len) +{ + int result = 0; + struct lws_protocols const * ws_protocol = lws_get_protocol(wsi); + struct wfp_client_protocol * protocol = (NULL != ws_protocol) ? ws_protocol->user: NULL; + + if (NULL != protocol) + { + wfp_timer_manager_check(protocol->timer_manager); + + switch (reason) + { + case LWS_CALLBACK_CLIENT_ESTABLISHED: + wfp_impl_client_protocol_handshake(protocol); + break; + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + protocol->is_connected = false; + protocol->provider.disconnected(protocol->user_data); + break; + case LWS_CALLBACK_CLIENT_CLOSED: + protocol->is_connected = false; + protocol->provider.disconnected(protocol->user_data); + protocol->wsi = NULL; + break; + case LWS_CALLBACK_CLIENT_RECEIVE: + wfp_impl_client_protocol_process(protocol, in, len); + break; + case LWS_CALLBACK_SERVER_WRITEABLE: + // fall-through + case LWS_CALLBACK_CLIENT_WRITEABLE: + if (wsi == protocol->wsi) + { + if (protocol->is_shutdown_requested) + { + result = 1; + } + else if (!wfp_slist_empty(&protocol->messages)) + { + struct wfp_slist_item * item = wfp_slist_remove_first(&protocol->messages); + struct wfp_message * message = wfp_container_of(item, struct wfp_message, item); + lws_write(wsi, (unsigned char*) message->data, message->length, LWS_WRITE_TEXT); + wfp_message_dispose(message); + + if (!wfp_slist_empty(&protocol->messages)) + { + lws_callback_on_writable(wsi); + } + } + } + break; + default: + break; + } + } + + return result; +} + +static bool wfp_impl_client_protocol_send( + json_t * request, + void * user_data) +{ + struct wfp_client_protocol * protocol = user_data; + + struct wfp_message * message = wfp_message_create(request); + wfp_slist_append(&protocol->messages, &message->item); + lws_callback_on_writable(protocol->wsi); + + return true; +} + +void wfp_impl_client_protocol_init( + struct wfp_client_protocol * protocol, + struct wfp_provider const * provider, + void * user_data) +{ + protocol->is_connected = false; + protocol->is_shutdown_requested = false; + wfp_slist_init(&protocol->messages); + + protocol->wsi = NULL; + + protocol->request.respond = &wfp_impl_client_protocol_respond; + protocol->request.user_data = protocol; + + protocol->timer_manager = wfp_timer_manager_create(); + protocol->proxy = wfp_jsonrpc_proxy_create(protocol->timer_manager, WFP_DEFAULT_TIMEOUT, &wfp_impl_client_protocol_send, protocol); + + protocol->user_data = user_data; + wfp_impl_provider_init_from_prototype(&protocol->provider, provider); +} + +void wfp_impl_client_protocol_cleanup( + struct wfp_client_protocol * protocol) +{ + wfp_jsonrpc_proxy_dispose(protocol->proxy); + wfp_timer_manager_dispose(protocol->timer_manager); + wfp_message_queue_cleanup(&protocol->messages); +} + +struct wfp_client_protocol * wfp_impl_client_protocol_create( + struct wfp_client_config const * config) +{ + struct wfp_client_protocol * protocol = malloc(sizeof(struct wfp_client_protocol)); + wfp_impl_client_protocol_init(protocol, &config->provider, config->user_data); + + return protocol; +} + +void wfp_impl_client_protocol_dispose( + struct wfp_client_protocol * protocol) +{ + wfp_impl_client_protocol_cleanup(protocol); + free(protocol); +} + +void wfp_impl_client_protocol_init_lws( + struct wfp_client_protocol * protocol, + struct lws_protocols * lws_protocol) +{ + lws_protocol->name = WFP_PROTOCOL_NAME_PROVIDER_CLIENT; + lws_protocol->callback = &wfp_impl_client_protocol_callback; + lws_protocol->per_session_data_size = 0; + lws_protocol->user = protocol; +} + +void wfp_impl_client_protocol_connect( + struct wfp_client_protocol * protocol, + struct lws_context * context, + char const * url) +{ + struct wfp_url url_data; + bool const success = wfp_url_init(&url_data, url); + if (success) + { + struct lws_client_connect_info info; + memset(&info, 0, sizeof(struct lws_client_connect_info)); + info.context = context; + info.port = url_data.port; + info.address = url_data.host; + info.path = url_data.path; + info.host = info.address; + info.origin = info.address; + info.ssl_connection = (url_data.use_tls) ? LCCSCF_USE_SSL : 0; + info.protocol = WFP_PROTOCOL_NAME_ADAPTER_SERVER; + info.local_protocol_name = WFP_PROTOCOL_NAME_PROVIDER_CLIENT; + info.pwsi = &protocol->wsi; + + lws_client_connect_via_info(&info); + + wfp_url_cleanup(&url_data); + } + else + { + protocol->provider.disconnected(protocol->user_data); + } + +} + +void wfp_impl_client_protocol_disconnect( + struct wfp_client_protocol * protocol) +{ + if (protocol->is_connected) + { + protocol->is_shutdown_requested = true; + lws_callback_on_writable(protocol->wsi); + } + else + { + protocol->provider.disconnected(protocol->user_data); + } +} diff --git a/lib/webfuse_provider/impl/client_protocol.h b/lib/webfuse_provider/impl/client_protocol.h new file mode 100644 index 0000000..60eec44 --- /dev/null +++ b/lib/webfuse_provider/impl/client_protocol.h @@ -0,0 +1,63 @@ +#ifndef WFP_IMPL_CLIENT_PROTOCOL_H +#define WFP_IMPL_CLIENT_PROTOCOL_H + +#include "webfuse_provider/impl/provider.h" +#include "webfuse_provider/impl/request.h" + +#include "webfuse_provider/impl/util/slist.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_client_config; +struct lws_protocols; +struct lws_context; +struct wfp_jsonrpc_proxy; +struct wfp_timer_manager; + +struct wfp_client_protocol +{ + bool is_connected; + bool is_shutdown_requested; + struct wfp_request request; + struct wfp_provider provider; + void * user_data; + struct lws * wsi; + struct wfp_timer_manager * timer_manager; + struct wfp_jsonrpc_proxy * proxy; + struct wfp_slist messages; +}; + +extern void wfp_impl_client_protocol_init( + struct wfp_client_protocol * protocol, + struct wfp_provider const * provider, + void * user_data); + +extern void wfp_impl_client_protocol_cleanup( + struct wfp_client_protocol * protocol); + +extern struct wfp_client_protocol * wfp_impl_client_protocol_create( + struct wfp_client_config const * config); + +extern void wfp_impl_client_protocol_dispose( + struct wfp_client_protocol * protocol); + +extern void wfp_impl_client_protocol_init_lws( + struct wfp_client_protocol * protocol, + struct lws_protocols * lws_protocol); + +extern void wfp_impl_client_protocol_connect( + struct wfp_client_protocol * protocol, + struct lws_context * context, + char const * url); + +extern void wfp_impl_client_protocol_disconnect( + struct wfp_client_protocol * protocol); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/credentials.c b/lib/webfuse_provider/impl/credentials.c new file mode 100644 index 0000000..9c0a82c --- /dev/null +++ b/lib/webfuse_provider/impl/credentials.c @@ -0,0 +1,46 @@ +#include "webfuse_provider/impl/credentials.h" + +#include +#include + +void wfp_impl_credentials_init( + struct wfp_credentials * credentials) +{ + credentials->type = NULL; + credentials->contents = json_object(); +} + +void wfp_impl_credentials_cleanup( + struct wfp_credentials * credentials) +{ + free(credentials->type); + json_decref(credentials->contents); +} + +void wfp_impl_credentials_set_type( + struct wfp_credentials * credentials, + char const * type) +{ + free(credentials->type); + credentials->type = strdup(type); +} + +void wfp_impl_credentials_add( + struct wfp_credentials * credentials, + char const * key, + char const * value) +{ + json_object_set_new(credentials->contents, key, json_string(value)); +} + +char const * wfp_impl_credentials_get_type( + struct wfp_credentials * credentials) +{ + return credentials->type; +} + +json_t * wfp_impl_credentials_get( + struct wfp_credentials * credentials) +{ + return credentials->contents; +} diff --git a/lib/webfuse_provider/impl/credentials.h b/lib/webfuse_provider/impl/credentials.h new file mode 100644 index 0000000..521bbff --- /dev/null +++ b/lib/webfuse_provider/impl/credentials.h @@ -0,0 +1,43 @@ +#ifndef WFP_IMPL_CREDENTIALS_H +#define WFP_IMPL_CREDENTIALS_H + +#include "webfuse_provider/credentials.h" +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_credentials +{ + char * type; + json_t * contents; +}; + +extern void wfp_impl_credentials_init( + struct wfp_credentials * credentials); + +extern void wfp_impl_credentials_cleanup( + struct wfp_credentials * credentials); + +extern void wfp_impl_credentials_set_type( + struct wfp_credentials * credentials, + char const * type); + +extern void wfp_impl_credentials_add( + struct wfp_credentials * credentials, + char const * key, + char const * value); + +extern char const * wfp_impl_credentials_get_type( + struct wfp_credentials * credentials); + +extern json_t * wfp_impl_credentials_get( + struct wfp_credentials * credentials); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/dirbuffer.c b/lib/webfuse_provider/impl/dirbuffer.c new file mode 100644 index 0000000..0d4f51c --- /dev/null +++ b/lib/webfuse_provider/impl/dirbuffer.c @@ -0,0 +1,42 @@ +#include "webfuse_provider/impl/dirbuffer.h" +#include + +struct wfp_dirbuffer * wfp_impl_dirbuffer_create(void) +{ + struct wfp_dirbuffer * buffer = malloc(sizeof(struct wfp_dirbuffer)); + buffer->entries = json_array(); + + return buffer; +} + +void wfp_impl_dirbuffer_dispose( + struct wfp_dirbuffer * buffer) +{ + if (NULL != buffer->entries) + { + json_decref(buffer->entries); + } + + free(buffer); +} + +void wfp_impl_dirbuffer_add( + struct wfp_dirbuffer * buffer, + char const * name, + ino_t inode) +{ + json_t * entry = json_object(); + json_object_set_new(entry, "name", json_string(name)); + json_object_set_new(entry, "inode", json_integer(inode)); + + json_array_append_new(buffer->entries, entry); +} + +json_t * wfp_impl_dirbuffer_take( + struct wfp_dirbuffer * buffer) +{ + json_t * entries = buffer->entries; + + buffer->entries = NULL; + return entries; +} diff --git a/lib/webfuse_provider/impl/dirbuffer.h b/lib/webfuse_provider/impl/dirbuffer.h new file mode 100644 index 0000000..eaa0278 --- /dev/null +++ b/lib/webfuse_provider/impl/dirbuffer.h @@ -0,0 +1,37 @@ +#ifndef WFP_IMPL_DIRBUFFER_H +#define WFP_IMPL_DIRBUFFER_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_dirbuffer +{ + json_t * entries; +}; + +extern struct wfp_dirbuffer * wfp_impl_dirbuffer_create(void); + +extern void wfp_impl_dirbuffer_dispose( + struct wfp_dirbuffer * buffer); + +extern void wfp_impl_dirbuffer_add( + struct wfp_dirbuffer * buffer, + char const * name, + ino_t inode); + +extern json_t * wfp_impl_dirbuffer_take( + struct wfp_dirbuffer * buffer); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/jsonrpc/error.c b/lib/webfuse_provider/impl/jsonrpc/error.c new file mode 100644 index 0000000..abe8a71 --- /dev/null +++ b/lib/webfuse_provider/impl/jsonrpc/error.c @@ -0,0 +1,27 @@ +#include "webfuse_provider/impl/jsonrpc/error.h" + +json_t * +wfp_jsonrpc_error( + int code, + char const * message) +{ + json_t * error = json_object(); + json_object_set_new(error, "code", json_integer(code)); + json_object_set_new(error, "message", json_string(message)); + + return error; +} + +void +wfp_jsonrpc_propate_error( + wfp_jsonrpc_proxy_finished_fn * finised, + void * user_data, + int code, + char const * message) +{ + json_t * error = wfp_jsonrpc_error(code, message); + finised(user_data, NULL, error); + + json_decref(error); +} + diff --git a/lib/webfuse_provider/impl/jsonrpc/error.h b/lib/webfuse_provider/impl/jsonrpc/error.h new file mode 100644 index 0000000..dd54f0a --- /dev/null +++ b/lib/webfuse_provider/impl/jsonrpc/error.h @@ -0,0 +1,29 @@ +#ifndef WFP_JSONRPC_ERROR_H +#define WFP_JSONRPC_ERROR_H + +#include +#include "webfuse_provider/impl/jsonrpc/proxy_finished_fn.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern json_t * +wfp_jsonrpc_error( + int code, + char const * message); + +extern void +wfp_jsonrpc_propate_error( + wfp_jsonrpc_proxy_finished_fn * finised, + void * user_data, + int code, + char const * message); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/lib/webfuse_provider/impl/jsonrpc/proxy.c b/lib/webfuse_provider/impl/jsonrpc/proxy.c new file mode 100644 index 0000000..cd6f64d --- /dev/null +++ b/lib/webfuse_provider/impl/jsonrpc/proxy.c @@ -0,0 +1,216 @@ +#include "webfuse_provider/impl/jsonrpc/proxy_intern.h" +#include "webfuse_provider/impl/jsonrpc/response_intern.h" +#include "webfuse_provider/impl/jsonrpc/error.h" +#include "webfuse_provider/status.h" + +#include "webfuse_provider/impl/timer/timer.h" + +#include +#include + +struct wfp_jsonrpc_proxy * +wfp_jsonrpc_proxy_create( + struct wfp_timer_manager * manager, + int timeout, + wfp_jsonrpc_send_fn * send, + void * user_data) +{ + struct wfp_jsonrpc_proxy * proxy = malloc(sizeof(struct wfp_jsonrpc_proxy)); + wfp_jsonrpc_proxy_init(proxy, manager, timeout, send, user_data); + + return proxy; +} + +void wfp_jsonrpc_proxy_dispose( + struct wfp_jsonrpc_proxy * proxy) +{ + wfp_jsonrpc_proxy_cleanup(proxy); + free(proxy); +} + +static void wfp_jsonrpc_proxy_on_timeout( + struct wfp_timer * timer, void * proxy_ptr) +{ + struct wfp_jsonrpc_proxy * proxy = proxy_ptr; + + if (proxy->request.is_pending) + { + wfp_jsonrpc_proxy_finished_fn * finished = proxy->request.finished; + void * user_data = proxy->request.user_data; + + proxy->request.is_pending = false; + proxy->request.id = 0; + proxy->request.user_data = NULL; + proxy->request.finished = NULL; + wfp_timer_cancel(timer); + + wfp_jsonrpc_propate_error(finished, user_data, WFP_BAD_TIMEOUT, "Timeout"); + } +} + +static json_t * wfp_jsonrpc_request_create( + char const * method, + int id, + char const * param_info, + va_list args) +{ + json_t * request = json_object(); + json_object_set_new(request, "method", json_string(method)); + json_t * params = json_array(); + + for (char const * param_type = param_info; '\0' != *param_type; param_type++) + { + switch(*param_type) + { + case 's': + { + char const * const value = va_arg(args, char const *); + json_array_append_new(params, json_string(value)); + } + break; + case 'i': + { + int const value = va_arg(args, int); + json_array_append_new(params, json_integer(value)); + } + break; + case 'j': + { + json_t * const value = va_arg(args, json_t *); + json_array_append_new(params, value); + } + break; + default: + fprintf(stderr, "fatal: unknown param_type '%c'\n", *param_type); + json_decref(params); + json_decref(request); + return NULL; + } + } + + + json_object_set_new(request, "params", params); + if (0 != id) + { + json_object_set_new(request, "id", json_integer(id)); + } + + return request; +} + +void wfp_jsonrpc_proxy_init( + struct wfp_jsonrpc_proxy * proxy, + struct wfp_timer_manager * timeout_manager, + int timeout, + wfp_jsonrpc_send_fn * send, + void * user_data) +{ + proxy->send = send; + proxy->timeout = timeout; + proxy->user_data = user_data; + proxy->request.is_pending = false; + proxy->request.timer = wfp_timer_create(timeout_manager, + &wfp_jsonrpc_proxy_on_timeout, proxy); +} + +void wfp_jsonrpc_proxy_cleanup( + struct wfp_jsonrpc_proxy * proxy) +{ + if (proxy->request.is_pending) + { + void * user_data = proxy->request.user_data; + wfp_jsonrpc_proxy_finished_fn * finished = proxy->request.finished; + + proxy->request.is_pending = false; + proxy->request.finished = NULL; + proxy->request.user_data = NULL; + proxy->request.id = 0; + wfp_timer_cancel(proxy->request.timer); + + wfp_jsonrpc_propate_error(finished, user_data, WFP_BAD, "Bad: cancelled pending request during shutdown"); + } + + wfp_timer_dispose(proxy->request.timer); +} + +void wfp_jsonrpc_proxy_vinvoke( + struct wfp_jsonrpc_proxy * proxy, + wfp_jsonrpc_proxy_finished_fn * finished, + void * user_data, + char const * method_name, + char const * param_info, + va_list args) +{ + if (!proxy->request.is_pending) + { + proxy->request.is_pending = true; + proxy->request.finished = finished; + proxy->request.user_data = user_data; + proxy->request.id = 42; + wfp_timer_start(proxy->request.timer, proxy->timeout); + + json_t * request = wfp_jsonrpc_request_create(method_name, proxy->request.id, param_info, args); + + bool const is_send = ((NULL != request) && (proxy->send(request, proxy->user_data))); + if (!is_send) + { + proxy->request.is_pending = false; + proxy->request.finished = NULL; + proxy->request.user_data = NULL; + proxy->request.id = 0; + wfp_timer_cancel(proxy->request.timer); + + wfp_jsonrpc_propate_error(finished, user_data, WFP_BAD, "Bad: requenst is not sent"); + } + + if (NULL != request) + { + json_decref(request); + } + } + else + { + wfp_jsonrpc_propate_error(finished, user_data, WFP_BAD_BUSY, "Busy"); + } +} + +extern void wfp_jsonrpc_proxy_vnotify( + struct wfp_jsonrpc_proxy * proxy, + char const * method_name, + char const * param_info, + va_list args) +{ + json_t * request = wfp_jsonrpc_request_create(method_name, 0, param_info, args); + + if (NULL != request) + { + proxy->send(request, proxy->user_data); + json_decref(request); + } +} + + +void wfp_jsonrpc_proxy_onresult( + struct wfp_jsonrpc_proxy * proxy, + json_t * message) +{ + struct wfp_jsonrpc_response response; + wfp_jsonrpc_response_init(&response, message); + + if ((proxy->request.is_pending) && (response.id == proxy->request.id)) + { + wfp_jsonrpc_proxy_finished_fn * finished = proxy->request.finished; + void * user_data = proxy->request.user_data; + + proxy->request.is_pending = false; + proxy->request.id = 0; + proxy->request.user_data = NULL; + proxy->request.finished = NULL; + wfp_timer_cancel(proxy->request.timer); + + finished(user_data, response.result, response.error); + } + + wfp_jsonrpc_response_cleanup(&response); +} + diff --git a/lib/webfuse_provider/impl/jsonrpc/proxy.h b/lib/webfuse_provider/impl/jsonrpc/proxy.h new file mode 100644 index 0000000..0f02edf --- /dev/null +++ b/lib/webfuse_provider/impl/jsonrpc/proxy.h @@ -0,0 +1,74 @@ +#ifndef WFP_JSONRPC_PROXY_H +#define WFP_JSONRPC_PROXY_H + +#ifndef __cplusplus +#include +#include +#include +#else +#include +#include +using std::size_t; +#endif + +#include +#include "webfuse_provider/impl/jsonrpc/send_fn.h" +#include "webfuse_provider/impl/jsonrpc/proxy_finished_fn.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct wfp_jsonrpc_proxy; +struct wfp_timer_manager; + +extern struct wfp_jsonrpc_proxy * +wfp_jsonrpc_proxy_create( + struct wfp_timer_manager * manager, + int timeout, + wfp_jsonrpc_send_fn * send, + void * user_data); + +extern void wfp_jsonrpc_proxy_dispose( + struct wfp_jsonrpc_proxy * proxy); + +//------------------------------------------------------------------------------ +/// \brief Invokes a method. +/// +/// Creates a method an sends it using the send function. +/// Proxy keeps track of method invokation. If no response is returned within +/// timeout, an error is propagated. +/// +/// \param proxy pointer to proxy instance +/// \param finished function which is called exactly once, either on success or +/// on failure. +/// \param method_name name of the method to invoke +/// \param param_info types of the param (s = string, i = integer, j = json) +/// \param ... params +//------------------------------------------------------------------------------ +extern void wfp_jsonrpc_proxy_invoke( + struct wfp_jsonrpc_proxy * proxy, + wfp_jsonrpc_proxy_finished_fn * finished, + void * user_data, + char const * method_name, + char const * param_info, + ... +); + +extern void wfp_jsonrpc_proxy_notify( + struct wfp_jsonrpc_proxy * proxy, + char const * method_name, + char const * param_info, + ... +); + +extern void wfp_jsonrpc_proxy_onresult( + struct wfp_jsonrpc_proxy * proxy, + json_t * message); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/lib/webfuse_provider/impl/jsonrpc/proxy_finished_fn.h b/lib/webfuse_provider/impl/jsonrpc/proxy_finished_fn.h new file mode 100644 index 0000000..6d2b0d3 --- /dev/null +++ b/lib/webfuse_provider/impl/jsonrpc/proxy_finished_fn.h @@ -0,0 +1,20 @@ +#ifndef WFP_JSONRPC_PROXY_FINISHED_FN_H +#define WFP_JSONRPC_PROXY_FINISHED_FN_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef void wfp_jsonrpc_proxy_finished_fn( + void * user_data, + json_t const * result, + json_t const * error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/jsonrpc/proxy_intern.h b/lib/webfuse_provider/impl/jsonrpc/proxy_intern.h new file mode 100644 index 0000000..3284040 --- /dev/null +++ b/lib/webfuse_provider/impl/jsonrpc/proxy_intern.h @@ -0,0 +1,62 @@ +#ifndef WFP_JSONRPC_PROXY_INTERN_H +#define WFP_JSONRPC_PROXY_INTERN_H + +#include "webfuse_provider/impl/jsonrpc/proxy.h" +#include "webfuse_provider/impl/jsonrpc/proxy_finished_fn.h" +#include "webfuse_provider/impl/jsonrpc/send_fn.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_timer; + +struct wfp_jsonrpc_request +{ + bool is_pending; + wfp_jsonrpc_proxy_finished_fn * finished; + void * user_data; + int id; + struct wfp_timer * timer; +}; + +struct wfp_jsonrpc_proxy +{ + struct wfp_jsonrpc_request request; + int timeout; + wfp_jsonrpc_send_fn * send; + void * user_data; +}; + +extern void +wfp_jsonrpc_proxy_init( + struct wfp_jsonrpc_proxy * proxy, + struct wfp_timer_manager * manager, + int timeout, + wfp_jsonrpc_send_fn * send, + void * user_data); + +extern void +wfp_jsonrpc_proxy_cleanup( + struct wfp_jsonrpc_proxy * proxy); + +extern void wfp_jsonrpc_proxy_vinvoke( + struct wfp_jsonrpc_proxy * proxy, + wfp_jsonrpc_proxy_finished_fn * finished, + void * user_data, + char const * method_name, + char const * param_info, + va_list args); + +extern void wfp_jsonrpc_proxy_vnotify( + struct wfp_jsonrpc_proxy * proxy, + char const * method_name, + char const * param_info, + va_list args); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/jsonrpc/proxy_variadic.c b/lib/webfuse_provider/impl/jsonrpc/proxy_variadic.c new file mode 100644 index 0000000..3f249db --- /dev/null +++ b/lib/webfuse_provider/impl/jsonrpc/proxy_variadic.c @@ -0,0 +1,28 @@ +#include "webfuse_provider/impl/jsonrpc/proxy_intern.h" + +void wfp_jsonrpc_proxy_invoke( + struct wfp_jsonrpc_proxy * proxy, + wfp_jsonrpc_proxy_finished_fn * finished, + void * user_data, + char const * method_name, + char const * param_info, + ...) +{ + va_list args; + va_start(args, param_info); + wfp_jsonrpc_proxy_vinvoke(proxy, finished, user_data, method_name, param_info, args); + va_end(args); +} + +extern void wfp_jsonrpc_proxy_notify( + struct wfp_jsonrpc_proxy * proxy, + char const * method_name, + char const * param_info, + ... +) +{ + va_list args; + va_start(args, param_info); + wfp_jsonrpc_proxy_vnotify(proxy, method_name, param_info, args); + va_end(args); +} diff --git a/lib/webfuse_provider/impl/jsonrpc/request.c b/lib/webfuse_provider/impl/jsonrpc/request.c new file mode 100644 index 0000000..9d9260d --- /dev/null +++ b/lib/webfuse_provider/impl/jsonrpc/request.c @@ -0,0 +1,81 @@ +#include "webfuse_provider/impl/jsonrpc/request.h" +#include "webfuse_provider/impl/jsonrpc/error.h" +#include + +struct wfp_jsonrpc_request +{ + int id; + wfp_jsonrpc_send_fn * send; + void * user_data; +}; + +bool +wfp_jsonrpc_is_request( + json_t * message) +{ + json_t * id = json_object_get(message, "id"); + json_t * method = json_object_get(message, "method"); + json_t * params = json_object_get(message, "params"); + + return (json_is_integer(id) && json_is_string(method) && + (json_is_array(params) || json_is_object(params))); +} + + +struct wfp_jsonrpc_request * +wfp_jsonrpc_request_create( + int id, + wfp_jsonrpc_send_fn * send, + void * user_data) +{ + struct wfp_jsonrpc_request * request = malloc(sizeof(struct wfp_jsonrpc_request)); + request->id = id; + request->send = send; + request->user_data = user_data; + + return request; +} + +void +wfp_jsonrpc_request_dispose( + struct wfp_jsonrpc_request * request) +{ + free(request); +} + +void * +wfp_jsonrpc_request_get_userdata( + struct wfp_jsonrpc_request * request) +{ + return request->user_data; +} + + +void +wfp_jsonrpc_respond( + struct wfp_jsonrpc_request * request, + json_t * result) +{ + json_t * response = json_object(); + json_object_set_new(response, "result", result); + json_object_set_new(response, "id", json_integer(request->id)); + + request->send(response, request->user_data); + json_decref(response); + wfp_jsonrpc_request_dispose(request); +} + +void wfp_jsonrpc_respond_error( + struct wfp_jsonrpc_request * request, + int code, + char const * message) +{ + json_t * response = json_object(); + json_object_set_new(response, "error", wfp_jsonrpc_error(code, message)); + json_object_set_new(response, "id", json_integer(request->id)); + + request->send(response, request->user_data); + json_decref(response); + wfp_jsonrpc_request_dispose(request); +} + diff --git a/lib/webfuse_provider/impl/jsonrpc/request.h b/lib/webfuse_provider/impl/jsonrpc/request.h new file mode 100644 index 0000000..aa0fd2e --- /dev/null +++ b/lib/webfuse_provider/impl/jsonrpc/request.h @@ -0,0 +1,53 @@ +#ifndef WFP_JSONRPC_REQUEST_H +#define WFP_JSONRPC_REQUEST_H + +#ifndef __cplusplus +#include +#include +#include +#else +#include +#include +using std::size_t; +#endif + +#include +#include "webfuse_provider/impl/jsonrpc/send_fn.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_jsonrpc_request; + +extern bool wfp_jsonrpc_is_request( + json_t * message); + +extern struct wfp_jsonrpc_request * +wfp_jsonrpc_request_create( + int id, + wfp_jsonrpc_send_fn * send, + void * user_data); + +extern void wfp_jsonrpc_request_dispose( + struct wfp_jsonrpc_request * request); + +extern void * wfp_jsonrpc_request_get_userdata( + struct wfp_jsonrpc_request * request); + +extern void wfp_jsonrpc_respond( + struct wfp_jsonrpc_request * request, + json_t * result); + +extern void wfp_jsonrpc_respond_error( + struct wfp_jsonrpc_request * request, + int code, + char const * message); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/jsonrpc/response.c b/lib/webfuse_provider/impl/jsonrpc/response.c new file mode 100644 index 0000000..acc7b49 --- /dev/null +++ b/lib/webfuse_provider/impl/jsonrpc/response.c @@ -0,0 +1,68 @@ +#include "webfuse_provider/impl/jsonrpc/response_intern.h" +#include "webfuse_provider/impl/jsonrpc/error.h" +#include "webfuse_provider/status.h" + +bool +wfp_jsonrpc_is_response( + json_t * message) +{ + json_t * id = json_object_get(message, "id"); + json_t * err = json_object_get(message, "error"); + json_t * result = json_object_get(message, "result"); + + return (json_is_integer(id) && + (json_is_object(err) || (NULL != result))); +} + + +void +wfp_jsonrpc_response_init( + struct wfp_jsonrpc_response * result, + json_t * response) +{ + result->id = -1; + result->result = NULL; + result->error = NULL; + + json_t * id_holder = json_object_get(response, "id"); + if (!json_is_integer(id_holder)) + { + result->error = wfp_jsonrpc_error(WFP_BAD_FORMAT, "invalid format: missing id"); + return; + } + + result->id = json_integer_value(id_holder); + result->result = json_object_get(response, "result"); + if (NULL != result->result) + { + json_incref(result->result); + } + else + { + json_t * error = json_object_get(response, "error"); + if ((json_is_object(error)) && (json_is_integer(json_object_get(error, "code")))) + { + result->error = error; + json_incref(result->error); + } + else + { + result->error = wfp_jsonrpc_error(WFP_BAD_FORMAT, "invalid format: invalid error object"); + } + } +} + +void +wfp_jsonrpc_response_cleanup( + struct wfp_jsonrpc_response * response) +{ + if (NULL != response->result) + { + json_decref(response->result); + } + + if (NULL != response->error) + { + json_decref(response->error); + } +} diff --git a/lib/webfuse_provider/impl/jsonrpc/response.h b/lib/webfuse_provider/impl/jsonrpc/response.h new file mode 100644 index 0000000..eae5cb8 --- /dev/null +++ b/lib/webfuse_provider/impl/jsonrpc/response.h @@ -0,0 +1,22 @@ +#ifndef WFP_JSONRPC_RESPONSE_H +#define WFP_JSONRPC_RESPONSE_H + +#ifndef __cplusplus +#include +#endif + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern bool wfp_jsonrpc_is_response( + json_t * message); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/jsonrpc/response_intern.h b/lib/webfuse_provider/impl/jsonrpc/response_intern.h new file mode 100644 index 0000000..e03dea7 --- /dev/null +++ b/lib/webfuse_provider/impl/jsonrpc/response_intern.h @@ -0,0 +1,36 @@ +#ifndef WFP_JSONRPC_RESPONSE_INTERN_H +#define WFP_JSONRPC_RESPONSE_INTERN_H + +#include "webfuse_provider/impl/jsonrpc/response.h" + +#ifndef __cplusplus +#include +#else +#include +using std::size_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct wfp_jsonrpc_response +{ + json_t * result; + json_t * error; + int id; +}; + +extern void wfp_jsonrpc_response_init( + struct wfp_jsonrpc_response * response, + json_t * message); + +extern void wfp_jsonrpc_response_cleanup( + struct wfp_jsonrpc_response * response); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/lib/webfuse_provider/impl/jsonrpc/send_fn.h b/lib/webfuse_provider/impl/jsonrpc/send_fn.h new file mode 100644 index 0000000..9c4ea1a --- /dev/null +++ b/lib/webfuse_provider/impl/jsonrpc/send_fn.h @@ -0,0 +1,23 @@ +#ifndef WFP_JSONRPC_SEND_FN_H +#define WFP_JSONRPC_SEND_FN_H + +#ifndef __cplusplus +#include +#endif + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef bool wfp_jsonrpc_send_fn( + json_t * request, + void * user_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/message.c b/lib/webfuse_provider/impl/message.c new file mode 100644 index 0000000..7943a5d --- /dev/null +++ b/lib/webfuse_provider/impl/message.c @@ -0,0 +1,24 @@ +#include "webfuse_provider/impl/message.h" + +#include +#include + +extern struct wfp_message * wfp_message_create(json_t const * value) +{ + size_t const length = json_dumpb(value, NULL, 0, JSON_COMPACT); + + char * data = malloc(sizeof(struct wfp_message) + LWS_PRE + length); + struct wfp_message * message = (struct wfp_message *) data; + message->data = &data[sizeof(struct wfp_message) + LWS_PRE]; + message->length = length; + + json_dumpb(value, message->data, length, JSON_COMPACT); + + return message; +} + +void wfp_message_dispose( + struct wfp_message * message) +{ + free(message); +} diff --git a/lib/webfuse_provider/impl/message.h b/lib/webfuse_provider/impl/message.h new file mode 100644 index 0000000..776f848 --- /dev/null +++ b/lib/webfuse_provider/impl/message.h @@ -0,0 +1,36 @@ +#ifndef WFP_MESSAGE_H +#define WFP_MESSAGE_H + +#ifndef __cplusplus +#include +#else +#include +using std::size_t; +#endif + +#include +#include "webfuse_provider/impl/util/slist.h" + +struct wfp_message +{ + struct wfp_slist_item item; + char * data; + size_t length; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern struct wfp_message * wfp_message_create( + json_t const * value); + +extern void wfp_message_dispose( + struct wfp_message * message); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/message_queue.c b/lib/webfuse_provider/impl/message_queue.c new file mode 100644 index 0000000..337bebe --- /dev/null +++ b/lib/webfuse_provider/impl/message_queue.c @@ -0,0 +1,17 @@ +#include "webfuse_provider/impl/message_queue.h" +#include "webfuse_provider/impl/message.h" +#include "webfuse_provider/impl/util/container_of.h" + +void wfp_message_queue_cleanup( + struct wfp_slist * queue) +{ + struct wfp_slist_item * item = wfp_slist_first(queue); + while (NULL != item) + { + struct wfp_slist_item * next = item->next; + struct wfp_message * message = wfp_container_of(item, struct wfp_message, item); + wfp_message_dispose(message); + item = next; + } + wfp_slist_init(queue); +} diff --git a/lib/webfuse_provider/impl/message_queue.h b/lib/webfuse_provider/impl/message_queue.h new file mode 100644 index 0000000..d839f80 --- /dev/null +++ b/lib/webfuse_provider/impl/message_queue.h @@ -0,0 +1,19 @@ +#ifndef WFP_MESSAGE_QUEUE_H +#define WFP_MESSAGE_QUEUE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_slist; + +extern void wfp_message_queue_cleanup( + struct wfp_slist * queue); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/operation/close.c b/lib/webfuse_provider/impl/operation/close.c new file mode 100644 index 0000000..3b87a49 --- /dev/null +++ b/lib/webfuse_provider/impl/operation/close.c @@ -0,0 +1,38 @@ +#include "webfuse_provider/impl/operation/close.h" +#include +#include "webfuse_provider/impl/util/util.h" + +void wfp_impl_close( + struct wfp_impl_invokation_context * context, + json_t * params, + int WFP_UNUSED_PARAM(id)) +{ + size_t const param_count = json_array_size(params); + if (4 == param_count) + { + json_t * inode_holder = json_array_get(params, 1); + json_t * handle_holder = json_array_get(params, 2); + json_t * flags_holder = json_array_get(params, 3); + + if (json_is_integer(inode_holder) && + json_is_integer(handle_holder) && + json_is_integer(flags_holder)) + { + ino_t inode = (ino_t) json_integer_value(inode_holder); + uint32_t handle = (uint32_t) (json_integer_value(handle_holder) & UINT32_MAX); + int flags = json_integer_value(flags_holder); + + context->provider->close(inode, handle, flags, context->user_data); + } + } + +} + +void wfp_impl_close_default( + ino_t WFP_UNUSED_PARAM(inode), + uint32_t WFP_UNUSED_PARAM(handle), + int WFP_UNUSED_PARAM(flags), + void * WFP_UNUSED_PARAM(user_data)) +{ + // empty +} \ No newline at end of file diff --git a/lib/webfuse_provider/impl/operation/close.h b/lib/webfuse_provider/impl/operation/close.h new file mode 100644 index 0000000..7c6fd7b --- /dev/null +++ b/lib/webfuse_provider/impl/operation/close.h @@ -0,0 +1,26 @@ +#ifndef WFP_IMPL_OPERATION_CLOSE_H +#define WFP_IMPL_OPERATION_CLOSE_H + +#include "webfuse_provider/impl/provider.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void wfp_impl_close( + struct wfp_impl_invokation_context * context, + json_t * params, + int id); + +extern void wfp_impl_close_default( + ino_t inode, + uint32_t handle, + int flags, + void * user_data); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/lib/webfuse_provider/impl/operation/error.h b/lib/webfuse_provider/impl/operation/error.h new file mode 100644 index 0000000..0d6c181 --- /dev/null +++ b/lib/webfuse_provider/impl/operation/error.h @@ -0,0 +1,22 @@ +#ifndef WFP_OPERATION_IMPL_ERROR_H +#define WFP_OPERATION_IMPL_ERROR_H + +#include "webfuse_provider/api.h" +#include "webfuse_provider/status.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_request; + +extern WFP_API void wfp_impl_respond_error( + struct wfp_request * request, + wfp_status status); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/operation/getattr.c b/lib/webfuse_provider/impl/operation/getattr.c new file mode 100644 index 0000000..7c71f08 --- /dev/null +++ b/lib/webfuse_provider/impl/operation/getattr.c @@ -0,0 +1,64 @@ +#include "webfuse_provider/impl/operation/getattr.h" + +#include + +#include "webfuse_provider/impl/operation/error.h" +#include "webfuse_provider/impl/request.h" +#include "webfuse_provider/impl/util/util.h" + + +void wfp_impl_getattr( + struct wfp_impl_invokation_context * context, + json_t * params, + int id) +{ + size_t const count = json_array_size(params); + if (2 == count) + { + json_t * inode_holder = json_array_get(params, 1); + + if (json_is_integer(inode_holder)) + { + ino_t inode = (ino_t) json_integer_value(inode_holder); + struct wfp_request * request = wfp_impl_request_create(context->request, id); + + context->provider->getattr(request, inode, context->user_data); + } + } +} + +void wfp_impl_getattr_default( + struct wfp_request * request, + ino_t WFP_UNUSED_PARAM(inode), + void * WFP_UNUSED_PARAM(user_data)) +{ + wfp_impl_respond_error(request, WFP_BAD_NOENTRY); +} + +void wfp_impl_respond_getattr( + struct wfp_request * request, + struct stat const * stat) +{ + bool const is_file = (0 != (stat->st_mode & S_IFREG)); + bool const is_dir = (0 != (stat->st_mode & S_IFDIR)); + + json_t * result = json_object(); + json_object_set_new(result, "inode", json_integer(stat->st_ino)); + json_object_set_new(result, "mode", json_integer(stat->st_mode & 0777)); + json_object_set_new(result, "atime", json_integer(stat->st_atime)); + json_object_set_new(result, "mtime", json_integer(stat->st_mtime)); + json_object_set_new(result, "ctime", json_integer(stat->st_ctime)); + + if (is_file) + { + json_object_set_new(result, "type", json_string("file")); + json_object_set_new(result, "size", json_integer(stat->st_size)); + } + + if (is_dir) + { + json_object_set_new(result, "type", json_string("dir")); + } + + wfp_impl_respond(request, result); +} diff --git a/lib/webfuse_provider/impl/operation/getattr.h b/lib/webfuse_provider/impl/operation/getattr.h new file mode 100644 index 0000000..a14f8de --- /dev/null +++ b/lib/webfuse_provider/impl/operation/getattr.h @@ -0,0 +1,29 @@ +#ifndef WFP_IMPL_OPERATION_GETATTR_H +#define WFP_IMPL_OPERATION_GETATTR_H + +#include "webfuse_provider/impl/provider.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void wfp_impl_respond_getattr( + struct wfp_request * request, + struct stat const * stat); + +extern void wfp_impl_getattr( + struct wfp_impl_invokation_context * context, + json_t * params, + int id); + +extern void wfp_impl_getattr_default( + struct wfp_request * request, + ino_t inode, + void * user_data); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/lib/webfuse_provider/impl/operation/lookup.c b/lib/webfuse_provider/impl/operation/lookup.c new file mode 100644 index 0000000..3f39c4a --- /dev/null +++ b/lib/webfuse_provider/impl/operation/lookup.c @@ -0,0 +1,68 @@ +#include "webfuse_provider/impl/operation/lookup.h" + +#include + +#include "webfuse_provider/impl/operation/error.h" +#include "webfuse_provider/impl/request.h" +#include "webfuse_provider/impl/util/util.h" + +void wfp_impl_lookup( + struct wfp_impl_invokation_context * context, + json_t * params, + int id) +{ + size_t const count = json_array_size(params); + if (3 == count) + { + json_t * inode_holder = json_array_get(params, 1); + json_t * name_holder = json_array_get(params, 2); + + if (json_is_integer(inode_holder) && + json_is_string(name_holder)) + { + ino_t inode = json_integer_value(inode_holder); + char const * name = json_string_value(name_holder); + + struct wfp_request * request = wfp_impl_request_create(context->request, id); + context->provider->lookup(request, inode, name, context->user_data); + } + } +} + +void wfp_impl_respond_lookup( + struct wfp_request * request, + struct stat const * stat) +{ + bool const is_file = (0 != (stat->st_mode & S_IFREG)); + bool const is_dir = (0 != (stat->st_mode & S_IFDIR)); + + json_t * result = json_object(); + json_object_set_new(result, "inode", json_integer(stat->st_ino)); + json_object_set_new(result, "mode", json_integer(stat->st_mode & 0777)); + json_object_set_new(result, "atime", json_integer(stat->st_atime)); + json_object_set_new(result, "mtime", json_integer(stat->st_mtime)); + json_object_set_new(result, "ctime", json_integer(stat->st_ctime)); + + if (is_file) + { + json_object_set_new(result, "type", json_string("file")); + json_object_set_new(result, "size", json_integer(stat->st_size)); + } + + if (is_dir) + { + json_object_set_new(result, "type", json_string("dir")); + } + + wfp_impl_respond(request, result); +} + +void wfp_impl_lookup_default( + struct wfp_request * request, + ino_t WFP_UNUSED_PARAM(parent), + char const * WFP_UNUSED_PARAM(name), + void * WFP_UNUSED_PARAM(user_data)) +{ + wfp_impl_respond_error(request, WFP_BAD_NOENTRY); +} + diff --git a/lib/webfuse_provider/impl/operation/lookup.h b/lib/webfuse_provider/impl/operation/lookup.h new file mode 100644 index 0000000..c4caf6c --- /dev/null +++ b/lib/webfuse_provider/impl/operation/lookup.h @@ -0,0 +1,30 @@ +#ifndef WFP_IMPL_OPERATION_LOOKUP_H +#define WFP_IMPL_OPERATION_LOOKUP_H + +#include "webfuse_provider/impl/provider.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void wfp_impl_respond_lookup( + struct wfp_request * request, + struct stat const * stat); + +extern void wfp_impl_lookup( + struct wfp_impl_invokation_context * context, + json_t * params, + int id); + +extern void wfp_impl_lookup_default( + struct wfp_request * request, + ino_t parent, + char const * name, + void * user_data); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/lib/webfuse_provider/impl/operation/open.c b/lib/webfuse_provider/impl/operation/open.c new file mode 100644 index 0000000..4803cfc --- /dev/null +++ b/lib/webfuse_provider/impl/operation/open.c @@ -0,0 +1,47 @@ +#include "webfuse_provider/impl/operation/open.h" +#include "webfuse_provider/impl/operation/error.h" +#include "webfuse_provider/impl/request.h" +#include "webfuse_provider/impl/util/util.h" + +void wfp_impl_open( + struct wfp_impl_invokation_context * context, + json_t * params, + int id) +{ + size_t const count = json_array_size(params); + if (3 == count) + { + json_t * inode_holder = json_array_get(params, 1); + json_t * flags_holder = json_array_get(params, 2); + + if (json_is_integer(inode_holder) && + json_is_integer(flags_holder)) + { + ino_t inode = (ino_t) json_integer_value(inode_holder); + int flags = (ino_t) json_integer_value(flags_holder); + + struct wfp_request * request = wfp_impl_request_create(context->request, id); + + context->provider->open(request, inode, flags, context->user_data); + } + } +} + +void wfp_impl_open_default( + struct wfp_request * request, + ino_t WFP_UNUSED_PARAM(inode), + int WFP_UNUSED_PARAM(flags), + void * WFP_UNUSED_PARAM(user_data)) +{ + wfp_impl_respond_error(request, WFP_BAD_NOENTRY); +} + +void wfp_impl_respond_open( + struct wfp_request * request, + uint32_t handle) +{ + json_t * result = json_object(); + json_object_set_new(result, "handle", json_integer((int) handle)); + + wfp_impl_respond(request, result); +} diff --git a/lib/webfuse_provider/impl/operation/open.h b/lib/webfuse_provider/impl/operation/open.h new file mode 100644 index 0000000..052d185 --- /dev/null +++ b/lib/webfuse_provider/impl/operation/open.h @@ -0,0 +1,30 @@ +#ifndef WFP_IMPL_OPERATION_OPEN_H +#define WFP_IMPL_OPERATION_OPEN_H + +#include "webfuse_provider/impl/provider.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void wfp_impl_respond_open( + struct wfp_request * request, + uint32_t handle); + +extern void wfp_impl_open( + struct wfp_impl_invokation_context * context, + json_t * params, + int id); + +extern void wfp_impl_open_default( + struct wfp_request * request, + ino_t inode, + int flags, + void * user_data); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/lib/webfuse_provider/impl/operation/read.c b/lib/webfuse_provider/impl/operation/read.c new file mode 100644 index 0000000..0383eb2 --- /dev/null +++ b/lib/webfuse_provider/impl/operation/read.c @@ -0,0 +1,78 @@ +#include "webfuse_provider/impl/operation/read.h" + +#include + +#include "webfuse_provider/impl/operation/error.h" +#include "webfuse_provider/impl/request.h" +#include "webfuse_provider/impl/util/util.h" +#include "webfuse_provider/impl/util/base64.h" + +void wfp_impl_read( + struct wfp_impl_invokation_context * context, + json_t * params, + int id) +{ + size_t const count = json_array_size(params); + if (5 == count) + { + json_t * inode_holder = json_array_get(params, 1); + json_t * handle_holder = json_array_get(params, 2); + json_t * offset_holder = json_array_get(params, 3); + json_t * length_holder = json_array_get(params, 4); + + if (json_is_integer(inode_holder) && + json_is_integer(handle_holder) && + json_is_integer(offset_holder) && + json_is_integer(length_holder)) + { + ino_t inode = (ino_t) json_integer_value(inode_holder); + int handle = json_integer_value(handle_holder); + size_t offset = json_integer_value(offset_holder); + size_t length = json_integer_value(length_holder); + struct wfp_request * request = wfp_impl_request_create(context->request, id); + + context->provider->read(request, inode, handle, offset, length, context->user_data); + } + } +} + +void wfp_impl_read_default( + struct wfp_request * request, + ino_t WFP_UNUSED_PARAM(inode), + uint32_t WFP_UNUSED_PARAM(handle), + size_t WFP_UNUSED_PARAM(offset), + size_t WFP_UNUSED_PARAM(length), + void * WFP_UNUSED_PARAM(user_data)) +{ + wfp_impl_respond_error(request, WFP_BAD_NOENTRY); +} + +void wfp_impl_respond_read( + struct wfp_request * request, + char const * data, + size_t length) +{ + if (0 < length) + { + size_t const size = wfp_impl_base64_encoded_size(length) + 1; + char * buffer = malloc(size); + wfp_impl_base64_encode((uint8_t const *) data, length, buffer, size); + + json_t * result = json_object(); + json_object_set_new(result, "data", json_string(buffer)); + json_object_set_new(result, "format", json_string("base64")); + json_object_set_new(result, "count", json_integer((int) length)); + + wfp_impl_respond(request, result); + free(buffer); + } + else + { + json_t * result = json_object(); + json_object_set_new(result, "data", json_string("")); + json_object_set_new(result, "format", json_string("identity")); + json_object_set_new(result, "count", json_integer(0)); + + wfp_impl_respond(request, result); + } +} \ No newline at end of file diff --git a/lib/webfuse_provider/impl/operation/read.h b/lib/webfuse_provider/impl/operation/read.h new file mode 100644 index 0000000..be6f734 --- /dev/null +++ b/lib/webfuse_provider/impl/operation/read.h @@ -0,0 +1,33 @@ +#ifndef WFP_IMPL_OPERATION_READ_H +#define WFP_IMPL_OPERATION_READ_H + +#include "webfuse_provider/impl/provider.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void wfp_impl_respond_read( + struct wfp_request * request, + char const * data, + size_t length); + +extern void wfp_impl_read( + struct wfp_impl_invokation_context * context, + json_t * params, + int id); + +extern void wfp_impl_read_default( + struct wfp_request * request, + ino_t inode, + uint32_t handle, + size_t offset, + size_t length, + void * user_data); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/lib/webfuse_provider/impl/operation/readdir.c b/lib/webfuse_provider/impl/operation/readdir.c new file mode 100644 index 0000000..6813f2d --- /dev/null +++ b/lib/webfuse_provider/impl/operation/readdir.c @@ -0,0 +1,42 @@ +#include "webfuse_provider/impl/operation/readdir.h" +#include "webfuse_provider/impl/operation/error.h" +#include "webfuse_provider/impl/dirbuffer.h" +#include "webfuse_provider/impl/request.h" +#include "webfuse_provider/impl/util/util.h" + +void wfp_impl_readdir( + struct wfp_impl_invokation_context * context, + json_t * params, + int id) +{ + size_t const count = json_array_size(params); + if (2 == count) + { + json_t * inode_holder = json_array_get(params, 1); + + if (json_is_integer(inode_holder)) + { + ino_t inode = (ino_t) json_integer_value(inode_holder); + struct wfp_request * request = wfp_impl_request_create(context->request, id); + + context->provider->readdir(request, inode, context->user_data); + } + } +} + +void wfp_impl_readdir_default( + struct wfp_request * request, + ino_t WFP_UNUSED_PARAM(directory), + void * WFP_UNUSED_PARAM(user_data)) +{ + wfp_impl_respond_error(request, WFP_BAD_NOENTRY); +} + +void wfp_impl_respond_readdir( + struct wfp_request * request, + struct wfp_dirbuffer * dirbuffer) +{ + json_t * result = wfp_impl_dirbuffer_take(dirbuffer); + wfp_impl_respond(request, result); +} + diff --git a/lib/webfuse_provider/impl/operation/readdir.h b/lib/webfuse_provider/impl/operation/readdir.h new file mode 100644 index 0000000..8884e9e --- /dev/null +++ b/lib/webfuse_provider/impl/operation/readdir.h @@ -0,0 +1,29 @@ +#ifndef WFP_IMPL_OPERATION_READDIR_H +#define WFP_IMPL_OPERATION_READDIR_H + +#include "webfuse_provider/impl/provider.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void wfp_impl_respond_readdir( + struct wfp_request * request, + struct wfp_dirbuffer * dirbuffer); + +extern void wfp_impl_readdir( + struct wfp_impl_invokation_context * context, + json_t * params, + int id); + +extern void wfp_impl_readdir_default( + struct wfp_request * request, + ino_t directory, + void * user_data); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/lib/webfuse_provider/impl/provider.c b/lib/webfuse_provider/impl/provider.c new file mode 100644 index 0000000..1042802 --- /dev/null +++ b/lib/webfuse_provider/impl/provider.c @@ -0,0 +1,125 @@ +#include "webfuse_provider/impl/provider.h" + +#include +#include + +#include "webfuse_provider/impl/request.h" +#include "webfuse_provider/impl/operation/lookup.h" +#include "webfuse_provider/impl/operation/getattr.h" +#include "webfuse_provider/impl/operation/readdir.h" +#include "webfuse_provider/impl/operation/open.h" +#include "webfuse_provider/impl/operation/close.h" +#include "webfuse_provider/impl/operation/read.h" + +typedef void wfp_impl_invoke_fn( + struct wfp_impl_invokation_context * context, + json_t * params, + int id); + + +struct wfp_impl_method +{ + char const * name; + wfp_impl_invoke_fn * invoke; + bool is_notification; +}; + +static void wfp_impl_provider_invoke_method( + struct wfp_impl_invokation_context * context, + char const * method_name, + json_t * params, + int id) +{ + static struct wfp_impl_method const methods[] = + { + {"lookup", &wfp_impl_lookup, false}, + {"getattr", &wfp_impl_getattr, false}, + {"readdir", &wfp_impl_readdir, false}, + {"open", &wfp_impl_open, false}, + {"close", &wfp_impl_close, true}, + {"read", &wfp_impl_read, false} + }; + static size_t const count = sizeof(methods) / sizeof(methods[0]); + + for (size_t i = 0; i < count; i++) + { + struct wfp_impl_method const * method = &methods[i]; + if (0 == strcmp(method_name, method->name)) + { + if ((0 < id) || (method->is_notification)) + { + method->invoke(context, params, id); + } + break; + } + } +} + +void wfp_impl_provider_init( + struct wfp_provider * provider) +{ + provider->lookup = &wfp_impl_lookup_default; + provider->getattr = &wfp_impl_getattr_default; + provider->readdir = &wfp_impl_readdir_default; + provider->open = &wfp_impl_open_default; + provider->close = &wfp_impl_close_default; + provider->read = &wfp_impl_read_default; + provider->connected = &wfp_impl_connected_default; + provider->disconnected = &wfp_impl_disconnected_default; + provider->get_credentials = NULL; +} + +void wfp_impl_provider_init_from_prototype( + struct wfp_provider * provider, + struct wfp_provider const * prototype) +{ + provider->lookup = prototype->lookup; + provider->getattr = prototype->getattr; + provider->readdir = prototype->readdir; + provider->open = prototype->open; + provider->close = prototype->close; + provider->read = prototype->read; + provider->connected = prototype->connected; + provider->disconnected = prototype->disconnected; + provider->get_credentials = prototype->get_credentials; +} + +void wfp_impl_provider_invoke( + struct wfp_impl_invokation_context * context, + json_t * request) +{ + json_t * method_holder = json_object_get(request, "method"); + json_t * params = json_object_get(request, "params"); + json_t * id_holder = json_object_get(request, "id"); + + if ((json_is_string(method_holder)) && (json_is_array(params))) + { + char const * method = json_string_value(method_holder); + int id = json_is_integer(id_holder) ? json_integer_value(id_holder) : 0; + + wfp_impl_provider_invoke_method(context, method, params, id); + } +} + +void wfp_impl_connected_default( + void * user_data) +{ + (void) user_data; + + // empty +} + +void wfp_impl_disconnected_default( + void * user_data) +{ + (void) user_data; + + // empty +} + + bool wfp_impl_provider_is_authentication_enabled( + struct wfp_provider * provider) +{ + return (NULL != provider->get_credentials); +} + diff --git a/lib/webfuse_provider/impl/provider.h b/lib/webfuse_provider/impl/provider.h new file mode 100644 index 0000000..f3c00c7 --- /dev/null +++ b/lib/webfuse_provider/impl/provider.h @@ -0,0 +1,62 @@ +#ifndef WFP_IMPL_PROVIDER_H +#define WFP_IMPL_PROVIDER_H + +#ifndef __cplusplus +#include +#endif + +#include +#include "webfuse_provider/client_config.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_provider +{ + wfp_connected_fn * connected; + wfp_disconnected_fn * disconnected; + wfp_lookup_fn * lookup; + wfp_getattr_fn * getattr; + wfp_readdir_fn * readdir; + wfp_open_fn * open; + wfp_close_fn * close; + wfp_read_fn * read; + wfp_get_credentials_fn * get_credentials; +}; + +struct wfp_impl_invokation_context +{ + struct wfp_provider const * provider; + void * user_data; + struct wfp_request * request; +}; + +extern void wfp_impl_provider_init( + struct wfp_provider * provider); + +extern void wfp_impl_provider_init_from_prototype( + struct wfp_provider * provider, + struct wfp_provider const * prototype); + + +extern void wfp_impl_provider_invoke( + struct wfp_impl_invokation_context * context, + json_t * request); + +extern bool wfp_impl_provider_is_authentication_enabled( + struct wfp_provider * provider); + +extern void wfp_impl_connected_default( + void * user_data); + +extern void wfp_impl_disconnected_default( + void * user_data); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/lib/webfuse_provider/impl/request.c b/lib/webfuse_provider/impl/request.c new file mode 100644 index 0000000..85f5087 --- /dev/null +++ b/lib/webfuse_provider/impl/request.c @@ -0,0 +1,52 @@ +#include "webfuse_provider/impl/request.h" + +#include +#include "webfuse_provider/impl/operation/error.h" + +struct wfp_request * wfp_impl_request_create( + struct wfp_request * prototype, + int id) +{ + struct wfp_request * request = malloc(sizeof(struct wfp_request)); + request->respond = prototype->respond; + request->user_data = prototype->user_data; + request->id = id; + + return request; +} + +void wfp_impl_request_dispose( + struct wfp_request * request) +{ + free(request); +} + +extern void wfp_impl_respond( + struct wfp_request * request, + json_t * result) +{ + json_t * response = json_object(); + json_object_set_new(response, "result", result); + json_object_set_new(response, "id", json_integer(request->id)); + + request->respond(response, request->user_data); + + json_decref(response); + wfp_impl_request_dispose(request); +} + +void wfp_impl_respond_error( + struct wfp_request * request, + wfp_status status) +{ + json_t * response = json_object(); + json_t * error = json_object(); + json_object_set_new(error, "code", json_integer(status)); + json_object_set_new(response, "error", error); + json_object_set_new(response, "id", json_integer(request->id)); + + request->respond(response, request->user_data); + + json_decref(response); + wfp_impl_request_dispose(request); +} \ No newline at end of file diff --git a/lib/webfuse_provider/impl/request.h b/lib/webfuse_provider/impl/request.h new file mode 100644 index 0000000..5945c1e --- /dev/null +++ b/lib/webfuse_provider/impl/request.h @@ -0,0 +1,43 @@ +#ifndef WFP_IMPL_REQUEST_H +#define WFP_IMPL_REQUEST_H + +#include +#include "webfuse_provider/impl/provider.h" +#include "webfuse_provider/status.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef void wfp_impl_request_respond_fn( + json_t * response, + void * user_data); + +struct wfp_request +{ + wfp_impl_request_respond_fn * respond; + void * user_data; + int id; +}; + +extern void wfp_impl_respond_error( + struct wfp_request * request, + wfp_status status); + +extern struct wfp_request * wfp_impl_request_create( + struct wfp_request * prototype, + int id); + +extern void wfp_impl_request_dispose( + struct wfp_request * request); + +extern void wfp_impl_respond( + struct wfp_request * request, + json_t * result); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/status.c b/lib/webfuse_provider/impl/status.c new file mode 100644 index 0000000..5153f6b --- /dev/null +++ b/lib/webfuse_provider/impl/status.c @@ -0,0 +1,34 @@ +#include "webfuse_provider/impl/status_intern.h" + +#include + + int wfp_status_to_rc(wfp_status status) +{ + switch(status) + { + case WFP_GOOD: return 0; + case WFP_BAD_NOTIMPLEMENTED: return -ENOSYS; + case WFP_BAD_TIMEOUT: return -ETIMEDOUT; + case WFP_BAD_BUSY: return -ENOENT; + case WFP_BAD_FORMAT: return -ENOENT; + case WFP_BAD_NOENTRY: return -ENOENT; + case WFP_BAD_ACCESS_DENIED: return -EACCES; + default: return -ENOENT; + } +} + +char const * wfp_status_tostring(wfp_status status) +{ + switch(status) + { + case WFP_GOOD: return "Good"; + case WFP_BAD: return "Bad"; + case WFP_BAD_NOTIMPLEMENTED: return "Bad (not implemented)"; + case WFP_BAD_TIMEOUT: return "Bad (timeout)"; + case WFP_BAD_BUSY: return "Bad (busy)"; + case WFP_BAD_FORMAT: return "Bad (format)"; + case WFP_BAD_NOENTRY: return "Bad (no entry)"; + case WFP_BAD_ACCESS_DENIED: return "Bad (access denied)"; + default: return "Bad (unknown)"; + } +} diff --git a/lib/webfuse_provider/impl/status_intern.h b/lib/webfuse_provider/impl/status_intern.h new file mode 100644 index 0000000..075a555 --- /dev/null +++ b/lib/webfuse_provider/impl/status_intern.h @@ -0,0 +1,19 @@ +#ifndef WFP_STATUS_INTERN_H +#define WFP_STATUS_INTERN_H + +#include "webfuse_provider/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern int wfp_status_to_rc(wfp_status status); + +extern char const * wfp_status_tostring(wfp_status status); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/lib/webfuse_provider/impl/timer/manager.c b/lib/webfuse_provider/impl/timer/manager.c new file mode 100644 index 0000000..e8eac2b --- /dev/null +++ b/lib/webfuse_provider/impl/timer/manager.c @@ -0,0 +1,93 @@ +#include "webfuse_provider/impl/timer/manager_intern.h" +#include "webfuse_provider/impl/timer/timer_intern.h" +#include "webfuse_provider/impl/timer/timepoint.h" + +#include +#include + +struct wfp_timer_manager +{ + struct wfp_timer * timers; +}; + +struct wfp_timer_manager * +wfp_timer_manager_create(void) +{ + struct wfp_timer_manager * manager = malloc(sizeof(struct wfp_timer_manager)); + manager->timers = NULL; + + return manager; +} + +void +wfp_timer_manager_dispose( + struct wfp_timer_manager * manager) +{ + struct wfp_timer * timer = manager->timers; + while (NULL != timer) + { + struct wfp_timer * next = timer->next; + + wfp_timer_trigger(timer); + timer = next; + } + + free(manager); +} + + +void wfp_timer_manager_check( + struct wfp_timer_manager * manager) +{ + struct wfp_timer * timer = manager->timers; + while (NULL != timer) + { + struct wfp_timer * next = timer->next; + + if (wfp_timer_is_timeout(timer)) + { + wfp_timer_manager_removetimer(manager, timer); + wfp_timer_trigger(timer); + } + + timer = next; + } +} + +void wfp_timer_manager_addtimer( + struct wfp_timer_manager * manager, + struct wfp_timer * timer) +{ + if (NULL != manager->timers) + { + manager->timers->prev = timer; + } + + timer->next = manager->timers; + timer->prev = NULL; + manager->timers = timer; +} + +void wfp_timer_manager_removetimer( + struct wfp_timer_manager * manager, + struct wfp_timer * timer) +{ + struct wfp_timer * prev = timer->prev; + struct wfp_timer * next = timer->next; + + if (NULL != prev) + { + prev->next = next; + } + + if (NULL != next) + { + next->prev = prev; + } + + if (manager->timers == timer) + { + manager->timers = next; + } +} + diff --git a/lib/webfuse_provider/impl/timer/manager.h b/lib/webfuse_provider/impl/timer/manager.h new file mode 100644 index 0000000..2515c2b --- /dev/null +++ b/lib/webfuse_provider/impl/timer/manager.h @@ -0,0 +1,27 @@ +#ifndef WFP_TIMER_MANAGER_H +#define WFP_TIMER_MANAGER_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_timer_manager; + +extern struct wfp_timer_manager * +wfp_timer_manager_create(void); + +extern void +wfp_timer_manager_dispose( + struct wfp_timer_manager * manager); + +extern void +wfp_timer_manager_check( + struct wfp_timer_manager * manager); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/lib/webfuse_provider/impl/timer/manager_intern.h b/lib/webfuse_provider/impl/timer/manager_intern.h new file mode 100644 index 0000000..e4c65c3 --- /dev/null +++ b/lib/webfuse_provider/impl/timer/manager_intern.h @@ -0,0 +1,26 @@ +#ifndef WFP_TIMER_MANAGER_INTERN_H +#define WFP_TIMER_MANAGER_INTERN_H + +#include "webfuse_provider/impl/timer/manager.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_timer; + +extern void wfp_timer_manager_addtimer( + struct wfp_timer_manager * manager, + struct wfp_timer * timer); + +extern void wfp_timer_manager_removetimer( + struct wfp_timer_manager * manager, + struct wfp_timer * timer); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/lib/webfuse_provider/impl/timer/on_timer_fn.h b/lib/webfuse_provider/impl/timer/on_timer_fn.h new file mode 100644 index 0000000..b1bc806 --- /dev/null +++ b/lib/webfuse_provider/impl/timer/on_timer_fn.h @@ -0,0 +1,19 @@ +#ifndef WFP_TIMER_ON_TIMER_FN_H +#define WFP_TIMER_ON_TIMER_FN_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_timer; + +typedef void wfp_timer_on_timer_fn( + struct wfp_timer * timer, + void * user_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/timer/timepoint.c b/lib/webfuse_provider/impl/timer/timepoint.c new file mode 100644 index 0000000..eb281ef --- /dev/null +++ b/lib/webfuse_provider/impl/timer/timepoint.c @@ -0,0 +1,31 @@ +#include "webfuse_provider/impl/timer/timepoint.h" + +#include + +#define WFP_TIMER_MSEC_PER_SEC ((wfp_timer_timepoint) 1000) +#define WFP_TIMER_NSEC_PER_MSEC ((wfp_timer_timepoint) 1000 * 1000) + +wfp_timer_timepoint wfp_timer_timepoint_now(void) +{ + struct timespec tp; + clock_gettime(CLOCK_MONOTONIC, &tp); + + wfp_timer_timepoint const now = (tp.tv_sec * WFP_TIMER_MSEC_PER_SEC) + (tp.tv_nsec / WFP_TIMER_NSEC_PER_MSEC); + return now; +} + +wfp_timer_timepoint wfp_timer_timepoint_in_msec(wfp_timer_timediff value) +{ + wfp_timer_timepoint const now = wfp_timer_timepoint_now(); + wfp_timer_timepoint result = now + ((wfp_timer_timepoint) value); + + return result; +} + +bool wfp_timer_timepoint_is_elapsed(wfp_timer_timepoint tp) +{ + wfp_timer_timepoint const now = wfp_timer_timepoint_now(); + wfp_timer_timediff const diff = (wfp_timer_timediff) (tp - now); + + return (0 > diff); +} diff --git a/lib/webfuse_provider/impl/timer/timepoint.h b/lib/webfuse_provider/impl/timer/timepoint.h new file mode 100644 index 0000000..1990138 --- /dev/null +++ b/lib/webfuse_provider/impl/timer/timepoint.h @@ -0,0 +1,31 @@ +#ifndef WFP_TIMER_TIMEPOINT_H +#define WFP_TIMER_TIMEPOINT_H + +#ifndef __cplusplus +#include +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef uint64_t wfp_timer_timepoint; +typedef int64_t wfp_timer_timediff; + +extern wfp_timer_timepoint wfp_timer_timepoint_now(void); + +extern wfp_timer_timepoint wfp_timer_timepoint_in_msec( + wfp_timer_timediff value); + +extern bool wfp_timer_timepoint_is_elapsed( + wfp_timer_timepoint timepoint); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/timer/timer.c b/lib/webfuse_provider/impl/timer/timer.c new file mode 100644 index 0000000..01b46e1 --- /dev/null +++ b/lib/webfuse_provider/impl/timer/timer.c @@ -0,0 +1,67 @@ +#include "webfuse_provider/impl/timer/timer_intern.h" +#include "webfuse_provider/impl/timer/manager_intern.h" +#include "webfuse_provider/impl/timer/timepoint.h" + +#include +#include +#include + +struct wfp_timer * +wfp_timer_create( + struct wfp_timer_manager * manager, + wfp_timer_on_timer_fn * on_timer, + void * user_data) +{ + struct wfp_timer * timer = malloc(sizeof(struct wfp_timer)); + timer->manager = manager; + timer->timeout = 0; + timer->on_timer = on_timer; + timer->user_data = user_data; + timer->prev = NULL; + timer->next = NULL; + + return timer; +} + +void +wfp_timer_dispose( + struct wfp_timer * timer) +{ + free(timer); +} + +void wfp_timer_start( + struct wfp_timer * timer, + int timeout_ms) +{ + timer->timeout = wfp_timer_timepoint_in_msec(timeout_ms); + + wfp_timer_manager_addtimer(timer->manager, timer); +} + +void wfp_timer_cancel( + struct wfp_timer * timer) +{ + wfp_timer_manager_removetimer(timer->manager, timer); + + timer->timeout = 0; +} + +bool wfp_timer_is_timeout( + struct wfp_timer * timer) +{ + return wfp_timer_timepoint_is_elapsed(timer->timeout); +} + + +void wfp_timer_trigger( + struct wfp_timer * timer) +{ + if (0 != timer->on_timer) + { + timer->prev = NULL; + timer->next = NULL; + + timer->on_timer(timer, timer->user_data); + } +} diff --git a/lib/webfuse_provider/impl/timer/timer.h b/lib/webfuse_provider/impl/timer/timer.h new file mode 100644 index 0000000..d4d99ee --- /dev/null +++ b/lib/webfuse_provider/impl/timer/timer.h @@ -0,0 +1,37 @@ +#ifndef WFP_TIMER_TIMER_H +#define WFP_TIMER_TIMER_H + +#include "webfuse_provider/impl/timer/on_timer_fn.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_timer; +struct wfp_timer_manager; + +extern struct wfp_timer * +wfp_timer_create( + struct wfp_timer_manager * manager, + wfp_timer_on_timer_fn * on_timer, + void * user_data); + +extern void +wfp_timer_dispose( + struct wfp_timer * timer); + +extern void +wfp_timer_start( + struct wfp_timer * timer, + int timeout_ms); + +extern void +wfp_timer_cancel( + struct wfp_timer * timer); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/timer/timer_intern.h b/lib/webfuse_provider/impl/timer/timer_intern.h new file mode 100644 index 0000000..88b668b --- /dev/null +++ b/lib/webfuse_provider/impl/timer/timer_intern.h @@ -0,0 +1,40 @@ +#ifndef WFP_TIMER_TIMER_H +#define WFP_TIMER_TIMER_H + +#include "webfuse_provider/impl/timer/timer.h" +#include "webfuse_provider/impl/timer/on_timer_fn.h" +#include "webfuse_provider/impl/timer/timepoint.h" + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_timer +{ + struct wfp_timer_manager * manager; + wfp_timer_timepoint timeout; + wfp_timer_on_timer_fn * on_timer; + void * user_data; + struct wfp_timer * next; + struct wfp_timer * prev; +}; + +extern bool wfp_timer_is_timeout( + struct wfp_timer * timer); + +extern void wfp_timer_trigger( + struct wfp_timer * timer); + + +#ifdef __cplusplus +} +#endif + + + +#endif diff --git a/lib/webfuse_provider/impl/util/base64.c b/lib/webfuse_provider/impl/util/base64.c new file mode 100644 index 0000000..061d044 --- /dev/null +++ b/lib/webfuse_provider/impl/util/base64.c @@ -0,0 +1,183 @@ +#include "webfuse_provider/impl/util/base64.h" + +static const uint8_t wfp_impl_base64_decode_table[256] = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 0 + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 1 + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 62, 0x80, 0x80, 0x80, 63, // 2 + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0x80, 0x80, 0x80, 0, 0x80, 0x80, // 3 + 0x80, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 4 + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0x80, 0x80, 0x80, 0x80, 0x80, // 5 + 0x80, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 6 + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0x80, 0x80, 0x80, 0x80, 0x80, // 7 + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 8 + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 9 + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // A + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // B + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // C + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // D + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // E + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // F +}; + + + +size_t wfp_impl_base64_encoded_size(size_t length) +{ + return ((length + 2) / 3) * 4; +} + +size_t wfp_impl_base64_encode( + uint8_t const * data, + size_t length, + char * buffer, + size_t buffer_size) +{ + // 0 1 2 3 4 5 6 + // 0123456789012345678901234567890123456789012345678901234567890123 + static char const table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + size_t const length_needed = wfp_impl_base64_encoded_size(length); + if (buffer_size < length_needed) + { + return 0; + } + + size_t pos = 0; + size_t out_pos = 0; + for(; (length - pos) >= 3; pos += 3) + { + buffer[out_pos++] = table[ data[pos] >> 2 ]; + buffer[out_pos++] = table[ ((data[pos ] & 0x03) << 4) | (data[pos + 1] >> 4) ]; + buffer[out_pos++] = table[ ((data[pos + 1] & 0x0f) << 2) | (data[pos + 2] >> 6) ]; + buffer[out_pos++] = table[ data[pos + 2] & 0x3f ]; + } + + switch((length - pos)) + { + case 1: + buffer[out_pos++] = table[ data[pos] >> 2 ]; + buffer[out_pos++] = table[ ((data[pos] & 0x03) << 4) ]; + buffer[out_pos++] = '='; + buffer[out_pos++] = '='; + break; + case 2: + buffer[out_pos++] = table[ data[pos] >> 2 ]; + buffer[out_pos++] = table[ ((data[pos ] & 0x03) << 4) | (data[pos + 1] >> 4) ]; + buffer[out_pos++] = table[ ((data[pos + 1] & 0x0f) << 2) ]; + buffer[out_pos++] = '='; + break; + default: + break; + } + + if (buffer_size > out_pos) + { + buffer[out_pos] = '\0'; + } + + return out_pos; +} + +size_t wfp_impl_base64_decoded_size(char const * data, size_t length) +{ + size_t result = 0; + if ((length > 0) && ((length % 4) == 0)) + { + result = (length / 4) * 3; + + if ('=' == data[length - 1]) + { + result--; + if ('=' == data[length - 2]) + { + result--; + } + } + } + + return result; +} + +size_t wfp_impl_base64_decode( + char const * data, + size_t length, + uint8_t * buffer, + size_t buffer_size) +{ + uint8_t const * table = wfp_impl_base64_decode_table; + size_t needed_size = wfp_impl_base64_decoded_size(data, length); + if ((0 == needed_size) || (buffer_size < needed_size)) + { + return 0; + } + + size_t out_pos = 0; + size_t pos = 0; + for(; pos < length - 4; pos += 4) + { + uint8_t a = table[ (unsigned char) data[pos ] ]; + uint8_t b = table[ (unsigned char) data[pos + 1] ]; + uint8_t c = table[ (unsigned char) data[pos + 2] ]; + uint8_t d = table[ (unsigned char) data[pos + 3] ]; + + buffer[out_pos++] = (a << 2) | (b >> 4); + buffer[out_pos++] = (b << 4) | (c >> 2); + buffer[out_pos++] = (c << 6) | d; + } + + // decode last block + { + uint8_t a = table[ (unsigned char) data[pos ] ]; + uint8_t b = table[ (unsigned char) data[pos + 1] ]; + uint8_t c = table[ (unsigned char) data[pos + 2] ]; + uint8_t d = table[ (unsigned char) data[pos + 3] ]; + + buffer[out_pos++] = (a << 2) | (b >> 4); + if ('=' != data[pos + 2]) + { + buffer[out_pos++] = (b << 4) | (c >> 2); + if ('=' != data[pos + 3]) + { + buffer[out_pos++] = (c << 6) | d; + } + } + } + + return out_pos; +} + +extern bool wfp_impl_base64_isvalid(char const * data, size_t length) +{ + uint8_t const * table = wfp_impl_base64_decode_table; + + if ((length == 0) || ((length % 4) != 0)) + { + return false; + } + + size_t pos = 0; + for(; pos < (length - 2); pos++) + { + unsigned char c = (unsigned char) data[pos]; + if (('=' == c) || (0x80 == table[c])) + { + return false; + } + } + + if (('=' == data[pos]) && ('=' != data[pos + 1])) + { + return false; + } + + for(;pos < length; pos++) + { + char c = data[pos]; + if (0x80 == table[ (unsigned char) c]) + { + return false; + } + } + + return true; +} diff --git a/lib/webfuse_provider/impl/util/base64.h b/lib/webfuse_provider/impl/util/base64.h new file mode 100644 index 0000000..257100e --- /dev/null +++ b/lib/webfuse_provider/impl/util/base64.h @@ -0,0 +1,40 @@ +#ifndef WFP_UTIL_BASE64_H +#define WFP_UTILBASE64_H + +#ifndef __cplusplus +#include +#include +#include +#else +#include +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern size_t wfp_impl_base64_encoded_size(size_t length); + +extern size_t wfp_impl_base64_encode( + uint8_t const * data, + size_t length, + char * buffer, + size_t buffer_size); + +extern size_t wfp_impl_base64_decoded_size(char const * data, size_t length); + +extern size_t wfp_impl_base64_decode( + char const * data, + size_t length, + uint8_t * buffer, + size_t buffer_size); + +extern bool wfp_impl_base64_isvalid(char const * data, size_t length); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/util/container_of.h b/lib/webfuse_provider/impl/util/container_of.h new file mode 100644 index 0000000..b9517de --- /dev/null +++ b/lib/webfuse_provider/impl/util/container_of.h @@ -0,0 +1,21 @@ +#ifndef WFP_UTIL_CONTAINER_OF_H +#define WFP_UTIL_CONTAINER_OF_H + +#ifndef __cplusplus +#include +#else +#include +#endif + +#ifdef __GNUC__ +#define wfp_container_of(pointer, type, member) \ + ({ \ + const typeof( ((type *)0)->member ) * __member = (pointer); \ + (type *)( (char *)__member - offsetof(type, member) ); \ + }) +#else +#define wfp_container_of(pointer, type, member) \ + (type *) (((char *) pointer) - offsetof(type, member)) +#endif + +#endif diff --git a/lib/webfuse_provider/impl/util/json_util.c b/lib/webfuse_provider/impl/util/json_util.c new file mode 100644 index 0000000..4ea81d5 --- /dev/null +++ b/lib/webfuse_provider/impl/util/json_util.c @@ -0,0 +1,27 @@ +#include "webfuse_provider/impl/util/json_util.h" + +int wfp_impl_json_get_int(json_t const * object, char const * key, int default_value) +{ + int result = default_value; + + json_t * holder = json_object_get(object, key); + if (json_is_integer(holder)) + { + result = json_integer_value(holder); + } + + return result; +} + +wfp_status +wfp_impl_jsonrpc_get_status( + json_t const * error) +{ + wfp_status status = WFP_GOOD; + if (NULL != error) + { + status = wfp_impl_json_get_int(error, "code", WFP_BAD_FORMAT); + } + + return status; +} diff --git a/lib/webfuse_provider/impl/util/json_util.h b/lib/webfuse_provider/impl/util/json_util.h new file mode 100644 index 0000000..fcc47df --- /dev/null +++ b/lib/webfuse_provider/impl/util/json_util.h @@ -0,0 +1,26 @@ +#ifndef WFP_JSON_UTIL_H +#define WFP_JSON_UTIL_H + +#include +#include "webfuse_provider/status.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern int +wfp_impl_json_get_int( + json_t const * object, + char const * key, + int default_value); + +extern wfp_status +wfp_impl_jsonrpc_get_status( + json_t const * error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/util/lws_log.c b/lib/webfuse_provider/impl/util/lws_log.c new file mode 100644 index 0000000..68b16bc --- /dev/null +++ b/lib/webfuse_provider/impl/util/lws_log.c @@ -0,0 +1,18 @@ +#include "webfuse_provider/impl/util/lws_log.h" +#include +#include + +#define WFP_LWSLOG_DISABLE 0 + +static bool wfp_impl_lwslog_is_diabled = false; + +void wfp_impl_lwslog_disable(void) +{ + if (!wfp_impl_lwslog_is_diabled) + { + lws_set_log_level(WFP_LWSLOG_DISABLE, NULL); + wfp_impl_lwslog_is_diabled = true; + } +} + + diff --git a/lib/webfuse_provider/impl/util/lws_log.h b/lib/webfuse_provider/impl/util/lws_log.h new file mode 100644 index 0000000..5afa60e --- /dev/null +++ b/lib/webfuse_provider/impl/util/lws_log.h @@ -0,0 +1,15 @@ +#ifndef WFP_LWS_LOG_H +#define WFP_LWS_LOG_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void wfp_impl_lwslog_disable(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/util/slist.c b/lib/webfuse_provider/impl/util/slist.c new file mode 100644 index 0000000..91b05ac --- /dev/null +++ b/lib/webfuse_provider/impl/util/slist.c @@ -0,0 +1,56 @@ +#include "webfuse_provider/impl/util/slist.h" +#include + +void wfp_slist_init( + struct wfp_slist * list) +{ + list->head.next = NULL; + list->last = &list->head; +} + +bool wfp_slist_empty( + struct wfp_slist * list) +{ + return (list->last == &list->head); +} + +struct wfp_slist_item * wfp_slist_first( + struct wfp_slist * list) +{ + return list->head.next; +} + +void wfp_slist_append( + struct wfp_slist * list, + struct wfp_slist_item * item) +{ + item->next = NULL; + list->last->next = item; + list->last = item; +} + +struct wfp_slist_item * wfp_slist_remove_first( + struct wfp_slist * list) +{ + return wfp_slist_remove_after(list, &list->head); +} + +struct wfp_slist_item * wfp_slist_remove_after( + struct wfp_slist * list, + struct wfp_slist_item * prev) +{ + + struct wfp_slist_item * result = prev->next; + + if (NULL != result) + { + prev->next = result->next; + + if (list->last == result) + { + list->last = prev; + } + } + + return result; +} diff --git a/lib/webfuse_provider/impl/util/slist.h b/lib/webfuse_provider/impl/util/slist.h new file mode 100644 index 0000000..95d03b4 --- /dev/null +++ b/lib/webfuse_provider/impl/util/slist.h @@ -0,0 +1,48 @@ +#ifndef WFP_UTIL_SLIST_H +#define WFP_UTIL_SLIST_H + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_slist_item +{ + struct wfp_slist_item * next; +}; + +struct wfp_slist +{ + struct wfp_slist_item head; + struct wfp_slist_item * last; +}; + +extern void wfp_slist_init( + struct wfp_slist * list); + +extern bool wfp_slist_empty( + struct wfp_slist * list); + +extern struct wfp_slist_item * wfp_slist_first( + struct wfp_slist * list); + +extern void wfp_slist_append( + struct wfp_slist * list, + struct wfp_slist_item * item); + +extern struct wfp_slist_item * wfp_slist_remove_first( + struct wfp_slist * list); + +extern struct wfp_slist_item * wfp_slist_remove_after( + struct wfp_slist * list, + struct wfp_slist_item * prev); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/util/url.c b/lib/webfuse_provider/impl/util/url.c new file mode 100644 index 0000000..b9bcf11 --- /dev/null +++ b/lib/webfuse_provider/impl/util/url.c @@ -0,0 +1,125 @@ +#include "webfuse_provider/impl/util/url.h" + +#include +#include + +struct wfp_url_protocol +{ + char const * name; + size_t name_length; + int default_port; + bool use_tls; +}; + +static bool wfp_url_readprotocol( + struct wfp_url * url, + char const * * data) +{ + static struct wfp_url_protocol const known_protocols[] = + { + {"ws://", 5, 80, false}, + {"wss://", 6, 443, true} + }; + static size_t const count = (sizeof(known_protocols) / sizeof(known_protocols[0])); + + bool found = false; + for(size_t i = 0; (!found) && (i < count); i++) + { + struct wfp_url_protocol const * protocol = &known_protocols[i]; + if (0 == strncmp(*data, protocol->name, protocol->name_length)) + { + url->port = protocol->default_port; + url->use_tls = protocol->use_tls; + *data = *data + protocol->name_length; + found = true; + } + } + + return found; +} + +static bool wfp_url_readhost( + struct wfp_url * url, + char const * * data) +{ + char * end = strpbrk(*data, ":/"); + bool const result = (NULL != end); + + if (result) + { + size_t length = end - *data; + url->host = strndup(*data, length); + *data = end; + } + + return result; +} + +static bool wfp_url_readport( + struct wfp_url * url, + char const * * data) +{ + bool result; + + if (':' == **data) + { + *data = *data + 1; + char * end = strchr(*data, '/'); + result = (NULL != end); + + if (result) + { + url->port = atoi(*data); + *data = end; + } + } + else + { + result = ('/' == **data); + } + + return result; +} + +static bool wfp_url_readpath( + struct wfp_url * url, + char const * * data) +{ + bool const result = ('/' == **data); + url->path = strdup(*data); + *data = NULL; + + return result; +} + + +bool wfp_url_init( + struct wfp_url * url, + char const * value) +{ + memset(url, 0, sizeof(struct wfp_url)); + char const * data = value; + + bool const result = + wfp_url_readprotocol(url, &data) && + wfp_url_readhost(url, &data) && + wfp_url_readport(url, &data) && + wfp_url_readpath(url, &data) + ; + + if (!result) + { + wfp_url_cleanup(url); + } + + return result; +} + +void wfp_url_cleanup( + struct wfp_url * url) +{ + free(url->host); + free(url->path); + memset(url, 0, sizeof(struct wfp_url)); +} + diff --git a/lib/webfuse_provider/impl/util/url.h b/lib/webfuse_provider/impl/util/url.h new file mode 100644 index 0000000..d85a25d --- /dev/null +++ b/lib/webfuse_provider/impl/util/url.h @@ -0,0 +1,32 @@ +#ifndef WFP_UTIL_URL_H +#define WFP_UTIL_URL_H + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif +struct wfp_url +{ + char * host; + int port; + char * path; + bool use_tls; +}; + +extern bool wfp_url_init( + struct wfp_url * url, + char const * value); + +extern void wfp_url_cleanup( + struct wfp_url * url); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse_provider/impl/util/util.h b/lib/webfuse_provider/impl/util/util.h new file mode 100644 index 0000000..0a54dd9 --- /dev/null +++ b/lib/webfuse_provider/impl/util/util.h @@ -0,0 +1,10 @@ +#ifndef WFP_UTIL_H +#define WFP_UTIL_H + +#ifdef __GNUC__ +#define WFP_UNUSED_PARAM(param) param __attribute__((unused)) +#else +#define WFP_UNUSED_PARAM(param) +#endif + +#endif diff --git a/meson.build b/meson.build index 036b954..333695c 100644 --- a/meson.build +++ b/meson.build @@ -1,15 +1,181 @@ -project('webfuse-provider', version: '0.1.0', license: 'LGPL-3.0+') +project('webfuse-provider', 'c', 'cpp', version: '0.4.0', license: 'LGPL-3.0+') +without_tests = get_option('without_tests') +without_examples = get_option('without_examples') -webfuse_provider_dep = dependency('webfuse_provider', version: '>=0.3.0', - fallback: ['webfuse', 'webfuse_provider_dep'], default_options: ['without_adapter=true']) +libwebsockets_dep = dependency('libwebsockets', version: '>=4.0.13', required: false) +if not libwebsockets_dep.found() + cmake = import('cmake') + libwebsockets = cmake.subproject('libwebsockets') + libwebsockets_dep = libwebsockets.dependency('websockets_shared') +endif + +jansson_dep = dependency('jansson', version: '>=2.11', fallback: ['jansson', 'jansson_dep']) + +pkg_config = import('pkgconfig') + +# Webfuse provider + +webfuse_provider_static = static_library('webfuse_provider', + 'lib/webfuse_provider/impl/message.c', + 'lib/webfuse_provider/impl/message_queue.c', + 'lib/webfuse_provider/impl/status.c', + 'lib/webfuse_provider/impl/util/slist.c', + 'lib/webfuse_provider/impl/util/base64.c', + 'lib/webfuse_provider/impl/util/lws_log.c', + 'lib/webfuse_provider/impl/util/json_util.c', + 'lib/webfuse_provider/impl/util/url.c', + 'lib/webfuse_provider/impl/timer/manager.c', + 'lib/webfuse_provider/impl/timer/timepoint.c', + 'lib/webfuse_provider/impl/timer/timer.c', + 'lib/webfuse_provider/impl/jsonrpc/proxy.c', + 'lib/webfuse_provider/impl/jsonrpc/proxy_variadic.c', + 'lib/webfuse_provider/impl/jsonrpc/request.c', + 'lib/webfuse_provider/impl/jsonrpc/response.c', + 'lib/webfuse_provider/impl/jsonrpc/error.c', + 'lib/webfuse_provider/api.c', + 'lib/webfuse_provider/impl/client.c', + 'lib/webfuse_provider/impl/client_config.c', + 'lib/webfuse_provider/impl/client_protocol.c', + 'lib/webfuse_provider/impl/provider.c', + 'lib/webfuse_provider/impl/request.c', + 'lib/webfuse_provider/impl/dirbuffer.c', + 'lib/webfuse_provider/impl/credentials.c', + 'lib/webfuse_provider/impl/operation/lookup.c', + 'lib/webfuse_provider/impl/operation/getattr.c', + 'lib/webfuse_provider/impl/operation/readdir.c', + 'lib/webfuse_provider/impl/operation/open.c', + 'lib/webfuse_provider/impl/operation/close.c', + 'lib/webfuse_provider/impl/operation/read.c', + c_args: ['-fvisibility=hidden'], + include_directories: ['include', 'lib'], + dependencies: [libwebsockets_dep, jansson_dep]) + +webfuse_provider_static_dep = declare_dependency( + include_directories: ['include'], + link_with: webfuse_provider_static, + dependencies: [libwebsockets_dep, jansson_dep]) + +webfuse_provider = shared_library('webfuse_provider', + 'lib/webfuse_provider/api.c', + version: meson.project_version(), + c_args: ['-fvisibility=hidden', '-DWFP_API=WFP_EXPORT'], + include_directories: ['include', 'lib'], + dependencies: [webfuse_provider_static_dep, libwebsockets_dep, jansson_dep], + install: true) + +webfuse_provider_dep = declare_dependency( + include_directories: ['include'], + link_with: [webfuse_provider], + dependencies: [libwebsockets_dep, jansson_dep]) + +install_subdir('include/webfuse_provider', install_dir: 'include') + +pkg_config.generate( + libraries: [webfuse_provider], + requires: ['libwebsockets', 'jansson'], + subdirs: '.', + version: meson.project_version(), + name: 'libwebfuse_provider', + filebase: 'webfuse_provider', + description: 'Provider library for websockets filesystem') + + +# Unit Tests + +if not without_tests + +gtest_dep = dependency('gtest', version: '>=1.10.0', fallback: ['gtest', 'gtest_dep']) +gmock_main_dep = dependency('gmock_main', version: '>=1.10.0', fallback: ['gtest', 'gmock_main_dep']) + +openssl = find_program('openssl') +test_server_certs = custom_target('test_server_certs', + output: ['server-key.pem', 'server-cert.pem'], + command: [openssl, 'req', '-x509', '-newkey', 'rsa:4096', '-keyout', 'server-key.pem', '-out', 'server-cert.pem', '-days', '365', '-nodes', '-batch', '-subj', '/CN=localhost']) +test_client_certs = custom_target('test_client_certs', + output: ['client-key.pem', 'client-cert.pem'], + command: [openssl, 'req', '-x509', '-newkey', 'rsa:4096', '-keyout', 'client-key.pem', '-out', 'client-cert.pem', '-days', '365', '-nodes', '-batch', '-subj', '/CN=localhost']) + +test_certs_dep = declare_dependency( + sources: [test_server_certs, test_client_certs]) + +alltests = executable('alltests', + 'test/webfuse_provider/test_util/timeout_watcher.cc', + 'test/webfuse_provider/test_util/ws_server.cc', + 'test/webfuse_provider/test_util/webfuse_server.cc', + 'test/webfuse_provider/test_util/client.cc', + 'test/webfuse_provider/test_util/jansson_test_environment.cc', + 'test/webfuse_provider/mocks/fake_invokation_context.cc', + 'test/webfuse_provider/mocks/mock_request.cc', + 'test/webfuse_provider/mocks/mock_provider_client.cc', + 'test/webfuse_provider/mocks/mock_provider.cc', + 'test/webfuse_provider/mocks/mock_jsonrpc_proxy.cc', + 'test/webfuse_provider/jsonrpc/mock_timer_callback.cc', + 'test/webfuse_provider/jsonrpc/mock_timer.cc', + 'test/webfuse_provider/jsonrpc/test_is_request.cc', + 'test/webfuse_provider/jsonrpc/test_request.cc', + 'test/webfuse_provider/jsonrpc/test_is_response.cc', + 'test/webfuse_provider/jsonrpc/test_response.cc', + 'test/webfuse_provider/jsonrpc/test_proxy.cc', + 'test/webfuse_provider/jsonrpc/test_response_parser.cc', + 'test/webfuse_provider/timer/test_timepoint.cc', + 'test/webfuse_provider/timer/test_timer.cc', + 'test/webfuse_provider/util/test_util.cc', + 'test/webfuse_provider/util/test_container_of.cc', + 'test/webfuse_provider/util/test_slist.cc', + 'test/webfuse_provider/util/test_base64.cc', + 'test/webfuse_provider/util/test_status.cc', + 'test/webfuse_provider/util/test_message.cc', + 'test/webfuse_provider/util/test_message_queue.cc', + 'test/webfuse_provider/util/test_url.cc', + 'test/webfuse_provider/provider/test_client.cc', + 'test/webfuse_provider/provider/test_client_protocol.cc', + 'test/webfuse_provider/provider/test_dirbuffer.cc', + 'test/webfuse_provider/provider/operation/test_close.cc', + 'test/webfuse_provider/provider/operation/test_getattr.cc', + 'test/webfuse_provider/provider/operation/test_lookup.cc', + 'test/webfuse_provider/provider/operation/test_open.cc', + 'test/webfuse_provider/provider/operation/test_read.cc', + 'test/webfuse_provider/provider/operation/test_readdir.cc', + link_args: [ + '-Wl,--wrap=wfp_timer_manager_create', + '-Wl,--wrap=wfp_timer_manager_dispose', + '-Wl,--wrap=wfp_timer_manager_check', + '-Wl,--wrap=wfp_timer_create', + '-Wl,--wrap=wfp_timer_dispose', + '-Wl,--wrap=wfp_timer_start', + '-Wl,--wrap=wfp_timer_cancel', + '-Wl,--wrap=wfp_impl_operation_context_get_proxy', + '-Wl,--wrap=wfp_jsonrpc_proxy_vinvoke', + '-Wl,--wrap=wfp_jsonrpc_proxy_vnotify', + ], + include_directories: ['include', 'lib', 'test'], + dependencies: [ + webfuse_provider_static_dep, + libwebsockets_dep, + jansson_dep, + gtest_dep, + gmock_main_dep, + test_certs_dep + ]) + +test('alltests', alltests) + +endif + +# Examples + +if not without_examples executable('webfuse-provider', - 'src/main.c', + 'examples/provider/main.c', dependencies: [webfuse_provider_dep]) executable('static-filesystem-provider', - 'src/static_filesystem/static_filesystem.c', - 'src/static_filesystem/path.c', - 'src/static_filesystem/main.c', + 'examples/static_filesystem/static_filesystem.c', + 'examples/static_filesystem/path.c', + 'examples/static_filesystem/main.c', dependencies: [webfuse_provider_dep]) + + +endif \ No newline at end of file diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..49bf188 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,2 @@ +option('without_tests', type: 'boolean', value: false, description: 'disable unit tests') +option('without_examples', type: 'boolean', value: false, description: 'disable examples') diff --git a/subprojects/gtest.wrap b/subprojects/gtest.wrap new file mode 100644 index 0000000..d52b0b3 --- /dev/null +++ b/subprojects/gtest.wrap @@ -0,0 +1,10 @@ +[wrap-file] +directory = googletest-release-1.10.0 + +source_url = https://github.com/google/googletest/archive/release-1.10.0.zip +source_filename = gtest-1.10.0.zip +source_hash = 94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91 + +patch_url = https://wrapdb.mesonbuild.com/v1/projects/gtest/1.10.0/1/get_zip +patch_filename = gtest-1.10.0-1-wrap.zip +patch_hash = 04ff14e8880e4e465f6260221e9dfd56fea6bc7cce4c4aff0dc528e4a2c8f514 \ No newline at end of file diff --git a/subprojects/libwebsockets.wrap b/subprojects/libwebsockets.wrap index f1aa3ee..da72f53 100644 --- a/subprojects/libwebsockets.wrap +++ b/subprojects/libwebsockets.wrap @@ -3,4 +3,4 @@ directory = libwebsockets-4.0.13 source_url = https://github.com/warmcat/libwebsockets/archive/v4.0.13.zip source_filename = v4.0.13.zip -source_hash = 0914ea3fdec496daf6b6a5c00f7ba1b52eb8cc3d55b66685df92920b232fd7a5 \ No newline at end of file +source_hash = 0914ea3fdec496daf6b6a5c00f7ba1b52eb8cc3d55b66685df92920b232fd7a5 diff --git a/subprojects/webfuse.wrap b/subprojects/webfuse.wrap deleted file mode 100644 index 469f97e..0000000 --- a/subprojects/webfuse.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = webfuse-0.3.1 - -source_url = https://github.com/falk-werner/webfuse/archive/v0.3.1.tar.gz -source_filename = v0.3.1.tar.gz -source_hash = 46624b5f29e8f87dd1aebfce3b13ca87e766bc59546b6e5f49665d2f8c555d62 diff --git a/test/webfuse_provider/jsonrpc/mock_timer.cc b/test/webfuse_provider/jsonrpc/mock_timer.cc new file mode 100644 index 0000000..cf01735 --- /dev/null +++ b/test/webfuse_provider/jsonrpc/mock_timer.cc @@ -0,0 +1,31 @@ +#include "webfuse_provider/jsonrpc/mock_timer.hpp" +#include "webfuse_provider/test_util/wrap.hpp" + +extern "C" +{ +static wfp_jsonrpc_test::ITimer * wfp_jsonrpc_MockTimer = nullptr; + +WFP_WRAP_FUNC0(wfp_jsonrpc_MockTimer, wfp_timer_manager *, wfp_timer_manager_create); +WFP_WRAP_FUNC1(wfp_jsonrpc_MockTimer, void, wfp_timer_manager_dispose, wfp_timer_manager *); +WFP_WRAP_FUNC1(wfp_jsonrpc_MockTimer, void, wfp_timer_manager_check, wfp_timer_manager *); + +WFP_WRAP_FUNC3(wfp_jsonrpc_MockTimer, wfp_timer *, wfp_timer_create, wfp_timer_manager *, wfp_timer_on_timer_fn *, void *); +WFP_WRAP_FUNC1(wfp_jsonrpc_MockTimer, void, wfp_timer_dispose, wfp_timer *); +WFP_WRAP_FUNC2(wfp_jsonrpc_MockTimer, void, wfp_timer_start, wfp_timer *, int); +WFP_WRAP_FUNC1(wfp_jsonrpc_MockTimer, void, wfp_timer_cancel, wfp_timer *); + +} + +namespace wfp_jsonrpc_test +{ +MockTimer::MockTimer() +{ + wfp_jsonrpc_MockTimer = this; +} + +MockTimer::~MockTimer() +{ + wfp_jsonrpc_MockTimer = nullptr; +} + +} \ No newline at end of file diff --git a/test/webfuse_provider/jsonrpc/mock_timer.hpp b/test/webfuse_provider/jsonrpc/mock_timer.hpp new file mode 100644 index 0000000..3e002ed --- /dev/null +++ b/test/webfuse_provider/jsonrpc/mock_timer.hpp @@ -0,0 +1,48 @@ +#ifndef WFP_JSONRPC_MOCK_TIMERMANAGER_HPP +#define WFP_JSONRPC_MOCK_TIMERMANAGER_HPP + +#include "webfuse_provider/impl/timer/timer.h" +#include "webfuse_provider/impl/timer/manager.h" +#include + +namespace wfp_jsonrpc_test +{ + +class ITimer +{ +public: + virtual ~ITimer() = default; + virtual wfp_timer_manager * wfp_timer_manager_create() = 0; + virtual void wfp_timer_manager_dispose(wfp_timer_manager * manager) = 0; + virtual void wfp_timer_manager_check(wfp_timer_manager * manager) = 0; + virtual wfp_timer * wfp_timer_create( + wfp_timer_manager * manager, + wfp_timer_on_timer_fn * on_timer, + void * user_data) = 0; + virtual void wfp_timer_dispose(wfp_timer * timer) = 0; + virtual void wfp_timer_start(wfp_timer * timer, int timeout_ms) = 0; + virtual void wfp_timer_cancel(wfp_timer * timer) = 0; +}; + +class MockTimer: public ITimer +{ +public: + MockTimer(); + ~MockTimer() override; + MOCK_METHOD0(wfp_timer_manager_create, wfp_timer_manager * ()); + MOCK_METHOD1(wfp_timer_manager_dispose, void(wfp_timer_manager * manager)); + MOCK_METHOD1(wfp_timer_manager_check, void (wfp_timer_manager * manager)); + MOCK_METHOD3(wfp_timer_create, wfp_timer *( + wfp_timer_manager * manager, + wfp_timer_on_timer_fn * on_timer, + void * user_data)); + MOCK_METHOD1(wfp_timer_dispose, void (wfp_timer * timer)); + MOCK_METHOD2(wfp_timer_start, void (wfp_timer * timer, int timeout_ms)); + MOCK_METHOD1(wfp_timer_cancel, void (wfp_timer * timer)); + +}; + + +} + +#endif diff --git a/test/webfuse_provider/jsonrpc/mock_timer_callback.cc b/test/webfuse_provider/jsonrpc/mock_timer_callback.cc new file mode 100644 index 0000000..cf9ab78 --- /dev/null +++ b/test/webfuse_provider/jsonrpc/mock_timer_callback.cc @@ -0,0 +1,41 @@ +#include "webfuse_provider/jsonrpc/mock_timer_callback.hpp" + +extern "C" +{ +using wfp_jsonrpc_test::MockTimerCallback; + +static void wfp_jsonrpc_test_MockTimerCallback_on_timer( + wfp_timer * timer, + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + self->on_timer(timer, user_data); +} + +} + +namespace wfp_jsonrpc_test +{ + +MockTimerCallback::MockTimerCallback() +{ + +} + +MockTimerCallback::~MockTimerCallback() +{ + +} + +wfp_timer_on_timer_fn * MockTimerCallback::on_timer_fn() +{ + return &wfp_jsonrpc_test_MockTimerCallback_on_timer; +} + +void * MockTimerCallback::user_data() +{ + return reinterpret_cast(this); +} + + +} \ No newline at end of file diff --git a/test/webfuse_provider/jsonrpc/mock_timer_callback.hpp b/test/webfuse_provider/jsonrpc/mock_timer_callback.hpp new file mode 100644 index 0000000..3dade79 --- /dev/null +++ b/test/webfuse_provider/jsonrpc/mock_timer_callback.hpp @@ -0,0 +1,23 @@ +#ifndef WFP_JSONRPC_MOCK_TIMERCALLBACK_HPP +#define WFP_JSONRPC_MOCK_TIMERCALLBACK_HPP + +#include "webfuse_provider/impl/timer/on_timer_fn.h" +#include + +namespace wfp_jsonrpc_test +{ +class MockTimerCallback +{ +public: + MockTimerCallback(); + virtual ~MockTimerCallback(); + wfp_timer_on_timer_fn * on_timer_fn(); + void * user_data(); + + MOCK_METHOD2(on_timer, void (wfp_timer * timer, void * user_data)); + +}; + +} + +#endif diff --git a/test/webfuse_provider/jsonrpc/test_is_request.cc b/test/webfuse_provider/jsonrpc/test_is_request.cc new file mode 100644 index 0000000..945ec04 --- /dev/null +++ b/test/webfuse_provider/jsonrpc/test_is_request.cc @@ -0,0 +1,112 @@ +#include +#include "webfuse_provider/impl/jsonrpc/request.h" + +TEST(wfp_jsonrpc_is_request, request_with_object_params) +{ + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("method")); + json_object_set_new(request, "params", json_object()); + json_object_set_new(request, "id", json_integer(42)); + + ASSERT_TRUE(wfp_jsonrpc_is_request(request)); + + json_decref(request); +} + +TEST(wfp_jsonrpc_is_request, request_with_array_params) +{ + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("method")); + json_object_set_new(request, "params", json_array()); + json_object_set_new(request, "id", json_integer(42)); + + ASSERT_TRUE(wfp_jsonrpc_is_request(request)); + + json_decref(request); +} + +TEST(wfp_jsonrpc_is_request, null_request) +{ + ASSERT_FALSE(wfp_jsonrpc_is_request(nullptr)); +} + +TEST(wfp_jsonrpc_is_request, invalid_request) +{ + json_t * request = json_array(); + json_array_append_new(request, json_string("method")); + json_array_append_new(request, json_object()); + json_array_append_new(request, json_integer(42)); + + ASSERT_FALSE(wfp_jsonrpc_is_request(request)); + + json_decref(request); +} + +TEST(wfp_jsonrpc_is_request, invalid_request_without_id) +{ + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("method")); + json_object_set_new(request, "params", json_object()); + + ASSERT_FALSE(wfp_jsonrpc_is_request(request)); + + json_decref(request); +} + +TEST(wfp_jsonrpc_is_request, invalid_request_due_to_invalid_id) +{ + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("method")); + json_object_set_new(request, "params", json_object()); + json_object_set_new(request, "id", json_string("42")); + + ASSERT_FALSE(wfp_jsonrpc_is_request(request)); + + json_decref(request); +} + +TEST(wfp_jsonrpc_is_request, invalid_request_without_method) +{ + json_t * request = json_object(); + json_object_set_new(request, "params", json_object()); + json_object_set_new(request, "id", json_integer(42)); + + ASSERT_FALSE(wfp_jsonrpc_is_request(request)); + + json_decref(request); +} + +TEST(wfp_jsonrpc_is_request, invalid_request_due_to_invalid_method) +{ + json_t * request = json_object(); + json_object_set_new(request, "method", json_integer(42)); + json_object_set_new(request, "params", json_object()); + json_object_set_new(request, "id", json_integer(42)); + + ASSERT_FALSE(wfp_jsonrpc_is_request(request)); + + json_decref(request); +} + +TEST(wfp_jsonrpc_is_request, invalid_request_without_params) +{ + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("method")); + json_object_set_new(request, "id", json_integer(42)); + + ASSERT_FALSE(wfp_jsonrpc_is_request(request)); + + json_decref(request); +} + +TEST(wfp_jsonrpc_is_request, invalid_request_due_to_invalid_params) +{ + json_t * request = json_object(); + json_object_set_new(request, "methdo", json_string("method")); + json_object_set_new(request, "params", json_string("params")); + json_object_set_new(request, "id", json_integer(42)); + + ASSERT_FALSE(wfp_jsonrpc_is_request(request)); + + json_decref(request); +} diff --git a/test/webfuse_provider/jsonrpc/test_is_response.cc b/test/webfuse_provider/jsonrpc/test_is_response.cc new file mode 100644 index 0000000..856f350 --- /dev/null +++ b/test/webfuse_provider/jsonrpc/test_is_response.cc @@ -0,0 +1,94 @@ +#include +#include "webfuse_provider/impl/jsonrpc/response.h" + +TEST(wfp_jsonrpc_is_response, valid_result) +{ + json_t * message = json_object(); + json_object_set_new(message, "result", json_object()); + json_object_set_new(message, "id", json_integer(42)); + + ASSERT_TRUE(wfp_jsonrpc_is_response(message)); + + json_decref(message); +} + +TEST(wfp_jsonrpc_is_response, valid_result_string) +{ + json_t * message = json_object(); + json_object_set_new(message, "result", json_string("also valid")); + json_object_set_new(message, "id", json_integer(42)); + + ASSERT_TRUE(wfp_jsonrpc_is_response(message)); + + json_decref(message); +} + +TEST(wfp_jsonrpc_is_response, valid_error) +{ + json_t * message = json_object(); + json_object_set_new(message, "error", json_object()); + json_object_set_new(message, "id", json_integer(42)); + + ASSERT_TRUE(wfp_jsonrpc_is_response(message)); + + json_decref(message); +} + +TEST(wfp_jsonrpc_is_response, invalid_null) +{ + ASSERT_FALSE(wfp_jsonrpc_is_response(nullptr)); +} + +TEST(wfp_jsonrpc_is_response, invalid_message) +{ + json_t * message = json_array(); + json_array_append_new(message, json_object()); + json_array_append_new(message, json_integer(42)); + + ASSERT_FALSE(wfp_jsonrpc_is_response(message)); + + json_decref(message); +} + +TEST(wfp_jsonrpc_is_response, invalid_missing_id) +{ + json_t * message = json_object(); + json_object_set_new(message, "result", json_object()); + + ASSERT_FALSE(wfp_jsonrpc_is_response(message)); + + json_decref(message); +} + +TEST(wfp_jsonrpc_is_response, invalid_id_wrong_type) +{ + json_t * message = json_object(); + json_object_set_new(message, "result", json_object()); + json_object_set_new(message, "id", json_string("42")); + + ASSERT_FALSE(wfp_jsonrpc_is_response(message)); + + json_decref(message); +} + + +TEST(wfp_jsonrpc_is_response, invalid_missing_result_and_error) +{ + json_t * message = json_object(); + json_object_set_new(message, "id", json_integer(42)); + + ASSERT_FALSE(wfp_jsonrpc_is_response(message)); + + json_decref(message); +} + +TEST(wfp_jsonrpc_is_response, invalid_error_wrong_type) +{ + json_t * message = json_object(); + json_object_set_new(message, "error", json_array()); + json_object_set_new(message, "id", json_integer(42)); + + ASSERT_FALSE(wfp_jsonrpc_is_response(message)); + + json_decref(message); +} diff --git a/test/webfuse_provider/jsonrpc/test_proxy.cc b/test/webfuse_provider/jsonrpc/test_proxy.cc new file mode 100644 index 0000000..3ecbb8e --- /dev/null +++ b/test/webfuse_provider/jsonrpc/test_proxy.cc @@ -0,0 +1,430 @@ +#include +#include "webfuse_provider/impl/jsonrpc/proxy.h" +#include "webfuse_provider/status.h" +#include "webfuse_provider/impl/timer/manager.h" + +#include "webfuse_provider/jsonrpc/mock_timer.hpp" + +#include +#include + +using namespace std::chrono_literals; +using wfp_jsonrpc_test::MockTimer; +using testing::Return; +using testing::_; +using testing::DoAll; +using testing::SaveArg; + +#define WFP_DEFAULT_TIMEOUT (10 * 1000) + +namespace +{ + int jsonrpc_get_status(json_t * error) + { + json_t * code = json_object_get(error, "code"); + return (json_is_integer(code)) ? json_integer_value(code) : WFP_BAD_FORMAT; + } + + struct SendContext + { + json_t * response; + bool result; + bool is_called; + + explicit SendContext(bool result_ = true) + : response(nullptr) + , result(result_) + , is_called(false) + { + } + + ~SendContext() + { + if (nullptr != response) + { + json_decref(response); + } + } + }; + + bool jsonrpc_send( + json_t * request, + void * user_data) + { + SendContext * context = reinterpret_cast(user_data); + context->is_called = true; + context->response = request; + json_incref(request); + + return context->result; + } + + struct FinishedContext + { + bool is_called; + json_t * result; + json_t * error; + + FinishedContext() + : is_called(false) + , result(nullptr) + , error(nullptr) + { + + } + + ~FinishedContext() + { + if (nullptr != result) + { + json_decref(result); + } + + if (nullptr != error) + { + json_decref(error); + } + } + }; + + void jsonrpc_finished( + void * user_data, + json_t const * result, + json_t const * error) + { + FinishedContext * context = reinterpret_cast(user_data); + context->is_called = true; + context->result = json_deep_copy(result); + context->error = json_deep_copy(error); + } +} + +TEST(wfp_jsonrpc_proxy, init) +{ + struct wfp_timer_manager * timer_manager = wfp_timer_manager_create(); + + SendContext context; + void * user_data = reinterpret_cast(&context); + struct wfp_jsonrpc_proxy * proxy = wfp_jsonrpc_proxy_create(timer_manager, WFP_DEFAULT_TIMEOUT, &jsonrpc_send, user_data); + + wfp_jsonrpc_proxy_dispose(proxy); + wfp_timer_manager_dispose(timer_manager); + + ASSERT_FALSE(context.is_called); +} + +TEST(wfp_jsonrpc_proxy, invoke) +{ + struct wfp_timer_manager * timer_manager = wfp_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wfp_jsonrpc_proxy * proxy = wfp_jsonrpc_proxy_create(timer_manager, WFP_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + FinishedContext finished_context; + void * finished_data = reinterpret_cast(&finished_context); + wfp_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); + + ASSERT_TRUE(send_context.is_called); + ASSERT_TRUE(json_is_object(send_context.response)); + + json_t * method = json_object_get(send_context.response, "method"); + ASSERT_TRUE(json_is_string(method)); + ASSERT_STREQ("foo", json_string_value(method)); + + json_t * params = json_object_get(send_context.response, "params"); + ASSERT_TRUE(json_is_array(params)); + ASSERT_EQ(2, json_array_size(params)); + ASSERT_TRUE(json_is_string(json_array_get(params, 0))); + ASSERT_STREQ("bar", json_string_value(json_array_get(params, 0))); + ASSERT_TRUE(json_is_integer(json_array_get(params, 1))); + ASSERT_EQ(42, json_integer_value(json_array_get(params, 1))); + + json_t * id = json_object_get(send_context.response, "id"); + ASSERT_TRUE(json_is_integer(id)); + + ASSERT_FALSE(finished_context.is_called); + + wfp_jsonrpc_proxy_dispose(proxy); + wfp_timer_manager_dispose(timer_manager); + + ASSERT_TRUE(finished_context.is_called); + ASSERT_FALSE(nullptr == finished_context.error); +} + +TEST(wfp_jsonrpc_proxy, invoke_calls_finish_if_send_fails) +{ + struct wfp_timer_manager * timer_manager = wfp_timer_manager_create(); + + SendContext send_context(false); + void * send_data = reinterpret_cast(&send_context); + struct wfp_jsonrpc_proxy * proxy = wfp_jsonrpc_proxy_create(timer_manager, WFP_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + FinishedContext finished_context; + void * finished_data = reinterpret_cast(&finished_context); + wfp_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); + + ASSERT_TRUE(send_context.is_called); + ASSERT_TRUE(json_is_object(send_context.response)); + + ASSERT_TRUE(finished_context.is_called); + ASSERT_FALSE(nullptr == finished_context.error); + + wfp_jsonrpc_proxy_dispose(proxy); + wfp_timer_manager_dispose(timer_manager); +} + +TEST(wfp_jsonrpc_proxy, invoke_fails_if_another_request_is_pending) +{ + struct wfp_timer_manager * timer_manager = wfp_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wfp_jsonrpc_proxy * proxy = wfp_jsonrpc_proxy_create(timer_manager, WFP_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + FinishedContext finished_context; + void * finished_data = reinterpret_cast(&finished_context); + wfp_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); + + FinishedContext finished_context2; + void * finished_data2 = reinterpret_cast(&finished_context2); + wfp_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data2, "foo", ""); + + ASSERT_TRUE(send_context.is_called); + ASSERT_TRUE(json_is_object(send_context.response)); + + ASSERT_FALSE(finished_context.is_called); + + ASSERT_TRUE(finished_context2.is_called); + ASSERT_EQ(WFP_BAD_BUSY, jsonrpc_get_status(finished_context2.error)); + + wfp_jsonrpc_proxy_dispose(proxy); + wfp_timer_manager_dispose(timer_manager); +} + +TEST(wfp_jsonrpc_proxy, invoke_fails_if_request_is_invalid) +{ + struct wfp_timer_manager * timer_manager = wfp_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wfp_jsonrpc_proxy * proxy = wfp_jsonrpc_proxy_create(timer_manager, WFP_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + FinishedContext finished_context; + void * finished_data = reinterpret_cast(&finished_context); + wfp_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data, "foo", "?", "error"); + + ASSERT_FALSE(send_context.is_called); + + ASSERT_TRUE(finished_context.is_called); + ASSERT_EQ(WFP_BAD, jsonrpc_get_status(finished_context.error)); + + wfp_jsonrpc_proxy_dispose(proxy); + wfp_timer_manager_dispose(timer_manager); +} + +TEST(wfp_jsonrpc_proxy, on_result) +{ + struct wfp_timer_manager * timer_manager = wfp_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wfp_jsonrpc_proxy * proxy = wfp_jsonrpc_proxy_create(timer_manager, WFP_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + FinishedContext finished_context; + void * finished_data = reinterpret_cast(&finished_context); + wfp_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); + + ASSERT_TRUE(send_context.is_called); + ASSERT_TRUE(json_is_object(send_context.response)); + + json_t * id = json_object_get(send_context.response, "id"); + ASSERT_TRUE(json_is_number(id)); + + json_t * response = json_object(); + json_object_set_new(response, "result", json_string("okay")); + json_object_set(response, "id", id); + + wfp_jsonrpc_proxy_onresult(proxy, response); + json_decref(response); + + ASSERT_TRUE(finished_context.is_called); + ASSERT_EQ(nullptr, finished_context.error); + ASSERT_TRUE(json_is_string(finished_context.result)); + ASSERT_STREQ("okay", json_string_value(finished_context.result)); + + wfp_jsonrpc_proxy_dispose(proxy); + wfp_timer_manager_dispose(timer_manager); +} + +TEST(wfp_jsonrpc_proxy, on_result_reject_response_with_unknown_id) +{ + struct wfp_timer_manager * timer_manager = wfp_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wfp_jsonrpc_proxy * proxy = wfp_jsonrpc_proxy_create(timer_manager, WFP_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + FinishedContext finished_context; + void * finished_data = reinterpret_cast(&finished_context); + wfp_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); + + ASSERT_TRUE(send_context.is_called); + ASSERT_TRUE(json_is_object(send_context.response)); + + json_t * id = json_object_get(send_context.response, "id"); + ASSERT_TRUE(json_is_number(id)); + + json_t * response = json_object(); + json_object_set_new(response, "result", json_string("okay")); + json_object_set_new(response, "id", json_integer(1 + json_integer_value(id))); + + wfp_jsonrpc_proxy_onresult(proxy, response); + json_decref(response); + + ASSERT_FALSE(finished_context.is_called); + + wfp_jsonrpc_proxy_dispose(proxy); + wfp_timer_manager_dispose(timer_manager); +} + +TEST(wfp_jsonrpc_proxy, timeout) +{ + struct wfp_timer_manager * timer_manager = wfp_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wfp_jsonrpc_proxy * proxy = wfp_jsonrpc_proxy_create(timer_manager, 0, &jsonrpc_send, send_data); + + FinishedContext finished_context; + void * finished_data = reinterpret_cast(&finished_context); + wfp_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); + + ASSERT_TRUE(send_context.is_called); + ASSERT_TRUE(json_is_object(send_context.response)); + + std::this_thread::sleep_for(10ms); + wfp_timer_manager_check(timer_manager); + + ASSERT_TRUE(finished_context.is_called); + ASSERT_EQ(WFP_BAD_TIMEOUT, jsonrpc_get_status(finished_context.error)); + + wfp_jsonrpc_proxy_dispose(proxy); + wfp_timer_manager_dispose(timer_manager); +} + +TEST(wfp_jsonrpc_proxy, cleanup_pending_request) +{ + struct wfp_timer_manager * timer_manager = wfp_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wfp_jsonrpc_proxy * proxy = wfp_jsonrpc_proxy_create(timer_manager, 10, &jsonrpc_send, send_data); + + FinishedContext finished_context; + void * finished_data = reinterpret_cast(&finished_context); + wfp_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); + + ASSERT_TRUE(send_context.is_called); + ASSERT_TRUE(json_is_object(send_context.response)); + + ASSERT_FALSE(finished_context.is_called); + + wfp_jsonrpc_proxy_dispose(proxy); + + ASSERT_TRUE(finished_context.is_called); + + wfp_timer_manager_dispose(timer_manager); +} + + + +TEST(wfp_jsonrpc_proxy, notify) +{ + struct wfp_timer_manager * timer_manager = wfp_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wfp_jsonrpc_proxy * proxy = wfp_jsonrpc_proxy_create(timer_manager, WFP_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + wfp_jsonrpc_proxy_notify(proxy, "foo", "si", "bar", 42); + + ASSERT_TRUE(send_context.is_called); + ASSERT_TRUE(json_is_object(send_context.response)); + + json_t * method = json_object_get(send_context.response, "method"); + ASSERT_TRUE(json_is_string(method)); + ASSERT_STREQ("foo", json_string_value(method)); + + json_t * params = json_object_get(send_context.response, "params"); + ASSERT_TRUE(json_is_array(params)); + ASSERT_EQ(2, json_array_size(params)); + ASSERT_TRUE(json_is_string(json_array_get(params, 0))); + ASSERT_STREQ("bar", json_string_value(json_array_get(params, 0))); + ASSERT_TRUE(json_is_integer(json_array_get(params, 1))); + ASSERT_EQ(42, json_integer_value(json_array_get(params, 1))); + + json_t * id = json_object_get(send_context.response, "id"); + ASSERT_EQ(nullptr, id); + + wfp_jsonrpc_proxy_dispose(proxy); + wfp_timer_manager_dispose(timer_manager); +} + +TEST(wfp_jsonrpc_proxy, notify_dont_send_invalid_request) +{ + struct wfp_timer_manager * timer_manager = wfp_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wfp_jsonrpc_proxy * proxy = wfp_jsonrpc_proxy_create(timer_manager, WFP_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + wfp_jsonrpc_proxy_notify(proxy, "foo", "?"); + + ASSERT_FALSE(send_context.is_called); + + wfp_jsonrpc_proxy_dispose(proxy); + wfp_timer_manager_dispose(timer_manager); +} + +TEST(wfp_jsonrpc_proxy, swallow_timeout_if_no_request_pending) +{ + MockTimer timer_api; + + wfp_timer_on_timer_fn * on_timer = nullptr; + void * timer_context = nullptr; + EXPECT_CALL(timer_api, wfp_timer_create(_, _, _)) + .Times(1) + .WillOnce(DoAll(SaveArg<1>(&on_timer), SaveArg<2>(&timer_context), Return(nullptr))); + EXPECT_CALL(timer_api, wfp_timer_dispose(_)).Times(1); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wfp_jsonrpc_proxy * proxy = wfp_jsonrpc_proxy_create(nullptr, 1, &jsonrpc_send, send_data); + + on_timer(nullptr, timer_context); + ASSERT_FALSE(send_context.is_called); + + + wfp_jsonrpc_proxy_dispose(proxy); +} + +TEST(wfp_jsonrpc_proxy, on_result_swallow_if_no_request_pending) +{ + struct wfp_timer_manager * timer_manager = wfp_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wfp_jsonrpc_proxy * proxy = wfp_jsonrpc_proxy_create(timer_manager, WFP_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + json_t * response = json_object(); + json_object_set_new(response, "result", json_string("okay")); + json_object_set_new(response, "id", json_integer(42)); + + wfp_jsonrpc_proxy_onresult(proxy, response); + json_decref(response); + + wfp_jsonrpc_proxy_dispose(proxy); + wfp_timer_manager_dispose(timer_manager); +} + diff --git a/test/webfuse_provider/jsonrpc/test_request.cc b/test/webfuse_provider/jsonrpc/test_request.cc new file mode 100644 index 0000000..5e8a66d --- /dev/null +++ b/test/webfuse_provider/jsonrpc/test_request.cc @@ -0,0 +1,138 @@ +#include +#include "webfuse_provider/impl/jsonrpc/request.h" +#include "webfuse_provider/status.h" + +namespace +{ + +struct Context +{ + json_t * response; +}; + +bool jsonrpc_send( + json_t * request, + void * user_data) +{ + Context * context = reinterpret_cast(user_data); + context->response = request; + json_incref(request); + + return true; +} + +} + +TEST(wfp_jsonrpc_request, create_dispose) +{ + Context context{nullptr}; + void * user_data = reinterpret_cast(&context); + + struct wfp_jsonrpc_request * request = + wfp_jsonrpc_request_create(42, &jsonrpc_send, user_data); + + ASSERT_NE(nullptr, request); + ASSERT_EQ(user_data, wfp_jsonrpc_request_get_userdata(request)); + + wfp_jsonrpc_request_dispose(request); +} + +TEST(wfp_jsonrpc_request, respond) +{ + Context context{nullptr}; + void * user_data = reinterpret_cast(&context); + + struct wfp_jsonrpc_request * request = + wfp_jsonrpc_request_create(42, &jsonrpc_send, user_data); + + wfp_jsonrpc_respond(request, json_string("okay")); + + ASSERT_NE(nullptr, context.response); + + + json_t * response = reinterpret_cast(context.response); + ASSERT_TRUE(json_is_object(response)); + + json_t * id = json_object_get(response, "id"); + ASSERT_TRUE(json_is_integer(id)); + ASSERT_EQ(42, json_integer_value(id)); + + json_t * result = json_object_get(response, "result"); + ASSERT_TRUE(json_is_string(result)); + ASSERT_STREQ("okay", json_string_value(result)); + + ASSERT_EQ(nullptr, json_object_get(response, "error")); + + json_decref(response); +} + +TEST(wfp_jsonrpc_request, respond_error) +{ + Context context{nullptr}; + void * user_data = reinterpret_cast(&context); + + struct wfp_jsonrpc_request * request = + wfp_jsonrpc_request_create(42, &jsonrpc_send, user_data); + + wfp_jsonrpc_respond_error(request, WFP_BAD, "Bad"); + + ASSERT_NE(nullptr, context.response); + + + json_t * response = reinterpret_cast(context.response); + ASSERT_TRUE(json_is_object(response)); + + json_t * id = json_object_get(response, "id"); + ASSERT_TRUE(json_is_integer(id)); + ASSERT_EQ(42, json_integer_value(id)); + + ASSERT_EQ(nullptr, json_object_get(response, "result")); + + json_t * err = json_object_get(response, "error"); + ASSERT_TRUE(json_is_object(err)); + + json_t * err_code = json_object_get(err, "code"); + ASSERT_TRUE(json_is_integer(err_code)); + ASSERT_EQ(WFP_BAD, json_integer_value(err_code)); + + json_t * err_message = json_object_get(err, "message"); + ASSERT_TRUE(json_is_string(err_message)); + ASSERT_STREQ("Bad", json_string_value(err_message)); + + json_decref(response); +} + +TEST(wfp_jsonrpc_request, is_request_object_params) +{ + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("some_method")); + json_object_set_new(request, "params", json_object()); + json_object_set_new(request, "id", json_integer(42)); + + ASSERT_TRUE(wfp_jsonrpc_is_request(request)); + + json_decref(request); +} + +TEST(wfp_jsonrpc_request, is_request_fail_missing_params) +{ + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("some_method")); + json_object_set_new(request, "id", json_integer(42)); + + ASSERT_FALSE(wfp_jsonrpc_is_request(request)); + + json_decref(request); +} + +TEST(wfp_jsonrpc_request, is_request_fail_params_wrong_type) +{ + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("some_method")); + json_object_set_new(request, "params", json_string("invalid_params")); + json_object_set_new(request, "id", json_integer(42)); + + ASSERT_FALSE(wfp_jsonrpc_is_request(request)); + + json_decref(request); +} diff --git a/test/webfuse_provider/jsonrpc/test_response.cc b/test/webfuse_provider/jsonrpc/test_response.cc new file mode 100644 index 0000000..9ab7c31 --- /dev/null +++ b/test/webfuse_provider/jsonrpc/test_response.cc @@ -0,0 +1,147 @@ +#include +#include "webfuse_provider/impl/jsonrpc/response_intern.h" +#include "webfuse_provider/status.h" + +TEST(wfp_json_response, init_result) +{ + json_t * message = json_object(); + json_object_set_new(message, "result", json_integer(47)); + json_object_set_new(message, "id", json_integer(11)); + + struct wfp_jsonrpc_response response; + wfp_jsonrpc_response_init(&response, message); + + ASSERT_EQ(nullptr, response.error); + ASSERT_TRUE(json_is_integer(response.result)); + ASSERT_EQ(47, json_integer_value(response.result)); + ASSERT_EQ(11, response.id); + + wfp_jsonrpc_response_cleanup(&response); + json_decref(message); +} + +TEST(wfp_json_response, init_error) +{ + json_t * message = json_object(); + json_t * err = json_object(); + json_object_set_new(err, "code", json_integer(42)); + json_object_set_new(err, "message", json_string("Don't Panic!")); + json_object_set_new(message, "error", err); + json_object_set_new(message, "id", json_integer(23)); + + struct wfp_jsonrpc_response response; + wfp_jsonrpc_response_init(&response, message); + + ASSERT_EQ(42, json_integer_value(json_object_get(response.error, "code"))); + ASSERT_STREQ("Don't Panic!", json_string_value(json_object_get(response.error, "message"))); + ASSERT_EQ(nullptr, response.result); + ASSERT_EQ(23, response.id); + + wfp_jsonrpc_response_cleanup(&response); + json_decref(message); +} + +TEST(wfp_json_response, init_fail_missing_result_and_error) +{ + json_t * message = json_object(); + json_object_set_new(message, "id", json_integer(12)); + + struct wfp_jsonrpc_response response; + wfp_jsonrpc_response_init(&response, message); + + ASSERT_EQ(WFP_BAD_FORMAT, json_integer_value(json_object_get(response.error, "code"))); + ASSERT_EQ(nullptr, response.result); + ASSERT_EQ(12, response.id); + + wfp_jsonrpc_response_cleanup(&response); + json_decref(message); +} + +TEST(wfp_json_response, init_fail_missing_id) +{ + json_t * message = json_object(); + json_object_set_new(message, "result", json_integer(47)); + + struct wfp_jsonrpc_response response; + wfp_jsonrpc_response_init(&response, message); + + ASSERT_EQ(WFP_BAD_FORMAT, json_integer_value(json_object_get(response.error, "code"))); + ASSERT_EQ(nullptr, response.result); + ASSERT_EQ(-1, response.id); + + wfp_jsonrpc_response_cleanup(&response); + json_decref(message); +} + +TEST(wfp_json_response, init_fail_wrong_id_type) +{ + json_t * message = json_object(); + json_object_set_new(message, "result", json_integer(47)); + json_object_set_new(message, "id", json_string("42")); + + struct wfp_jsonrpc_response response; + wfp_jsonrpc_response_init(&response, message); + + ASSERT_EQ(WFP_BAD_FORMAT, json_integer_value(json_object_get(response.error, "code"))); + ASSERT_EQ(nullptr, response.result); + ASSERT_EQ(-1, response.id); + + wfp_jsonrpc_response_cleanup(&response); + json_decref(message); +} + +TEST(wfp_json_response, init_fail_error_missing_code) +{ + json_t * message = json_object(); + json_t * err = json_object(); + json_object_set_new(err, "message", json_string("Don't Panic!")); + json_object_set_new(message, "error", err); + json_object_set_new(message, "id", json_integer(23)); + + struct wfp_jsonrpc_response response; + wfp_jsonrpc_response_init(&response, message); + + ASSERT_EQ(WFP_BAD_FORMAT, json_integer_value(json_object_get(response.error, "code"))); + ASSERT_EQ(nullptr, response.result); + ASSERT_EQ(23, response.id); + + wfp_jsonrpc_response_cleanup(&response); + json_decref(message); +} + +TEST(wfp_json_response, init_fail_error_wrong_code_type) +{ + json_t * message = json_object(); + json_t * err = json_object(); + json_object_set_new(err, "code", json_string("42")); + json_object_set_new(err, "message", json_string("Don't Panic!")); + json_object_set_new(message, "error", err); + json_object_set_new(message, "id", json_integer(23)); + + struct wfp_jsonrpc_response response; + wfp_jsonrpc_response_init(&response, message); + + ASSERT_EQ(WFP_BAD_FORMAT, json_integer_value(json_object_get(response.error, "code"))); + ASSERT_EQ(nullptr, response.result); + ASSERT_EQ(23, response.id); + + wfp_jsonrpc_response_cleanup(&response); + json_decref(message); +} + +TEST(wfp_json_response, init_fail_error_wrong_type) +{ + json_t * message = json_object(); + json_object_set_new(message, "error", json_string("invalid error type")); + json_object_set_new(message, "id", json_integer(23)); + + struct wfp_jsonrpc_response response; + wfp_jsonrpc_response_init(&response, message); + + ASSERT_EQ(WFP_BAD_FORMAT, json_integer_value(json_object_get(response.error, "code"))); + ASSERT_EQ(nullptr, response.result); + ASSERT_EQ(23, response.id); + + wfp_jsonrpc_response_cleanup(&response); + json_decref(message); +} diff --git a/test/webfuse_provider/jsonrpc/test_response_parser.cc b/test/webfuse_provider/jsonrpc/test_response_parser.cc new file mode 100644 index 0000000..69a2e43 --- /dev/null +++ b/test/webfuse_provider/jsonrpc/test_response_parser.cc @@ -0,0 +1,58 @@ +#include +#include + +#include "webfuse_provider/impl/jsonrpc/response_intern.h" + + +static void response_parse_str( + std::string const & buffer, + struct wfp_jsonrpc_response * response) +{ + json_t * message = json_loadb(buffer.c_str(), buffer.size(), 0, nullptr); + if (nullptr != message) + { + wfp_jsonrpc_response_init(response, message); + json_decref(message); + } +} + +TEST(response_parser, test) +{ + struct wfp_jsonrpc_response response; + + // no object + response_parse_str("[]", &response); + ASSERT_NE(nullptr, response.error); + ASSERT_EQ(-1, response.id); + ASSERT_EQ(nullptr, response.result); + wfp_jsonrpc_response_cleanup(&response); + + // empty + response_parse_str("{}", &response); + ASSERT_NE(nullptr, response.error); + ASSERT_EQ(-1, response.id); + ASSERT_EQ(nullptr, response.result); + wfp_jsonrpc_response_cleanup(&response); + + // no data + response_parse_str("{\"id\":42}", &response); + ASSERT_NE(nullptr, response.error); + ASSERT_EQ(42, response.id); + ASSERT_EQ(nullptr, response.result); + wfp_jsonrpc_response_cleanup(&response); + + // custom error code + response_parse_str("{\"error\":{\"code\": 42}, \"id\": 42}", &response); + ASSERT_NE(nullptr, response.error); + ASSERT_EQ(42, json_integer_value(json_object_get(response.error, "code"))); + ASSERT_EQ(42, response.id); + ASSERT_EQ(nullptr, response.result); + wfp_jsonrpc_response_cleanup(&response); + + // valid response + response_parse_str("{\"result\": true, \"id\": 42}", &response); + ASSERT_EQ(nullptr, response.error); + ASSERT_EQ(42, response.id); + ASSERT_NE(nullptr, response.result); + wfp_jsonrpc_response_cleanup(&response); +} diff --git a/test/webfuse_provider/mocks/fake_invokation_context.cc b/test/webfuse_provider/mocks/fake_invokation_context.cc new file mode 100644 index 0000000..1d5343c --- /dev/null +++ b/test/webfuse_provider/mocks/fake_invokation_context.cc @@ -0,0 +1,17 @@ +#include "webfuse_provider/mocks/fake_invokation_context.hpp" + +namespace webfuse_test +{ + +wfp_impl_invokation_context create_context(MockProvider& provider, wfp_request * request) +{ + wfp_impl_invokation_context context = + { + provider.get_provider(), + provider.get_userdata(), + request + }; + return context; +} + +} \ No newline at end of file diff --git a/test/webfuse_provider/mocks/fake_invokation_context.hpp b/test/webfuse_provider/mocks/fake_invokation_context.hpp new file mode 100644 index 0000000..fcf0adc --- /dev/null +++ b/test/webfuse_provider/mocks/fake_invokation_context.hpp @@ -0,0 +1,16 @@ +#ifndef FAKE_INVOCATION_CONTEXT_HPP +#define FAKE_INVOCATION_CONTEXT_HPP + +#include "webfuse_provider/impl/provider.h" +#include "webfuse_provider/mocks/mock_provider.hpp" + +namespace webfuse_test +{ + +wfp_impl_invokation_context create_context( + MockProvider& provider, + wfp_request * request = nullptr); + +} + +#endif \ No newline at end of file diff --git a/test/webfuse_provider/mocks/mock_jsonrpc_proxy.cc b/test/webfuse_provider/mocks/mock_jsonrpc_proxy.cc new file mode 100644 index 0000000..3231727 --- /dev/null +++ b/test/webfuse_provider/mocks/mock_jsonrpc_proxy.cc @@ -0,0 +1,34 @@ +#include "webfuse_provider/mocks/mock_jsonrpc_proxy.hpp" +#include "webfuse_provider/test_util/wrap.hpp" + +extern "C" +{ +static webfuse_test::MockJsonRpcProxy * webfuse_test_MockJsonRpcProxy = nullptr; + +WFP_WRAP_VFUNC5(webfuse_test_MockJsonRpcProxy, void, wfp_jsonrpc_proxy_vinvoke, + struct wfp_jsonrpc_proxy *, + wfp_jsonrpc_proxy_finished_fn *, + void *, + char const *, + char const *); + +WFP_WRAP_VFUNC3(webfuse_test_MockJsonRpcProxy, void, wfp_jsonrpc_proxy_vnotify, + struct wfp_jsonrpc_proxy *, + char const *, + char const *); +} + +namespace webfuse_test +{ + +MockJsonRpcProxy::MockJsonRpcProxy() +{ + webfuse_test_MockJsonRpcProxy = this; +} + +MockJsonRpcProxy::~MockJsonRpcProxy() +{ + webfuse_test_MockJsonRpcProxy = nullptr; +} + +} \ No newline at end of file diff --git a/test/webfuse_provider/mocks/mock_jsonrpc_proxy.hpp b/test/webfuse_provider/mocks/mock_jsonrpc_proxy.hpp new file mode 100644 index 0000000..2018573 --- /dev/null +++ b/test/webfuse_provider/mocks/mock_jsonrpc_proxy.hpp @@ -0,0 +1,30 @@ +#ifndef MOCK_JSONRPC_PROXY_HPP +#define MOCK_JSONRPC_PROXY_HPP + +#include "webfuse_provider/impl/jsonrpc/proxy_intern.h" +#include + +namespace webfuse_test +{ + +class MockJsonRpcProxy +{ +public: + MockJsonRpcProxy(); + virtual ~MockJsonRpcProxy(); + MOCK_METHOD5(wfp_jsonrpc_proxy_vinvoke, void ( + struct wfp_jsonrpc_proxy * proxy, + wfp_jsonrpc_proxy_finished_fn * finished, + void * user_data, + char const * method_name, + char const * param_info)); + MOCK_METHOD3(wfp_jsonrpc_proxy_vnotify, void ( + struct wfp_jsonrpc_proxy * proxy, + char const * method_name, + char const * param_info)); + +}; + +} + +#endif diff --git a/test/webfuse_provider/mocks/mock_operation_context.cc b/test/webfuse_provider/mocks/mock_operation_context.cc new file mode 100644 index 0000000..ff3e5a7 --- /dev/null +++ b/test/webfuse_provider/mocks/mock_operation_context.cc @@ -0,0 +1,27 @@ +#include "webfuse_provider/mocks/mock_operation_context.hpp" +#include "webfuse/test_util/wrap.hpp" + +extern "C" +{ +static webfuse_test::MockOperationContext * webfuse_test_MockOperationContext = nullptr; + +WFP_WRAP_FUNC1(webfuse_test_MockOperationContext, + struct wfp_jsonrpc_proxy *, wfp_impl_operation_context_get_proxy, + struct wfp_impl_operation_context *); + +} + +namespace webfuse_test +{ + +MockOperationContext::MockOperationContext() +{ + webfuse_test_MockOperationContext = this; +} + +MockOperationContext::~MockOperationContext() +{ + webfuse_test_MockOperationContext = nullptr; +} + +} \ No newline at end of file diff --git a/test/webfuse_provider/mocks/mock_operation_context.hpp b/test/webfuse_provider/mocks/mock_operation_context.hpp new file mode 100644 index 0000000..3e5f60e --- /dev/null +++ b/test/webfuse_provider/mocks/mock_operation_context.hpp @@ -0,0 +1,22 @@ +#ifndef MOCK_OPERATIONS_CONTEXT_HPP +#define MOCK_OPERATIONS_CONTEXT_HPP + +#include "webfuse/adapter/impl/operation/context.h" +#include + +namespace webfuse_test +{ + +class MockOperationContext +{ +public: + MockOperationContext(); + virtual ~MockOperationContext(); + MOCK_METHOD1(wfp_impl_operation_context_get_proxy, wfp_jsonrpc_proxy * ( + struct wfp_impl_operation_context * context)); + +}; + +} + +#endif diff --git a/test/webfuse_provider/mocks/mock_provider.cc b/test/webfuse_provider/mocks/mock_provider.cc new file mode 100644 index 0000000..ccce595 --- /dev/null +++ b/test/webfuse_provider/mocks/mock_provider.cc @@ -0,0 +1,130 @@ +#include "webfuse_provider/mocks/mock_provider.hpp" + +extern "C" +{ +using webfuse_test::MockProvider; + +static void webfuse_test_MockProvider_connected( + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->connected(); +} + +static void webfuse_test_MockProvider_disconnected( + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->disconnected(); +} + +static void webfuse_test_MockProvider_lookup( + wfp_request * request, + ino_t parent, + char const * name, + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->lookup(request, parent, name); +} + +static void webfuse_test_MockProvider_getattr( + wfp_request * request, + ino_t inode, + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->getattr(request, inode); +} + +static void webfuse_test_MockProvider_readdir( + wfp_request * request, + ino_t directory, + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->readdir(request, directory); +} + +static void webfuse_test_MockProvider_open( + wfp_request * request, + ino_t inode, + int flags, + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->open(request, inode, flags); + +} + +static void webfuse_test_MockProvider_close( + ino_t inode, + uint32_t handle, + int flags, + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->close(inode, handle, flags); + +} + +static void webfuse_test_MockProvider_read( + wfp_request * request, + ino_t inode, + uint32_t handle, + size_t offset, + size_t length, + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->read(request, inode, handle, offset, length); +} + +static void webfuse_test_MockProvider_get_credentials( + wfp_credentials * credentials, + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->get_credentials(credentials); +} + + +static wfp_provider const webfuse_test_MockProvider = +{ + &webfuse_test_MockProvider_connected, + &webfuse_test_MockProvider_disconnected, + &webfuse_test_MockProvider_lookup, + &webfuse_test_MockProvider_getattr, + &webfuse_test_MockProvider_readdir, + &webfuse_test_MockProvider_open, + &webfuse_test_MockProvider_close, + &webfuse_test_MockProvider_read, + &webfuse_test_MockProvider_get_credentials, +}; + +} + +namespace webfuse_test +{ + +MockProvider::MockProvider() +{ + +} + +MockProvider::~MockProvider() +{ + +} + +wfp_provider const * MockProvider::get_provider() +{ + return &webfuse_test_MockProvider; +} + +void * MockProvider::get_userdata() +{ + return reinterpret_cast(this); +} + +} \ No newline at end of file diff --git a/test/webfuse_provider/mocks/mock_provider.hpp b/test/webfuse_provider/mocks/mock_provider.hpp new file mode 100644 index 0000000..7191847 --- /dev/null +++ b/test/webfuse_provider/mocks/mock_provider.hpp @@ -0,0 +1,31 @@ +#ifndef WFP_MOCK_PROVIDER_HPP +#define WFP_MOCK_PROVIDER_HPP + +#include "webfuse_provider/impl/provider.h" +#include + +namespace webfuse_test +{ + +class MockProvider +{ +public: + MockProvider(); + ~MockProvider(); + wfp_provider const * get_provider(); + void * get_userdata(); + MOCK_METHOD0(connected, void ()); + MOCK_METHOD0(disconnected, void ()); + MOCK_METHOD0(on_timer, void()); + MOCK_METHOD3(lookup, void(wfp_request * request, ino_t parent, char const * name)); + MOCK_METHOD2(getattr, void(wfp_request * request, ino_t inode)); + MOCK_METHOD2(readdir, void(wfp_request * request, ino_t directory)); + MOCK_METHOD3(open, void(wfp_request * request, ino_t inode, int flags)); + MOCK_METHOD3(close, void(ino_t inode, uint32_t handle, int flags)); + MOCK_METHOD5(read, void(wfp_request * request, ino_t inode, uint32_t handle, size_t offset, size_t length)); + MOCK_METHOD1(get_credentials, void(wfp_credentials * credentials)); +}; + +} + +#endif diff --git a/test/webfuse_provider/mocks/mock_provider_client.cc b/test/webfuse_provider/mocks/mock_provider_client.cc new file mode 100644 index 0000000..5c64303 --- /dev/null +++ b/test/webfuse_provider/mocks/mock_provider_client.cc @@ -0,0 +1,218 @@ +#include "webfuse_provider/mocks/mock_provider_client.hpp" +#include "webfuse_provider/operation/error.h" +#include "webfuse_provider/dirbuffer.h" + +extern "C" +{ +using webfuse_test::IProviderClient; +using webfuse_test::ProviderClientException; + +static void webfuse_test_iproviderclient_onconnected( + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + self->OnConnected(); +} + +static void webfuse_test_iproviderclient_ondisconnected( + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + self->OnDisconnected(); +} + +static void webfuse_test_iproviderclient_onlookup( + struct wfp_request * request, + ino_t parent, + char const * name, + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + + try + { + struct stat buffer; + memset(&buffer, 0, sizeof(buffer)); + self->Lookup(parent, name, &buffer); + wfp_respond_lookup(request, &buffer); + } + catch (ProviderClientException& ex) + { + wfp_respond_error(request, ex.GetErrorCode()); + } + catch (...) + { + wfp_respond_error(request, WFP_BAD); + } +} + +static void webfuse_test_iproviderclient_ongetattr( + struct wfp_request * request, + ino_t inode, + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + + try + { + struct stat buffer; + memset(&buffer, 0, sizeof(struct stat)); + self->GetAttr(inode, &buffer); + wfp_respond_getattr(request,&buffer); + } + catch (ProviderClientException& ex) + { + wfp_respond_error(request, ex.GetErrorCode()); + } + catch (...) + { + wfp_respond_error(request, WFP_BAD); + } + +} + +static void webfuse_test_iproviderclient_onreaddir( + struct wfp_request * request, + ino_t directory, + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + wfp_dirbuffer * buffer = wfp_dirbuffer_create(); + + try + { + self->ReadDir(directory, buffer); + wfp_respond_readdir(request, buffer); + } + catch (ProviderClientException& ex) + { + wfp_respond_error(request, ex.GetErrorCode()); + } + catch (...) + { + wfp_respond_error(request, WFP_BAD); + } + + wfp_dirbuffer_dispose(buffer); + +} + +static void webfuse_test_iproviderclient_onopen( + struct wfp_request * request, + ino_t inode, + int flags, + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + + try + { + uint32_t handle = 0; + self->Open(inode, flags, &handle); + wfp_respond_open(request, handle); + } + catch (ProviderClientException& ex) + { + wfp_respond_error(request, ex.GetErrorCode()); + } + catch (...) + { + wfp_respond_error(request, WFP_BAD); + } + +} + +static void webfuse_test_iproviderclient_onclose( + ino_t inode, + uint32_t handle, + int flags, + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + self->Close(inode, handle, flags); +} + +static void webfuse_test_iproviderclient_onread( + struct wfp_request * request, + ino_t inode, + uint32_t handle, + size_t offset, + size_t length, + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + char * data = new char[length]; + + try + { + size_t bytes_read = 0; + self->Read(inode, handle, offset, length, data, &bytes_read); + wfp_respond_read(request, data, bytes_read); + } + catch (ProviderClientException& ex) + { + wfp_respond_error(request, ex.GetErrorCode()); + } + catch (...) + { + wfp_respond_error(request, WFP_BAD); + } + + delete[] data; +} + +static void webfuse_test_iproviderclient_get_credentials( + wfp_credentials * credentials, + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + + try + { + self->GetCredentials(credentials); + } + catch (...) + { + // swallow + } +} + +} + +namespace webfuse_test +{ + +ProviderClientException::ProviderClientException(wfp_status error_code) +: runtime_error("ProviderClientException") +, error_code_(error_code) +{ + +} + +wfp_status ProviderClientException::GetErrorCode() +{ + return error_code_; +} + + +void IProviderClient::AttachTo(wfp_client_config * config, bool enableAuthentication) +{ + void * self = reinterpret_cast(this); + wfp_client_config_set_userdata(config, self); + wfp_client_config_set_onconnected(config, &webfuse_test_iproviderclient_onconnected); + wfp_client_config_set_ondisconnected(config, &webfuse_test_iproviderclient_ondisconnected); + + wfp_client_config_set_onlookup(config, &webfuse_test_iproviderclient_onlookup); + wfp_client_config_set_ongetattr(config, &webfuse_test_iproviderclient_ongetattr); + wfp_client_config_set_onreaddir(config, &webfuse_test_iproviderclient_onreaddir); + wfp_client_config_set_onopen(config, &webfuse_test_iproviderclient_onopen); + wfp_client_config_set_onclose(config, &webfuse_test_iproviderclient_onclose); + wfp_client_config_set_onread(config, &webfuse_test_iproviderclient_onread); + + if (enableAuthentication) + { + wfp_client_config_enable_authentication(config, &webfuse_test_iproviderclient_get_credentials); + } +} + +} \ No newline at end of file diff --git a/test/webfuse_provider/mocks/mock_provider_client.hpp b/test/webfuse_provider/mocks/mock_provider_client.hpp new file mode 100644 index 0000000..e4d83a9 --- /dev/null +++ b/test/webfuse_provider/mocks/mock_provider_client.hpp @@ -0,0 +1,53 @@ +#ifndef WFP_MOCK_PROVIDER_CLIENT_HPP +#define WFP_MOCK_PROVIDER_CLIENT_HPP + +#include +#include "webfuse_provider/client_config.h" +#include "webfuse_provider/status.h" +#include + +namespace webfuse_test +{ + class ProviderClientException: public std::runtime_error + { + public: + explicit ProviderClientException(wfp_status error_code); + wfp_status GetErrorCode(); + private: + wfp_status error_code_; + }; + + class IProviderClient + { + public: + virtual ~IProviderClient() = default; + virtual void OnConnected() = 0; + virtual void OnDisconnected() = 0; + virtual void Lookup(ino_t parent, char const * name, struct stat * result) = 0; + virtual void GetAttr(ino_t inode, struct stat * buffer) = 0; + virtual void ReadDir(ino_t directory, wfp_dirbuffer * buffer) = 0; + virtual void Open(ino_t inode, int flags, uint32_t * handle) = 0; + virtual void Close(ino_t inode, uint32_t handle, int flags) = 0; + virtual void Read(ino_t inode, uint32_t handle, size_t offset, size_t length, char * buffer, size_t * bytes_read) = 0; + virtual void GetCredentials(wfp_credentials * credentials) = 0; + + void AttachTo(wfp_client_config * config, bool enableAuthentication = false); + }; + + class MockProviderClient: public IProviderClient + { + public: + ~MockProviderClient() override = default; + MOCK_METHOD0( OnConnected, void()); + MOCK_METHOD0( OnDisconnected, void()); + MOCK_METHOD3( Lookup, void(ino_t parent, char const * name, struct stat * result)); + MOCK_METHOD2( GetAttr, void(ino_t inode, struct stat * buffer)); + MOCK_METHOD2( ReadDir, void(ino_t directory, wfp_dirbuffer * buffer)); + MOCK_METHOD3( Open, void(ino_t inode, int flags, uint32_t * handle)); + MOCK_METHOD3( Close, void(ino_t inode, uint32_t handle, int flags)); + MOCK_METHOD6( Read, void(ino_t inode, uint32_t handle, size_t offset, size_t length, char * buffer, size_t * bytes_read)); + MOCK_METHOD1( GetCredentials, void (wfp_credentials * credentials)); + }; +} + +#endif diff --git a/test/webfuse_provider/mocks/mock_request.cc b/test/webfuse_provider/mocks/mock_request.cc new file mode 100644 index 0000000..c1423d0 --- /dev/null +++ b/test/webfuse_provider/mocks/mock_request.cc @@ -0,0 +1,50 @@ +#include "webfuse_provider/mocks/mock_request.hpp" +#include + +extern "C" +{ + +static void webfuse_test_MockRequest_respond( + json_t * response, + void * user_data) +{ + auto * request = reinterpret_cast(user_data); + + json_t * result = json_object_get(response, "result"); + json_t * error = json_object_get(response, "error"); + json_t * id_holder = json_object_get(response, "id"); + + int id = -1; + if (json_is_integer(id_holder)) + { + id = json_integer_value(id_holder); + } + + if (nullptr != result) + { + request->respond(result, id); + } + else + { + request->respond_error(error, id); + } +} + +} + +namespace webfuse_test +{ + +struct wfp_request * MockRequest::create_request(int id) +{ + auto * request = reinterpret_cast(malloc(sizeof(wfp_request))); + request->respond = &webfuse_test_MockRequest_respond; + request->user_data = reinterpret_cast(this); + request->id = id; + + return request; +} + + + +} \ No newline at end of file diff --git a/test/webfuse_provider/mocks/mock_request.hpp b/test/webfuse_provider/mocks/mock_request.hpp new file mode 100644 index 0000000..eb25d3e --- /dev/null +++ b/test/webfuse_provider/mocks/mock_request.hpp @@ -0,0 +1,147 @@ +#ifndef WFP_MOCK_REQUEST_HPP +#define WFP_MOCK_REQUEST_HPP + +#include +#include +#include +#include "webfuse_provider/impl/request.h" + + +namespace webfuse_test +{ + +class MockRequest +{ +public: + struct wfp_request * create_request(int id); + MOCK_METHOD2(respond, void(json_t * result, int id)); + MOCK_METHOD2(respond_error, void(json_t * error, int id)); +}; + +MATCHER_P3(StatMatcher, inode, mode, file_type, "") +{ + json_t * inode_holder = json_object_get(arg, "inode"); + if ((!json_is_integer(inode_holder)) || (inode != json_integer_value(inode_holder))) + { + *result_listener << "missing inode"; + return false; + } + + json_t * mode_holder = json_object_get(arg, "mode"); + if ((!json_is_integer(mode_holder)) || (mode != json_integer_value(mode_holder))) + { + *result_listener << "missing mode"; + return false; + } + + json_t * type_holder = json_object_get(arg, "type"); + if ((!json_is_string(type_holder)) || (0 != strcmp(file_type, json_string_value(type_holder)))) + { + *result_listener << "missing type"; + return false; + } + + return true; +} + +MATCHER_P(OpenMatcher, handle, "") +{ + json_t * handle_holder = json_object_get(arg, "handle"); + if ((!json_is_integer(handle_holder)) || (handle != json_integer_value(handle_holder))) + { + *result_listener << "missing handle"; + return false; + } + + return true; +} + +MATCHER_P3(ReadResultMatcher, data, format, count, "") +{ + json_t * format_holder = json_object_get(arg, "format"); + if ((!json_is_string(format_holder)) || (0 != strcmp(format, json_string_value(format_holder)))) + { + *result_listener << "invalid or missing format: " << json_string_value(format_holder); + return false; + } + + json_t * count_holder = json_object_get(arg, "count"); + if ((!json_is_integer(count_holder)) || (count != json_integer_value(count_holder))) + { + *result_listener << "invalid or missing count: " << json_integer_value(count_holder); + return false; + } + + json_t * data_holder = json_object_get(arg, "data"); + if ((!json_is_string(data_holder)) || (0 != strcmp(data, json_string_value(data_holder)))) + { + *result_listener << "invalid or missing data: " << json_string_value(data_holder); + return false; + } + + return true; +} + +MATCHER_P(ReaddirMatcher, contained_elements , "") +{ + if (!json_is_array(arg)) + { + *result_listener << "result is not array"; + return false; + } + + { + size_t i; + json_t * value; + + json_array_foreach(arg, i, value) + { + json_t * inode = json_object_get(value, "inode"); + json_t * name = json_object_get(value, "name"); + + if(!json_is_integer(inode)) + { + *result_listener << "invalid result: missing inode"; + return false; + } + + if (!json_is_string(name)) + { + *result_listener << "invalid result: missing name"; + return false; + } + } + } + + for(size_t i = 0; NULL != contained_elements[i]; i++) + { + char const * element = contained_elements[i]; + bool found = false; + size_t j; + json_t * value; + + json_array_foreach(arg, j, value) + { + json_t * name = json_object_get(value, "name"); + + found = (0 == strcmp(element, json_string_value(name))); + if (found) + { + break; + } + } + + if (!found) + { + *result_listener << "missing required directory element: " << element; + return false; + } + } + + return true; +} + +} + + +#endif diff --git a/test/webfuse_provider/provider/operation/test_close.cc b/test/webfuse_provider/provider/operation/test_close.cc new file mode 100644 index 0000000..503bb80 --- /dev/null +++ b/test/webfuse_provider/provider/operation/test_close.cc @@ -0,0 +1,99 @@ +#include "webfuse_provider/impl/operation/close.h" +#include "webfuse_provider/mocks/mock_provider.hpp" +#include "webfuse_provider/mocks/fake_invokation_context.hpp" + +#include + +using ::webfuse_test::MockProvider; +using ::webfuse_test::create_context; +using ::testing::_; + +TEST(wfp_close, close) +{ + int inode = 42; + int handle = 0xdeadbeef; + int flags = 23; + + MockProvider provider; + EXPECT_CALL(provider, close(inode, handle, flags)).Times(1); + + wfp_impl_invokation_context context = create_context(provider); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(inode)); + json_array_append_new(params, json_integer(handle)); + json_array_append_new(params, json_integer(flags)); + + wfp_impl_close(&context, params, 42); + json_decref(params); +} + +TEST(wfp_close, close_fail_invalid_param_count) +{ + MockProvider provider; + EXPECT_CALL(provider, close(_,_,_)).Times(0); + + wfp_impl_invokation_context context = create_context(provider); + + json_t * params = json_array(); + wfp_impl_close(&context, params, 42); + json_decref(params); +} + +TEST(wfp_close, close_fail_inode_invalid_type) +{ + MockProvider provider; + EXPECT_CALL(provider, close(_,_,_)).Times(0); + + wfp_impl_invokation_context context = create_context(provider); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_string("42")); + json_array_append_new(params, json_integer(0)); + json_array_append_new(params, json_integer(0)); + + wfp_impl_close(&context, params, 42); + json_decref(params); +} + +TEST(wfp_close, close_fail_handle_invalid_type) +{ + MockProvider provider; + EXPECT_CALL(provider, close(_,_,_)).Times(0); + + wfp_impl_invokation_context context = create_context(provider); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(0)); + json_array_append_new(params, json_string("42")); + json_array_append_new(params, json_integer(0)); + + wfp_impl_close(&context, params, 42); + json_decref(params); +} + +TEST(wfp_close, close_fail_flags_invalid_type) +{ + MockProvider provider; + EXPECT_CALL(provider, close(_,_,_)).Times(0); + + wfp_impl_invokation_context context = create_context(provider); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(0)); + json_array_append_new(params, json_integer(0)); + json_array_append_new(params, json_string("42")); + + wfp_impl_close(&context, params, 42); + json_decref(params); +} + + +TEST(wfp_close, default_nop) +{ + wfp_impl_close_default(0, 0, 0, nullptr); +} \ No newline at end of file diff --git a/test/webfuse_provider/provider/operation/test_getattr.cc b/test/webfuse_provider/provider/operation/test_getattr.cc new file mode 100644 index 0000000..5346f33 --- /dev/null +++ b/test/webfuse_provider/provider/operation/test_getattr.cc @@ -0,0 +1,107 @@ +#include "webfuse_provider/impl/operation/getattr.h" +#include "webfuse_provider/mocks/mock_request.hpp" +#include "webfuse_provider/mocks/mock_provider.hpp" +#include "webfuse_provider/mocks/fake_invokation_context.hpp" + +#include +#include + +using ::webfuse_test::MockProvider; +using ::webfuse_test::MockRequest; +using ::webfuse_test::StatMatcher; +using ::webfuse_test::create_context; +using ::testing::_; +using ::testing::Invoke; + +namespace +{ + +void free_request(wfp_request * request, ino_t) +{ + free(request); +} + +} + +TEST(wfp_impl_getattr, default_responds_error) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond_error(_,42)).Times(1); + + wfp_impl_getattr_default(req, 0, nullptr); +} + +TEST(wfp_impl_getattr, respond_file) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond(StatMatcher(23, 0754, "file"), 42)).Times(1); + + struct stat buffer; + memset(&buffer, 0, sizeof(buffer)); + buffer.st_ino = 23; + buffer.st_mode = S_IFREG | 0754; + wfp_impl_respond_getattr(req, &buffer); +} + +TEST(wfp_impl_getattr, respond_dir) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond(StatMatcher(23, 0754, "dir"), 42)).Times(1); + + struct stat buffer; + memset(&buffer, 0, sizeof(buffer)); + buffer.st_ino = 23; + buffer.st_mode = S_IFDIR | 0754; + wfp_impl_respond_getattr(req, &buffer); +} + +TEST(wfp_impl_getattr, invoke_provider) +{ + ino_t inode = 23; + MockProvider provider; + EXPECT_CALL(provider,getattr(_, inode)).Times(1).WillOnce(Invoke(free_request)); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(inode)); + + wfp_impl_getattr(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_getattr, fail_invalid_param_count) +{ + MockProvider provider; + EXPECT_CALL(provider,getattr(_, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + + wfp_impl_getattr(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_getattr, fail_invalid_inode_type) +{ + MockProvider provider; + EXPECT_CALL(provider,getattr(_, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_string("42")); + + wfp_impl_getattr(&context, params, 42); + json_decref(params); +} diff --git a/test/webfuse_provider/provider/operation/test_lookup.cc b/test/webfuse_provider/provider/operation/test_lookup.cc new file mode 100644 index 0000000..b4db6f9 --- /dev/null +++ b/test/webfuse_provider/provider/operation/test_lookup.cc @@ -0,0 +1,129 @@ +#include "webfuse_provider/impl/operation/lookup.h" +#include "webfuse_provider/mocks/mock_request.hpp" +#include "webfuse_provider/mocks/mock_provider.hpp" +#include "webfuse_provider/mocks/fake_invokation_context.hpp" + +#include +#include + +using ::webfuse_test::MockProvider; +using ::webfuse_test::MockRequest; +using ::webfuse_test::StatMatcher; +using ::webfuse_test::create_context; +using ::testing::_; +using ::testing::Invoke; +using ::testing::StrEq; + +namespace +{ + +void free_request(wfp_request * request, ino_t, char const *) +{ + free(request); +} + +} + +TEST(wfp_impl_lookup, invoke_provider) +{ + ino_t inode = 42; + MockProvider provider; + EXPECT_CALL(provider,lookup(_, inode,StrEq("some.file"))).Times(1) + .WillOnce(Invoke(free_request)); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(inode)); + json_array_append_new(params, json_string("some.file")); + + wfp_impl_lookup(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_lookup, fail_invalid_param_count) +{ + MockProvider provider; + EXPECT_CALL(provider,lookup(_, _,_)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(23)); + + wfp_impl_lookup(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_lookup, fail_invalid_inode_type) +{ + MockProvider provider; + EXPECT_CALL(provider,lookup(_, _,_)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_string("23")); + json_array_append_new(params, json_string("some.file")); + + wfp_impl_lookup(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_lookup, fail_invalid_name_type) +{ + MockProvider provider; + EXPECT_CALL(provider,lookup(_, _,_)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(23)); + json_array_append_new(params, json_integer(1)); + + wfp_impl_lookup(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_lookup, default_responds_error) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond_error(_,42)).Times(1); + + wfp_impl_lookup_default(req, 1, "some.file", nullptr); +} + +TEST(wfp_impl_lookup, respond_file) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond(StatMatcher(23, 0754, "file"), 42)).Times(1); + + struct stat buffer; + memset(&buffer, 0, sizeof(buffer)); + buffer.st_ino = 23; + buffer.st_mode = S_IFREG | 0754; + wfp_impl_respond_lookup(req, &buffer); +} + +TEST(wfp_impl_lookup, respond_dir) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond(StatMatcher(23, 0754, "dir"), 42)).Times(1); + + struct stat buffer; + memset(&buffer, 0, sizeof(buffer)); + buffer.st_ino = 23; + buffer.st_mode = S_IFDIR | 0754; + wfp_impl_respond_lookup(req, &buffer); +} diff --git a/test/webfuse_provider/provider/operation/test_open.cc b/test/webfuse_provider/provider/operation/test_open.cc new file mode 100644 index 0000000..3689941 --- /dev/null +++ b/test/webfuse_provider/provider/operation/test_open.cc @@ -0,0 +1,114 @@ +#include "webfuse_provider/impl/operation/open.h" +#include "webfuse_provider/mocks/mock_request.hpp" +#include "webfuse_provider/mocks/mock_provider.hpp" +#include "webfuse_provider/mocks/fake_invokation_context.hpp" + +#include +#include + +using ::webfuse_test::MockProvider; +using ::webfuse_test::MockRequest; +using ::webfuse_test::OpenMatcher; +using ::webfuse_test::create_context; +using ::testing::_; +using ::testing::Invoke; +using ::testing::StrEq; + +namespace +{ + +void free_request(wfp_request * request, ino_t, int) +{ + free(request); +} + +} + +TEST(wfp_impl_open, invoke_provider) +{ + ino_t inode = 42; + int flags = 0; + MockProvider provider; + EXPECT_CALL(provider,open(_, inode, flags)).Times(1) + .WillOnce(Invoke(free_request)); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(inode)); + json_array_append_new(params, json_integer(flags)); + + wfp_impl_open(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_open, fail_invalid_param_count) +{ + MockProvider provider; + EXPECT_CALL(provider,open(_, _, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(23)); + + wfp_impl_open(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_open, fail_invalid_inode_type) +{ + MockProvider provider; + EXPECT_CALL(provider,open(_, _, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_string("")); + json_array_append_new(params, json_integer(0)); + + wfp_impl_open(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_open, fail_invalid_flags_type) +{ + MockProvider provider; + EXPECT_CALL(provider,open(_, _, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(23)); + json_array_append_new(params, json_string("")); + + wfp_impl_open(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_open, default_responds_error) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond_error(_,42)).Times(1); + + wfp_impl_open_default(req, 1, 0, nullptr); +} + +TEST(wfp_impl_open, respond) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond(OpenMatcher(23), 42)).Times(1); + + wfp_impl_respond_open(req, 23); +} + diff --git a/test/webfuse_provider/provider/operation/test_read.cc b/test/webfuse_provider/provider/operation/test_read.cc new file mode 100644 index 0000000..5b818ff --- /dev/null +++ b/test/webfuse_provider/provider/operation/test_read.cc @@ -0,0 +1,173 @@ +#include "webfuse_provider/impl/operation/read.h" +#include "webfuse_provider/mocks/mock_request.hpp" +#include "webfuse_provider/mocks/mock_provider.hpp" +#include "webfuse_provider/mocks/fake_invokation_context.hpp" + +#include +#include + +using ::webfuse_test::MockProvider; +using ::webfuse_test::MockRequest; +using ::webfuse_test::ReadResultMatcher; +using ::webfuse_test::create_context; +using ::testing::_; +using ::testing::Invoke; +using ::testing::StrEq; + +namespace +{ + +void free_request(wfp_request * request, ino_t, uint32_t, size_t ,size_t) +{ + free(request); +} + +} + +TEST(wfp_impl_read, invoke_provider) +{ + ino_t inode = 42; + uint32_t handle = 5; + size_t offset = 2; + size_t length = 1; + MockProvider provider; + EXPECT_CALL(provider, read(_, inode, handle, offset, length)).Times(1) + .WillOnce(Invoke(free_request)); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(inode)); + json_array_append_new(params, json_integer(handle)); + json_array_append_new(params, json_integer(offset)); + json_array_append_new(params, json_integer(length)); + + wfp_impl_read(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_read, fail_invalid_param_count) +{ + MockProvider provider; + EXPECT_CALL(provider, read(_, _, _, _, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(1)); + json_array_append_new(params, json_integer(2)); + json_array_append_new(params, json_integer(3)); + json_array_append_new(params, json_integer(4)); + json_array_append_new(params, json_integer(5)); + + wfp_impl_read(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_read, fail_invalid_inode_type) +{ + MockProvider provider; + EXPECT_CALL(provider, read(_, _, _, _, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_string("42")); + json_array_append_new(params, json_integer(2)); + json_array_append_new(params, json_integer(3)); + json_array_append_new(params, json_integer(4)); + + wfp_impl_read(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_read, fail_invalid_handle_type) +{ + MockProvider provider; + EXPECT_CALL(provider, read(_, _, _, _, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(1)); + json_array_append_new(params, json_string("42")); + json_array_append_new(params, json_integer(3)); + json_array_append_new(params, json_integer(4)); + + wfp_impl_read(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_read, fail_invalid_offset_type) +{ + MockProvider provider; + EXPECT_CALL(provider, read(_, _, _, _, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(1)); + json_array_append_new(params, json_integer(2)); + json_array_append_new(params, json_string("42")); + json_array_append_new(params, json_integer(4)); + + wfp_impl_read(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_read, fail_invalid_length_type) +{ + MockProvider provider; + EXPECT_CALL(provider, read(_, _, _, _, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(1)); + json_array_append_new(params, json_integer(2)); + json_array_append_new(params, json_integer(3)); + json_array_append_new(params, json_string("42")); + + wfp_impl_read(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_read, default_responds_error) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond_error(_,42)).Times(1); + + wfp_impl_read_default(req, 0, 0, 1, 2, nullptr); +} + +TEST(wfp_impl_read, respond) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond(ReadResultMatcher("d2Y=", "base64", 2), 42)).Times(1); + + char const data[] = "wf"; + wfp_impl_respond_read(req, data, 2); +} + +TEST(wfp_impl_read, respond_empty) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond(ReadResultMatcher("", "identity", 0), 42)).Times(1); + + wfp_impl_respond_read(req, nullptr, 0); +} diff --git a/test/webfuse_provider/provider/operation/test_readdir.cc b/test/webfuse_provider/provider/operation/test_readdir.cc new file mode 100644 index 0000000..6a14f8d --- /dev/null +++ b/test/webfuse_provider/provider/operation/test_readdir.cc @@ -0,0 +1,100 @@ +#include "webfuse_provider/impl/operation/readdir.h" +#include "webfuse_provider/mocks/mock_request.hpp" +#include "webfuse_provider/mocks/mock_provider.hpp" +#include "webfuse_provider/mocks/fake_invokation_context.hpp" +#include "webfuse_provider/dirbuffer.h" + +#include +#include + +using ::webfuse_test::MockProvider; +using ::webfuse_test::MockRequest; +using ::webfuse_test::ReaddirMatcher; +using ::webfuse_test::create_context; +using ::testing::_; +using ::testing::Invoke; + +namespace +{ + +void free_request(wfp_request * request, ino_t) +{ + free(request); +} + +} + +TEST(wfp_impl_readdir, invoke_provider) +{ + ino_t inode = 23; + MockProvider provider; + EXPECT_CALL(provider,readdir(_, inode)).Times(1).WillOnce(Invoke(free_request)); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(inode)); + + wfp_impl_readdir(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_readdir, fail_invalid_param_count) +{ + MockProvider provider; + EXPECT_CALL(provider,readdir(_, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(1)); + json_array_append_new(params, json_integer(1)); + + wfp_impl_readdir(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_readdir, fail_invalid_inode_type) +{ + MockProvider provider; + EXPECT_CALL(provider,readdir(_, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_string("1")); + + wfp_impl_readdir(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_readdir, default_responds_error) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond_error(_,42)).Times(1); + + wfp_impl_readdir_default(req, 0, nullptr); +} + +TEST(wfp_impl_readdir, respond) +{ + char const item0[] = "some.file"; + char const * items[] = { item0, nullptr }; + + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond(ReaddirMatcher(items), 42)).Times(1); + + wfp_dirbuffer * buffer = wfp_dirbuffer_create(); + wfp_dirbuffer_add(buffer, item0, 42); + wfp_impl_respond_readdir(req, buffer); + wfp_dirbuffer_dispose(buffer); + +} diff --git a/test/webfuse_provider/provider/test_client.cc b/test/webfuse_provider/provider/test_client.cc new file mode 100644 index 0000000..32843c9 --- /dev/null +++ b/test/webfuse_provider/provider/test_client.cc @@ -0,0 +1,346 @@ +#include "webfuse_provider/webfuse_provider.h" +#include "webfuse_provider/test_util/webfuse_server.hpp" +#include "webfuse_provider/mocks/mock_provider_client.hpp" +#include "webfuse_provider/test_util/client.hpp" + +#include +#include +#include + +using webfuse_test::WebfuseServer; +using webfuse_test::MockProviderClient; +using webfuse_test::Client; +using testing::Invoke; +using testing::_; +using testing::StrEq; + +#define TIMEOUT (std::chrono::seconds(10)) + +TEST(Client, Connect) +{ + MockProviderClient provider; + + std::promise connected; + EXPECT_CALL(provider, OnConnected()).Times(1) + .WillOnce(Invoke([&]() { connected.set_value(); })); + + std::promise disconnected; + EXPECT_CALL(provider, OnDisconnected()).Times(1) + .WillOnce(Invoke([&]() { disconnected.set_value(); })); + + wfp_client_config * config = wfp_client_config_create(); + provider.AttachTo(config); + + { + WebfuseServer server; + Client client(config, server.GetUrl()); + + ASSERT_EQ(std::future_status::ready, connected.get_future().wait_for(TIMEOUT)); + + client.Disconnect(); + ASSERT_EQ(std::future_status::ready, disconnected.get_future().wait_for(TIMEOUT)); + } + + wfp_client_config_dispose(config); +} + +TEST(Client, ConnectWithTls) +{ + MockProviderClient provider; + + std::promise connected; + EXPECT_CALL(provider, OnConnected()).Times(1) + .WillOnce(Invoke([&]() { connected.set_value(); })); + + std::promise disconnected; + EXPECT_CALL(provider, OnDisconnected()).Times(1) + .WillOnce(Invoke([&]() { disconnected.set_value(); })); + + wfp_client_config * config = wfp_client_config_create(); + wfp_client_config_set_certpath(config, "client-cert.pem"); + wfp_client_config_set_keypath(config, "client-key.pem"); + wfp_client_config_set_ca_filepath(config, "server-cert.pem"); + provider.AttachTo(config); + + { + WebfuseServer server(true); + Client client(config, server.GetUrl()); + + ASSERT_EQ(std::future_status::ready, connected.get_future().wait_for(TIMEOUT)); + + client.Disconnect(); + ASSERT_EQ(std::future_status::ready, disconnected.get_future().wait_for(TIMEOUT)); + } + + wfp_client_config_dispose(config); +} + +TEST(Client, ConnectFailWithInvalidUrl) +{ + MockProviderClient provider; + + EXPECT_CALL(provider, OnConnected()).Times(0); + + std::promise disconnected; + EXPECT_CALL(provider, OnDisconnected()).Times(1) + .WillOnce(Invoke([&]() { disconnected.set_value(); })); + + wfp_client_config * config = wfp_client_config_create(); + provider.AttachTo(config); + + { + Client client(config, "invalid_url"); + + ASSERT_EQ(std::future_status::ready, disconnected.get_future().wait_for(TIMEOUT)); + } + + wfp_client_config_dispose(config); +} + +TEST(Client, ConnectFailNotReachable) +{ + MockProviderClient provider; + + EXPECT_CALL(provider, OnConnected()).Times(0); + + std::promise disconnected; + EXPECT_CALL(provider, OnDisconnected()).Times(1) + .WillOnce(Invoke([&]() { disconnected.set_value(); })); + + wfp_client_config * config = wfp_client_config_create(); + provider.AttachTo(config); + + { + Client client(config, "ws://localhost:4/"); + + ASSERT_EQ(std::future_status::ready, disconnected.get_future().wait_for(TIMEOUT)); + } + + wfp_client_config_dispose(config); +} + +TEST(Client, Lookup) +{ + MockProviderClient provider; + + std::promise connected; + EXPECT_CALL(provider, OnConnected()).Times(1) + .WillOnce(Invoke([&]() { connected.set_value(); })); + + std::promise disconnected; + EXPECT_CALL(provider, OnDisconnected()).Times(1) + .WillOnce(Invoke([&]() { disconnected.set_value(); })); + + EXPECT_CALL(provider, Lookup(1,StrEq("foo"),_)).Times(1) + .WillOnce(Invoke([](ino_t, char const *, struct stat * result) { + result->st_ino = 42; + result->st_mode = S_IFREG | 0644; + })); + + wfp_client_config * config = wfp_client_config_create(); + provider.AttachTo(config); + + { + WebfuseServer server; + Client client(config, server.GetUrl()); + + ASSERT_EQ(std::future_status::ready, connected.get_future().wait_for(TIMEOUT)); + + json_t * response = server.Lookup(1, "foo"); + ASSERT_TRUE(json_is_object(response)); + json_t * result = json_object_get(response, "result"); + + json_t * inode = json_object_get(result, "inode"); + ASSERT_EQ(42, json_integer_value(inode)); + + json_t * mode = json_object_get(result, "mode"); + ASSERT_EQ(0644, json_integer_value(mode)); + + json_t * type = json_object_get(result, "type"); + ASSERT_STREQ("file", json_string_value(type)); + + json_decref(response); + + client.Disconnect(); + ASSERT_EQ(std::future_status::ready, disconnected.get_future().wait_for(TIMEOUT)); + } + + wfp_client_config_dispose(config); +} + +TEST(Client, LookupFail) +{ + MockProviderClient provider; + + std::promise connected; + EXPECT_CALL(provider, OnConnected()).Times(1) + .WillOnce(Invoke([&]() { connected.set_value(); })); + + std::promise disconnected; + EXPECT_CALL(provider, OnDisconnected()).Times(1) + .WillOnce(Invoke([&]() { disconnected.set_value(); })); + + EXPECT_CALL(provider, Lookup(1,StrEq("foo"),_)).Times(1) + .WillOnce(Invoke([](ino_t, char const *, struct stat * result) { + throw std::runtime_error("something went wrong"); + })); + + wfp_client_config * config = wfp_client_config_create(); + provider.AttachTo(config); + + { + WebfuseServer server; + Client client(config, server.GetUrl()); + + ASSERT_EQ(std::future_status::ready, connected.get_future().wait_for(TIMEOUT)); + + json_t * response = server.Lookup(1, "foo"); + ASSERT_TRUE(json_is_object(response)); + json_t * error = json_object_get(response, "error"); + + json_t * code = json_object_get(error, "code"); + ASSERT_NE(0, json_integer_value(code)); + + json_decref(response); + + client.Disconnect(); + ASSERT_EQ(std::future_status::ready, disconnected.get_future().wait_for(TIMEOUT)); + } + + wfp_client_config_dispose(config); +} + +TEST(Client, Open) +{ + MockProviderClient provider; + + std::promise connected; + EXPECT_CALL(provider, OnConnected()).Times(1) + .WillOnce(Invoke([&]() { connected.set_value(); })); + + std::promise disconnected; + EXPECT_CALL(provider, OnDisconnected()).Times(1) + .WillOnce(Invoke([&]() { disconnected.set_value(); })); + + EXPECT_CALL(provider, Open(1, 0, _)).Times(1) + .WillOnce(Invoke([](int, int, uint32_t * handle) { + *handle = 4711; + })); + + wfp_client_config * config = wfp_client_config_create(); + provider.AttachTo(config); + + { + WebfuseServer server; + Client client(config, server.GetUrl()); + + ASSERT_EQ(std::future_status::ready, connected.get_future().wait_for(TIMEOUT)); + + json_t * response = server.Open(1, 0); + ASSERT_TRUE(json_is_object(response)); + json_t * result = json_object_get(response, "result"); + + json_t * handle = json_object_get(result, "handle"); + ASSERT_EQ(4711, json_integer_value(handle)); + + json_decref(response); + + client.Disconnect(); + ASSERT_EQ(std::future_status::ready, disconnected.get_future().wait_for(TIMEOUT)); + } + + wfp_client_config_dispose(config); +} + +TEST(Client, Read) +{ + MockProviderClient provider; + + std::promise connected; + EXPECT_CALL(provider, OnConnected()).Times(1) + .WillOnce(Invoke([&]() { connected.set_value(); })); + + std::promise disconnected; + EXPECT_CALL(provider, OnDisconnected()).Times(1) + .WillOnce(Invoke([&]() { disconnected.set_value(); })); + + EXPECT_CALL(provider, Read(42, 5, 0, 1, _, _)).Times(1) + .WillOnce(Invoke([](ino_t, uint32_t, size_t, size_t , char * buffer, size_t * bytes_read) { + buffer[0] = 42; + *bytes_read = 1; + })); + + wfp_client_config * config = wfp_client_config_create(); + provider.AttachTo(config); + + { + WebfuseServer server; + Client client(config, server.GetUrl()); + + ASSERT_EQ(std::future_status::ready, connected.get_future().wait_for(TIMEOUT)); + + json_t * response = server.Read(42, 5, 0, 1); + ASSERT_TRUE(json_is_object(response)); + json_t * result = json_object_get(response, "result"); + + json_t * format = json_object_get(result, "format"); + ASSERT_STREQ("base64", json_string_value(format)); + + json_t * count = json_object_get(result, "count"); + ASSERT_EQ(1, json_integer_value(count)); + + json_t * data = json_object_get(result, "data"); + ASSERT_STREQ("Kg==", json_string_value(data)); + + json_decref(response); + + client.Disconnect(); + ASSERT_EQ(std::future_status::ready, disconnected.get_future().wait_for(TIMEOUT)); + } + + wfp_client_config_dispose(config); +} + +TEST(Client, ReadDir) +{ + MockProviderClient provider; + + std::promise connected; + EXPECT_CALL(provider, OnConnected()).Times(1) + .WillOnce(Invoke([&]() { connected.set_value(); })); + + std::promise disconnected; + EXPECT_CALL(provider, OnDisconnected()).Times(1) + .WillOnce(Invoke([&]() { disconnected.set_value(); })); + + EXPECT_CALL(provider, ReadDir(42, _)).Times(1) + .WillOnce(Invoke([](ino_t, wfp_dirbuffer * buffer) { + wfp_dirbuffer_add(buffer, ".", 42); + wfp_dirbuffer_add(buffer, "..", 1); + wfp_dirbuffer_add(buffer, "foo.txt", 43); + })); + + wfp_client_config * config = wfp_client_config_create(); + provider.AttachTo(config); + + { + WebfuseServer server; + Client client(config, server.GetUrl()); + + ASSERT_EQ(std::future_status::ready, connected.get_future().wait_for(TIMEOUT)); + + json_t * response = server.ReadDir(42); + ASSERT_TRUE(json_is_object(response)); + json_t * result = json_object_get(response, "result"); + + ASSERT_TRUE(json_is_array(result)); + ASSERT_EQ(3, json_array_size(result)); + + json_decref(response); + + client.Disconnect(); + ASSERT_EQ(std::future_status::ready, disconnected.get_future().wait_for(TIMEOUT)); + } + + wfp_client_config_dispose(config); +} diff --git a/test/webfuse_provider/provider/test_client_protocol.cc b/test/webfuse_provider/provider/test_client_protocol.cc new file mode 100644 index 0000000..318b832 --- /dev/null +++ b/test/webfuse_provider/provider/test_client_protocol.cc @@ -0,0 +1,268 @@ +#include +#include + +#include +#include +#include "webfuse_provider/test_util/ws_server.h" +#include "webfuse_provider/mocks/mock_provider_client.hpp" +#include "webfuse_provider/protocol_names.h" +#include "webfuse_provider/test_util/timeout_watcher.hpp" + +#include + +#include +#include +#include + +using webfuse_test::WsServer; +using webfuse_test::MockProviderClient; +using webfuse_test::IProviderClient; +using webfuse_test::TimeoutWatcher; +using testing::_; +using testing::AtMost; +using testing::Invoke; + +#define DEFAULT_TIMEOUT (std::chrono::milliseconds(5 * 1000)) + +namespace +{ + +class ClientProtocolFixture +{ + ClientProtocolFixture(ClientProtocolFixture const &) = delete; + ClientProtocolFixture& operator=(ClientProtocolFixture const &) = delete; +public: + explicit ClientProtocolFixture(IProviderClient& client, bool enableAuthentication = false) + { + server = new WsServer(WFP_PROTOCOL_NAME_ADAPTER_SERVER); + + config = wfp_client_config_create(); + client.AttachTo(config, enableAuthentication); + protocol = wfp_client_protocol_create(config); + + memset(protocols, 0, sizeof(struct lws_protocols) * 2); + wfp_client_protocol_init_lws(protocol, protocols); + + memset(&info, 0, sizeof(struct lws_context_creation_info)); + info.port = CONTEXT_PORT_NO_LISTEN; + info.protocols = protocols; + info.uid = -1; + info.gid = -1; + + context = lws_create_context(&info); + } + + ~ClientProtocolFixture() + { + lws_context_destroy(context); + wfp_client_protocol_dispose(protocol); + wfp_client_config_dispose(config); + delete server; + } + + void Connect() + { + TimeoutWatcher watcher(DEFAULT_TIMEOUT); + + wfp_client_protocol_connect(protocol, context, server->GetUrl().c_str()); + while (!server->IsConnected()) + { + watcher.check(); + lws_service(context, 0); + } + } + + void Disconnect() + { + wfp_client_protocol_disconnect(protocol); + } + + void SendToClient(json_t * request) + { + server->SendMessage(request); + } + + json_t * ReceiveMessageFromClient() + { + TimeoutWatcher watcher(DEFAULT_TIMEOUT); + json_t * result = server->ReceiveMessage(); + while (nullptr == result) + { + watcher.check(); + lws_service(context, 0); + result = server->ReceiveMessage(); + } + + return result; + } + + void AwaitAuthentication( + std::string const & expected_username, + std::string const & expected_password) + { + json_t * request = ReceiveMessageFromClient(); + ASSERT_TRUE(json_is_object(request)); + + json_t * method = json_object_get(request, "method"); + ASSERT_TRUE(json_is_string(method)); + ASSERT_STREQ("authenticate", json_string_value(method)); + + json_t * id = json_object_get(request, "id"); + ASSERT_TRUE(json_is_integer(id)); + + json_t * params = json_object_get(request, "params"); + ASSERT_TRUE(json_is_array(params)); + ASSERT_EQ(2, json_array_size(params)); + + json_t * type = json_array_get(params, 0); + ASSERT_TRUE(json_is_string(type)); + ASSERT_STREQ("username", json_string_value(type)); + + json_t * credentials = json_array_get(params, 1); + ASSERT_TRUE(json_is_object(credentials)); + + json_t * username = json_object_get(credentials, "username"); + ASSERT_TRUE(json_is_string(username)); + ASSERT_STREQ(expected_username.c_str(), json_string_value(username)); + + json_t * password = json_object_get(credentials, "password"); + ASSERT_TRUE(json_is_string(password)); + ASSERT_STREQ(expected_password.c_str(), json_string_value(password)); + + json_t * response = json_object(); + json_object_set_new(response, "result", json_object()); + json_object_set(response, "id", id); + SendToClient(response); + + json_decref(request); + } + + void AwaitAddFilesystem(std::string& filesystemName) + { + json_t * addFilesystemRequest = ReceiveMessageFromClient(); + ASSERT_NE(nullptr, addFilesystemRequest); + ASSERT_TRUE(json_is_object(addFilesystemRequest)); + + json_t * method = json_object_get(addFilesystemRequest, "method"); + ASSERT_TRUE(json_is_string(method)); + ASSERT_STREQ("add_filesystem", json_string_value(method)); + + json_t * params = json_object_get(addFilesystemRequest, "params"); + ASSERT_TRUE(json_is_array(params)); + ASSERT_EQ(1, json_array_size(params)); + + json_t * filesystem = json_array_get(params, 0); + ASSERT_TRUE(json_is_string(filesystem)); + filesystemName = json_string_value(filesystem); + + json_t * id = json_object_get(addFilesystemRequest, "id"); + ASSERT_TRUE(json_is_integer(id)); + + json_t * response = json_object(); + json_t * result = json_object(); + json_object_set(result, "id", filesystem); + json_object_set_new(response, "result", result); + json_object_set(response, "id", id); + + SendToClient(response); + + json_decref(addFilesystemRequest); + } + +private: + WsServer * server; + wfp_client_config * config; + wfp_client_protocol * protocol; + struct lws_context_creation_info info; + struct lws_protocols protocols[2]; + struct lws_context * context; + +}; + +void GetCredentials(wfp_credentials * credentials) +{ + wfp_credentials_set_type(credentials, "username"); + wfp_credentials_add(credentials, "username", "bob"); + wfp_credentials_add(credentials, "password", "secret"); +} + +} + +TEST(client_protocol, connect) +{ + MockProviderClient provider; + ClientProtocolFixture fixture(provider); + + EXPECT_CALL(provider, OnConnected()).Times(AtMost(1)); + EXPECT_CALL(provider, OnDisconnected()).Times(1); + + fixture.Connect(); + if (HasFatalFailure()) { return; } + + std::string filesystem; + fixture.AwaitAddFilesystem(filesystem); + if (HasFatalFailure()) { return; } +} + +TEST(client_protocol, disconnect_without_connect) +{ + MockProviderClient provider; + ClientProtocolFixture fixture(provider); + + EXPECT_CALL(provider, OnDisconnected()).Times(1); + + fixture.Disconnect(); +} + +TEST(client_protocol, connect_with_username_authentication) +{ + MockProviderClient provider; + ClientProtocolFixture fixture(provider, true); + + EXPECT_CALL(provider, OnConnected()).Times(AtMost(1)); + EXPECT_CALL(provider, OnDisconnected()).Times(1); + EXPECT_CALL(provider, GetCredentials(_)).Times(1).WillOnce(Invoke(GetCredentials)); + + fixture.Connect(); + if (HasFatalFailure()) { return; } + + fixture.AwaitAuthentication("bob", "secret"); + if (HasFatalFailure()) { return; } + + std::string filesystem; + fixture.AwaitAddFilesystem(filesystem); + if (HasFatalFailure()) { return; } +} + +TEST(client_protocol, getattr) +{ + MockProviderClient provider; + ClientProtocolFixture fixture(provider); + + EXPECT_CALL(provider, OnConnected()).Times(1); + EXPECT_CALL(provider, OnDisconnected()).Times(1); + EXPECT_CALL(provider, GetAttr(1, _)).Times(1); + + fixture.Connect(); + if (HasFatalFailure()) { return; } + + std::string filesystem; + fixture.AwaitAddFilesystem(filesystem); + if (HasFatalFailure()) { return; } + + json_t * params = json_array(); + json_array_append_new(params, json_string(filesystem.c_str())); + json_array_append_new(params, json_integer(1)); + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("getattr")); + json_object_set_new(request, "params", params); + json_object_set_new(request, "id", json_integer(42)); + + fixture.SendToClient(request); + json_t * response = fixture.ReceiveMessageFromClient(); + ASSERT_TRUE(json_is_object(response)); + + json_decref(response); + + fixture.Disconnect(); +} \ No newline at end of file diff --git a/test/webfuse_provider/provider/test_dirbuffer.cc b/test/webfuse_provider/provider/test_dirbuffer.cc new file mode 100644 index 0000000..639b0ef --- /dev/null +++ b/test/webfuse_provider/provider/test_dirbuffer.cc @@ -0,0 +1,32 @@ +#include "webfuse_provider/impl/dirbuffer.h" +#include + +TEST(DirBuffer, CreateDispose) +{ + wfp_dirbuffer * buffer = wfp_impl_dirbuffer_create(); + wfp_impl_dirbuffer_dispose(buffer); +} + +TEST(DirBuffer, Add) +{ + wfp_dirbuffer * buffer = wfp_impl_dirbuffer_create(); + wfp_impl_dirbuffer_add(buffer, "answer", 42); + + ASSERT_EQ(1, json_array_size(buffer->entries)); + + json_t * entry = json_array_get(buffer->entries, 0); + ASSERT_STREQ("answer", json_string_value(json_object_get(entry, "name"))); + ASSERT_EQ(42, json_integer_value(json_object_get(entry, "inode"))); + + wfp_impl_dirbuffer_dispose(buffer); +} + +TEST(DirBuffer, Take) +{ + wfp_dirbuffer * buffer = wfp_impl_dirbuffer_create(); + json_t * entries = wfp_impl_dirbuffer_take(buffer); + wfp_impl_dirbuffer_dispose(buffer); + + ASSERT_TRUE(json_is_array(entries)); + json_decref(entries); +} diff --git a/test/webfuse_provider/test_util/client.cc b/test/webfuse_provider/test_util/client.cc new file mode 100644 index 0000000..a213f88 --- /dev/null +++ b/test/webfuse_provider/test_util/client.cc @@ -0,0 +1,113 @@ +#include "webfuse_provider/test_util/client.hpp" +#include "webfuse_provider/client.h" + +#include +#include +#include + +namespace webfuse_test +{ + +class Client::Private +{ +public: + Private(wfp_client_config * config, std::string const & url) + : client(wfp_client_create(config)) + , is_shutdown_requested(false) + { + wfp_client_connect(client, url.c_str()); + thread = std::thread(&Run, this); + } + + ~Private() + { + invoke(shutdown); + + thread.join(); + wfp_client_dispose(client); + } + + void Disconnect() + { + invoke(disconnect); + } + +private: + enum command + { + run, + shutdown, + disconnect + }; + + void invoke(command cmd) + { + { + std::unique_lock lock(mutex); + commands.push(cmd); + } + + wfp_client_interrupt(client); + } + + static void Run(Private * self) + { + bool is_running = true; + while (is_running) + { + switch (self->get_command()) + { + case run: + wfp_client_service(self->client); + break; + case shutdown: + is_running = false; + break; + case disconnect: + wfp_client_disconnect(self->client); + break; + default: + break; + } + } + } + + command get_command() + { + command result = run; + + std::unique_lock lock(mutex); + if (!commands.empty()) + { + result = commands.front(); + commands.pop(); + } + + return result; + } + + wfp_client * client; + bool is_shutdown_requested; + std::thread thread; + std::mutex mutex; + std::queue commands; +}; + +Client::Client(wfp_client_config * config, std::string const & url) +: d(new Private(config, url)) +{ + +} + +Client::~Client() +{ + delete d; +} + +void Client::Disconnect() +{ + d->Disconnect(); +} + + +} \ No newline at end of file diff --git a/test/webfuse_provider/test_util/client.hpp b/test/webfuse_provider/test_util/client.hpp new file mode 100644 index 0000000..9b0412b --- /dev/null +++ b/test/webfuse_provider/test_util/client.hpp @@ -0,0 +1,25 @@ +#ifndef WFP_TEST_UTIL_CLIENT_HPP +#define WFP_TEST_UTIL_CLIENT_HPP + +#include "webfuse_provider/client_config.h" +#include + +namespace webfuse_test +{ + +class Client +{ + Client(Client const &) = delete; + Client & operator=(Client const &) = delete; +public: + Client(wfp_client_config * config, std::string const & url); + ~Client(); + void Disconnect(); +private: + class Private; + Private * d; +}; + +} + +#endif diff --git a/test/webfuse_provider/test_util/jansson_test_environment.cc b/test/webfuse_provider/test_util/jansson_test_environment.cc new file mode 100644 index 0000000..24d091f --- /dev/null +++ b/test/webfuse_provider/test_util/jansson_test_environment.cc @@ -0,0 +1,16 @@ +#include +#include + +namespace webfuse_test +{ + +class JanssonTestEnvironment: public ::testing::Environment +{ +public: + void SetUp() + { + json_object_seed(0); + } +}; +# +} \ No newline at end of file diff --git a/test/webfuse_provider/test_util/timeout_watcher.cc b/test/webfuse_provider/test_util/timeout_watcher.cc new file mode 100644 index 0000000..28d13af --- /dev/null +++ b/test/webfuse_provider/test_util/timeout_watcher.cc @@ -0,0 +1,58 @@ +#include "webfuse_provider/test_util/timeout_watcher.hpp" +#include +#include + +using std::chrono::milliseconds; +using std::chrono::duration_cast; +using std::chrono::steady_clock; + +namespace +{ + milliseconds now() + { + return duration_cast(steady_clock::now().time_since_epoch()); + } +} + +namespace webfuse_test +{ + +TimeoutWatcher::TimeoutWatcher(milliseconds timeout) +: startedAt(now()) +, timeout_(timeout) +{ + +} + +TimeoutWatcher::~TimeoutWatcher() +{ + +} + +bool TimeoutWatcher::isTimeout() +{ + return (now() - startedAt) > timeout_; +} + +void TimeoutWatcher::check() +{ + if (isTimeout()) + { + throw std::runtime_error("timeout"); + } +} + +bool TimeoutWatcher::waitUntil(std::function predicate) +{ + bool result = predicate(); + while ((!result) && (!isTimeout())) + { + std::this_thread::yield(); + result = predicate(); + } + + return result; +} + + +} \ No newline at end of file diff --git a/test/webfuse_provider/test_util/timeout_watcher.hpp b/test/webfuse_provider/test_util/timeout_watcher.hpp new file mode 100644 index 0000000..f0119a9 --- /dev/null +++ b/test/webfuse_provider/test_util/timeout_watcher.hpp @@ -0,0 +1,28 @@ +#ifndef WFP_TEST_TIMEOUT_WATCHER_HPP +#define WFP_TEST_TIMEOUT_WATCHER_HPP + +#include +#include + +namespace webfuse_test +{ + +class TimeoutWatcher final +{ + TimeoutWatcher(TimeoutWatcher const & other) = delete; + TimeoutWatcher& operator=(TimeoutWatcher const & other) = delete; +public: + explicit TimeoutWatcher(std::chrono::milliseconds timeout); + ~TimeoutWatcher(); + bool isTimeout(); + void check(); + bool waitUntil(std::function predicate); +private: + std::chrono::milliseconds startedAt; + std::chrono::milliseconds timeout_; +}; + +} + + +#endif diff --git a/test/webfuse_provider/test_util/webfuse_server.cc b/test/webfuse_provider/test_util/webfuse_server.cc new file mode 100644 index 0000000..b57f247 --- /dev/null +++ b/test/webfuse_provider/test_util/webfuse_server.cc @@ -0,0 +1,362 @@ +#include "webfuse_provider/test_util/webfuse_server.hpp" +#include "webfuse_provider/impl/util/lws_log.h" +#include "webfuse_provider/protocol_names.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMEOUT (std::chrono::seconds(10)) + +namespace +{ + +class IServer +{ +public: + virtual ~IServer() = default; + virtual void OnConnected(lws * wsi) = 0; + virtual void OnConnectionClosed(lws * wsi) = 0; + virtual void OnMessageReceived(lws * wsi, char const * data, size_t length) = 0; + virtual void OnWritable(lws * wsi) = 0; +}; + +} + +extern "C" +{ + +static int wfp_test_utils_webfuse_server_callback( + lws * wsi, + lws_callback_reasons reason, + void * user, + void * in, + size_t len) +{ + int result = 0; + lws_protocols const * protocol = lws_get_protocol(wsi); + auto * server = reinterpret_cast(nullptr != protocol ? protocol->user : nullptr); + + if (nullptr != server) + { + switch (reason) + { + case LWS_CALLBACK_ESTABLISHED: + server->OnConnected(wsi); + break; + case LWS_CALLBACK_CLOSED: + server->OnConnectionClosed(wsi); + break; + case LWS_CALLBACK_RECEIVE: + { + auto * data = reinterpret_cast(in); + server->OnMessageReceived(wsi, data, len); + } + break; + case LWS_CALLBACK_SERVER_WRITEABLE: + server->OnWritable(wsi); + break; + default: + break; + } + } + + return result; +} + +} + +namespace webfuse_test +{ + +class WebfuseServer::Private: public IServer +{ +public: + Private(bool use_tls) + : id(0) + , is_shutdown_requested(false) + , message(nullptr) + , client(nullptr) + { + wfp_impl_lwslog_disable(); + + IServer * server = this; + memset(protocols, 0, sizeof(struct lws_protocols) * 2 ); + + protocols[0].name = WFP_PROTOCOL_NAME_ADAPTER_SERVER; + protocols[0].callback = &wfp_test_utils_webfuse_server_callback; + protocols[0].per_session_data_size = 0; + protocols[0].user = reinterpret_cast(server); + + memset(&info, 0, sizeof(struct lws_context_creation_info)); + info.port = 0; + info.mounts = NULL; + info.protocols = protocols; + info.vhost_name = "localhost"; + info.ws_ping_pong_interval = 10; + info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; + info.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS; + + context = lws_create_context(&info); + + if (use_tls) + { + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.ssl_cert_filepath = "server-cert.pem"; + info.ssl_private_key_filepath = "server-key.pem"; + } + + struct lws_vhost * vhost = lws_create_vhost(context, &info); + int port = lws_get_vhost_port(vhost); + std::ostringstream stream; + stream << (use_tls ? "wss://" : "ws://") << "localhost:" << port << "/"; + url = stream.str(); + + thread = std::thread(&Run, this); + + } + + ~Private() override + { + { + std::unique_lock lock(mutex); + is_shutdown_requested = true; + } + + lws_cancel_service(context); + thread.join(); + lws_context_destroy(context); + } + + std::string const & GetUrl() const + { + return url; + } + + std::string const & GetFilesystem() const + { + return filesystem; + } + + json_t * Invoke(std::string const & method, json_t * params) + { + std::promise response; + { + std::unique_lock lock(mutex); + message = &response; + id++; + + json_t * request = json_object(); + json_object_set_new(request, "method", json_string(method.c_str())); + json_object_set_new(request, "params", params); + json_object_set_new(request, "id", json_integer(id)); + + char * request_text = json_dumps(request, 0); + write_queue.push(request_text); + free(request_text); + json_decref(request); + } + lws_callback_on_writable(client); + + json_t * result = nullptr; + auto future = response.get_future(); + auto state = future.wait_for(TIMEOUT); + if (std::future_status::ready == state) + { + std::string response_text = future.get(); + result = json_loadb(response_text.c_str(), response_text.size(), 0, nullptr); + + std::unique_lock lock(mutex); + message = nullptr; + } + + return result; + } + + void OnConnected(lws * wsi) override + { + std::unique_lock lock(mutex); + client = wsi; + } + + void OnConnectionClosed(lws * wsi) override + { + std::unique_lock lock(mutex); + client = nullptr; + } + + void OnMessageReceived(lws * wsi, char const * data, size_t length) override + { + { + std::unique_lock lock(mutex); + if (nullptr != message) + { + message->set_value(std::string(data, length)); + } + } + + json_t * message = json_loadb(data, length, 0, nullptr); + if (message) + { + json_t * method = json_object_get(message, "method"); + if (json_is_string(method)) + { + if (0 == strcmp("add_filesystem", json_string_value(method))) + { + json_t * id = json_object_get(message, "id"); + + json_t * response = json_object(); + json_t * result = json_object(); + json_object_set_new(result, "id", json_string(GetFilesystem().c_str())); + json_object_set_new(response, "result", result); + json_object_set(response, "id", id); + + char * response_text = json_dumps(response, 0); + { + std::unique_lock lock(mutex); + write_queue.push(response_text); + } + free(response_text); + json_decref(response); + + lws_callback_on_writable(wsi); + } + } + + json_decref(message); + } + } + + void OnWritable(lws * wsi) override + { + bool notify = false; + + { + std::unique_lock lock(mutex); + + if (!write_queue.empty()) + { + std::string const & message = write_queue.front(); + + unsigned char * data = new unsigned char[LWS_PRE + message.size()]; + memcpy(&data[LWS_PRE], message.c_str(), message.size()); + lws_write(wsi, &data[LWS_PRE], message.size(), LWS_WRITE_TEXT); + delete[] data; + + write_queue.pop(); + notify = !write_queue.empty(); + } + } + + if (notify) + { + lws_callback_on_writable(wsi); + } + } + + + +private: + static void Run(Private * self) + { + bool is_running = true; + while (is_running) + { + lws_service(self->context, 0); + { + std::unique_lock lock(self->mutex); + is_running = !self->is_shutdown_requested; + } + } + } + + int id; + bool is_shutdown_requested; + std::promise * message; + lws * client; + std::string url; + lws_context * context; + lws_protocols protocols[2]; + lws_context_creation_info info; + std::thread thread; + std::mutex mutex; + std::queue write_queue; + + std::string filesystem = "fs"; +}; + + +WebfuseServer::WebfuseServer(bool use_tls) +: d(new Private(use_tls)) +{ + +} + +WebfuseServer::~WebfuseServer() +{ + delete d; +} + +std::string const & WebfuseServer::GetUrl() +{ + return d->GetUrl(); +} + +json_t * WebfuseServer::Invoke(std::string const & method, json_t * params) +{ + return d->Invoke(method, params); +} + +json_t * WebfuseServer::Invoke(std::string const & method, std::string const & params) +{ + json_t * params_json = json_loads(params.c_str(), 0, nullptr); + return d->Invoke(method, params_json); +} + +json_t * WebfuseServer::Lookup(int parent, std::string const & name) +{ + json_t * params = json_array(); + json_array_append_new(params, json_string(d->GetFilesystem().c_str())); + json_array_append_new(params, json_integer(parent)); + json_array_append_new(params, json_string(name.c_str())); + + return d->Invoke("lookup", params); +} + +json_t * WebfuseServer::Open(int inode, int flags) +{ + json_t * params = json_array(); + json_array_append_new(params, json_string(d->GetFilesystem().c_str())); + json_array_append_new(params, json_integer(inode)); + json_array_append_new(params, json_integer(flags)); + + return d->Invoke("open", params); +} + +json_t * WebfuseServer::Read(int inode, int handle, int offset, int length) +{ + json_t * params = json_array(); + json_array_append_new(params, json_string(d->GetFilesystem().c_str())); + json_array_append_new(params, json_integer(inode)); + json_array_append_new(params, json_integer(handle)); + json_array_append_new(params, json_integer(offset)); + json_array_append_new(params, json_integer(length)); + + return d->Invoke("read", params); +} + +json_t * WebfuseServer::ReadDir(int inode) +{ + json_t * params = json_array(); + json_array_append_new(params, json_string(d->GetFilesystem().c_str())); + json_array_append_new(params, json_integer(inode)); + + return d->Invoke("readdir", params); +} + +} \ No newline at end of file diff --git a/test/webfuse_provider/test_util/webfuse_server.hpp b/test/webfuse_provider/test_util/webfuse_server.hpp new file mode 100644 index 0000000..ec16dc4 --- /dev/null +++ b/test/webfuse_provider/test_util/webfuse_server.hpp @@ -0,0 +1,31 @@ +#ifndef WFP_TEST_UTIL_WEBFUSE_SERVER_HPP +#define WFP_TEST_UTIL_WEBFUSE_SERVER_HPP + +#include +#include + +namespace webfuse_test +{ + +class WebfuseServer +{ + WebfuseServer(WebfuseServer const &) = delete; + WebfuseServer& operator=(WebfuseServer const &) = delete; +public: + WebfuseServer(bool use_tls = false); + ~WebfuseServer(); + std::string const & GetUrl(); + json_t * Invoke(std::string const & method, json_t * params); + json_t * Invoke(std::string const & method, std::string const & params); + json_t * Lookup(int parent, std::string const & name); + json_t * Open(int inode, int flags); + json_t * Read(int inode, int handle, int offset, int length); + json_t * ReadDir(int inode); +private: + class Private; + Private * d; +}; + +} + +#endif diff --git a/test/webfuse_provider/test_util/wrap.hpp b/test/webfuse_provider/test_util/wrap.hpp new file mode 100644 index 0000000..bb34ff4 --- /dev/null +++ b/test/webfuse_provider/test_util/wrap.hpp @@ -0,0 +1,132 @@ +#ifndef WFP_WRAP_HPP +#define WFP_WRAP_HPP + +#define WFP_WRAP_FUNC0( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (); \ + RETURN_TYPE __wrap_ ## FUNC_NAME () \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(); \ + } \ + } + +#define WFP_WRAP_FUNC1( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME, ARG1_TYPE ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (ARG1_TYPE); \ + RETURN_TYPE __wrap_ ## FUNC_NAME (ARG1_TYPE arg1) \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (arg1); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(arg1); \ + } \ + } + +#define WFP_WRAP_FUNC2( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME, ARG1_TYPE, ARG2_TYPE ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (ARG1_TYPE, ARG2_TYPE); \ + RETURN_TYPE __wrap_ ## FUNC_NAME (ARG1_TYPE arg1, ARG2_TYPE arg2) \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (arg1, arg2); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(arg1, arg2); \ + } \ + } + +#define WFP_WRAP_FUNC3( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (ARG1_TYPE, ARG2_TYPE, ARG3_TYPE); \ + RETURN_TYPE __wrap_ ## FUNC_NAME (ARG1_TYPE arg1, ARG2_TYPE arg2, ARG3_TYPE arg3) \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (arg1, arg2, arg3); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(arg1, arg2, arg3); \ + } \ + } + +#define WFP_WRAP_FUNC4( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE); \ + RETURN_TYPE __wrap_ ## FUNC_NAME (ARG1_TYPE arg1, ARG2_TYPE arg2, ARG3_TYPE arg3, ARG4_TYPE arg4) \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (arg1, arg2, arg3, arg4); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(arg1, arg2, arg3, arg4); \ + } \ + } + +#define WFP_WRAP_FUNC5( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE, ARG5_TYPE ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE, ARG5_TYPE); \ + RETURN_TYPE __wrap_ ## FUNC_NAME (ARG1_TYPE arg1, ARG2_TYPE arg2, ARG3_TYPE arg3, ARG4_TYPE arg4, ARG5_TYPE arg5) \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (arg1, arg2, arg3, arg4, arg5); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(arg1, arg2, arg3, arg4, arg5); \ + } \ + } + +#define WFP_WRAP_FUNC6( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE, ARG5_TYPE, ARG6_TYPE ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE, ARG5_TYPE, ARG6_TYPE); \ + RETURN_TYPE __wrap_ ## FUNC_NAME (ARG1_TYPE arg1, ARG2_TYPE arg2, ARG3_TYPE arg3, ARG4_TYPE arg4, ARG5_TYPE arg5, ARG6_TYPE arg6) \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (arg1, arg2, arg3, arg4, arg5, arg6); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(arg1, arg2, arg3, arg4, arg5, arg6); \ + } \ + } + + +#define WFP_WRAP_VFUNC3( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, va_list); \ + RETURN_TYPE __wrap_ ## FUNC_NAME (ARG1_TYPE arg1, ARG2_TYPE arg2, ARG3_TYPE arg3, va_list args) \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (arg1, arg2, arg3, args); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(arg1, arg2, arg3); \ + } \ + } + +#define WFP_WRAP_VFUNC5( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE, ARG5_TYPE ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE, ARG5_TYPE, va_list); \ + RETURN_TYPE __wrap_ ## FUNC_NAME (ARG1_TYPE arg1, ARG2_TYPE arg2, ARG3_TYPE arg3, ARG4_TYPE arg4, ARG5_TYPE arg5, va_list args) \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (arg1, arg2, arg3, arg4, arg5, args); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(arg1, arg2, arg3, arg4, arg5); \ + } \ + } + + +#endif diff --git a/test/webfuse_provider/test_util/ws_server.cc b/test/webfuse_provider/test_util/ws_server.cc new file mode 100644 index 0000000..2386894 --- /dev/null +++ b/test/webfuse_provider/test_util/ws_server.cc @@ -0,0 +1,306 @@ +#include "webfuse_provider/test_util/ws_server.h" +#include "webfuse_provider/impl/util/lws_log.h" + +#include +#include +#include +#include +#include +#include + +namespace +{ + +class IServer +{ +public: + virtual ~IServer() = default; + virtual void OnConnected(lws * wsi) = 0; + virtual void OnConnectionClosed(lws * wsi) = 0; + virtual void OnMessageReceived(struct lws * wsi, char const * data, size_t length) = 0; + virtual void OnWritable(struct lws * wsi) = 0; +}; + +} + +namespace webfuse_test +{ + +class WsServer::Private : IServer +{ + Private(Private const &) = delete; + Private & operator=(Private const &) = delete; +public: + Private(std::string const & protocol, int port); + ~Private(); + bool IsConnected(); + std::string GetUrl() const; + void SendMessage(json_t * message); + json_t * ReceiveMessage(); + void OnConnected(lws * wsi) override; + void OnConnectionClosed(lws * wsi) override; + void OnMessageReceived(struct lws * wsi, char const * data, size_t length) override; + void OnWritable(struct lws * wsi) override; + +private: + static void run(Private * self); + std::string protocol_; + int port_; + bool is_connected; + bool is_shutdown_requested; + lws * wsi_; + lws_context * ws_context; + lws_protocols ws_protocols[2]; + lws_context_creation_info info; + std::thread context; + std::mutex mutex; + std::queue writeQueue; + std::queue recvQueue; +}; + +} + +extern "C" +{ + +static int wfp_test_utils_ws_server_callback( + struct lws * wsi, + enum lws_callback_reasons reason, + void * user, + void * in, + size_t len) +{ + int result = 0; + struct lws_protocols const * ws_protocol = lws_get_protocol(wsi); + auto * server = reinterpret_cast(nullptr != ws_protocol ? ws_protocol->user : nullptr); + + if (nullptr != server) + { + switch (reason) + { + case LWS_CALLBACK_ESTABLISHED: + server->OnConnected(wsi); + break; + case LWS_CALLBACK_CLOSED: + server->OnConnectionClosed(wsi); + break; + case LWS_CALLBACK_RECEIVE: + { + auto * data = reinterpret_cast(in); + server->OnMessageReceived(wsi, data, len); + } + break; + case LWS_CALLBACK_SERVER_WRITEABLE: + server->OnWritable(wsi); + break; + default: + break; + } + } + + return result; +} + +} + + +namespace webfuse_test +{ + +WsServer::WsServer(std::string const & protocol, int port) +: d(new Private(protocol, port)) +{ + +} + +WsServer::~WsServer() +{ + delete d; +} + +bool WsServer::IsConnected() +{ + return d->IsConnected(); +} + +void WsServer::SendMessage(json_t * message) +{ + d->SendMessage(message); +} + +json_t * WsServer::ReceiveMessage() +{ + return d->ReceiveMessage(); +} + +std::string WsServer::GetUrl() const +{ + return d->GetUrl(); +} + + +WsServer::Private::Private(std::string const & protocol, int port) +: protocol_(protocol) +, port_(port) +, is_connected(false) +, is_shutdown_requested(false) +, wsi_(nullptr) +{ + wfp_impl_lwslog_disable(); + IServer * server = this; + memset(ws_protocols, 0, sizeof(struct lws_protocols) * 2 ); + + ws_protocols[0].name = protocol_.c_str(); + ws_protocols[0].callback = &wfp_test_utils_ws_server_callback; + ws_protocols[0].per_session_data_size = 0; + ws_protocols[0].user = reinterpret_cast(server); + + memset(&info, 0, sizeof(struct lws_context_creation_info)); + info.port = port; + info.mounts = NULL; + info.protocols =ws_protocols; + info.vhost_name = "localhost"; + info.ws_ping_pong_interval = 10; + info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; + info.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS; + + ws_context = lws_create_context(&info); + + struct lws_vhost * vhost = lws_create_vhost(ws_context, &info); + port_ = lws_get_vhost_port(vhost); + + context = std::thread(&run, this); +} + +WsServer::Private::~Private() +{ + { + std::unique_lock lock(mutex); + is_shutdown_requested = true; + } + + lws_cancel_service(ws_context); + context.join(); + lws_context_destroy(ws_context); +} + +void WsServer::Private::run(Private * self) +{ + bool is_running = true; + while (is_running) + { + lws_service(self->ws_context, 0); + { + std::unique_lock lock(self->mutex); + is_running = !self->is_shutdown_requested; + } + } +} + +bool WsServer::Private::IsConnected() +{ + std::unique_lock lock(mutex); + return is_connected; +} + +void WsServer::Private::OnConnected(lws * wsi) +{ + std::unique_lock lock(mutex); + is_connected = true; + wsi_ = wsi; +} + +void WsServer::Private::OnConnectionClosed(lws * wsi) +{ + std::unique_lock lock(mutex); + if (wsi == wsi_) + { + is_connected = false; + wsi_ = nullptr; + } +} + +void WsServer::Private::OnWritable(struct lws * wsi) +{ + bool notify = false; + + { + std::unique_lock lock(mutex); + + if (!writeQueue.empty()) + { + std::string const & message = writeQueue.front(); + + unsigned char * data = new unsigned char[LWS_PRE + message.size()]; + memcpy(&data[LWS_PRE], message.c_str(), message.size()); + lws_write(wsi, &data[LWS_PRE], message.size(), LWS_WRITE_TEXT); + delete[] data; + + writeQueue.pop(); + notify = !writeQueue.empty(); + } + } + + if (notify) + { + lws_callback_on_writable(wsi); + } +} + + +void WsServer::Private::SendMessage(json_t * message) +{ + lws * wsi = nullptr; + + { + std::unique_lock lock(mutex); + + if (nullptr != wsi_) + { + char* message_text = json_dumps(message, JSON_COMPACT); + writeQueue.push(message_text); + json_decref(message); + free(message_text); + wsi = wsi_; + } + } + + if (nullptr != wsi) + { + lws_callback_on_writable(wsi_); + } +} + +void WsServer::Private::OnMessageReceived(struct lws * wsi, char const * data, size_t length) +{ + std::unique_lock lock(mutex); + if (wsi == wsi_) + { + recvQueue.push(std::string(data, length)); + } +} + +json_t * WsServer::Private::ReceiveMessage() +{ + std::unique_lock lock(mutex); + + json_t * result = nullptr; + if (!recvQueue.empty()) + { + std::string const & message_text = recvQueue.front(); + result = json_loads(message_text.c_str(), JSON_DECODE_ANY, nullptr); + recvQueue.pop(); + } + + return result; +} + +std::string WsServer::Private::GetUrl() const +{ + std::ostringstream stream; + stream << "ws://localhost:" << port_ << "/"; + return stream.str(); +} + + +} \ No newline at end of file diff --git a/test/webfuse_provider/test_util/ws_server.h b/test/webfuse_provider/test_util/ws_server.h new file mode 100644 index 0000000..8c28005 --- /dev/null +++ b/test/webfuse_provider/test_util/ws_server.h @@ -0,0 +1,29 @@ +#ifndef WFP_TEST_UTILS_WS_SERVER_HPP +#define WFP_TEST_UTILS_WS_SERVER_HPP + +#include +#include + +namespace webfuse_test +{ + +class WsServer +{ + WsServer(WsServer const &) = delete; + WsServer & operator=(WsServer const &) = delete; +public: + WsServer(std::string const & protocol, int port = 0); + ~WsServer(); + bool IsConnected(); + std::string GetUrl() const; + void SendMessage(json_t * message); + json_t * ReceiveMessage(); +private: + class Private; + Private * d; +}; + +} + + +#endif diff --git a/test/webfuse_provider/timer/test_timepoint.cc b/test/webfuse_provider/timer/test_timepoint.cc new file mode 100644 index 0000000..3818fa9 --- /dev/null +++ b/test/webfuse_provider/timer/test_timepoint.cc @@ -0,0 +1,38 @@ +#include + +#include "webfuse_provider/impl/timer/timepoint.h" + +#include +#include + +using namespace std::chrono_literals; + +TEST(wfp_timer_timepoint, now) +{ + wfp_timer_timepoint start = wfp_timer_timepoint_now(); + std::this_thread::sleep_for(42ms); + wfp_timer_timepoint end = wfp_timer_timepoint_now(); + + ASSERT_LT(start, end); + ASSERT_LT(end, start + 500); +} + +TEST(wfp_timer_timepoint, in_msec) +{ + wfp_timer_timepoint now = wfp_timer_timepoint_now(); + wfp_timer_timepoint later = wfp_timer_timepoint_in_msec(42); + + ASSERT_LT(now, later); + ASSERT_LT(later, now + 500); +} + +TEST(wfp_timer_timepoint, elapsed) +{ + wfp_timer_timepoint now; + + now = wfp_timer_timepoint_now(); + ASSERT_TRUE(wfp_timer_timepoint_is_elapsed(now - 1)); + + now = wfp_timer_timepoint_now(); + ASSERT_FALSE(wfp_timer_timepoint_is_elapsed(now + 500)); +} diff --git a/test/webfuse_provider/timer/test_timer.cc b/test/webfuse_provider/timer/test_timer.cc new file mode 100644 index 0000000..a87e869 --- /dev/null +++ b/test/webfuse_provider/timer/test_timer.cc @@ -0,0 +1,148 @@ +#include + +#include +#include +#include + +#include "webfuse_provider/impl/timer/timer.h" +#include "webfuse_provider/impl/timer/manager.h" + +using std::size_t; +using namespace std::chrono_literals; + +extern "C" +{ + void on_timeout(struct wfp_timer * timer, void * user_data) + { + (void) timer; + + bool * triggered = reinterpret_cast(user_data); + *triggered = true; + } +} + +TEST(wfp_timer, init) +{ + bool triggered = false; + struct wfp_timer_manager * manager = wfp_timer_manager_create(); + struct wfp_timer * timer = wfp_timer_create(manager, &on_timeout, reinterpret_cast(&triggered)); + + wfp_timer_dispose(timer); + wfp_timer_manager_dispose(manager); +} + +TEST(wfp_timer, trigger) +{ + bool triggered = false; + struct wfp_timer_manager * manager = wfp_timer_manager_create(); + struct wfp_timer * timer = wfp_timer_create(manager, &on_timeout, reinterpret_cast(&triggered)); + + wfp_timer_start(timer, -1); + wfp_timer_manager_check(manager); + + ASSERT_TRUE(triggered); + + wfp_timer_dispose(timer); + wfp_timer_manager_dispose(manager); +} + +TEST(wfp_timer, trigger_on_dispose) +{ + bool triggered = false; + struct wfp_timer_manager * manager = wfp_timer_manager_create(); + struct wfp_timer * timer = wfp_timer_create(manager, &on_timeout, reinterpret_cast(&triggered)); + + wfp_timer_start(timer, (5 * 60 * 1000)); + + wfp_timer_manager_dispose(manager); + ASSERT_TRUE(triggered); + + wfp_timer_dispose(timer); +} + +TEST(wfp_timer, cancel) +{ + bool triggered = false; + struct wfp_timer_manager * manager = wfp_timer_manager_create(); + struct wfp_timer * timer = wfp_timer_create(manager, &on_timeout, reinterpret_cast(&triggered)); + + wfp_timer_start(timer, 250); + std::this_thread::sleep_for(500ms); + wfp_timer_cancel(timer); + wfp_timer_manager_check(manager); + + ASSERT_FALSE(triggered); + + wfp_timer_dispose(timer); + wfp_timer_manager_dispose(manager); +} + +TEST(wfp_timer, cancel_multiple_timers) +{ + static size_t const count = 5; + struct wfp_timer_manager * manager = wfp_timer_manager_create(); + struct wfp_timer * timer[count]; + + bool triggered = false; + for(size_t i = 0; i < count; i++) + { + timer[i] = wfp_timer_create(manager, &on_timeout, reinterpret_cast(&triggered)); + wfp_timer_start(timer[i], 0); + } + + std::this_thread::sleep_for(10ms); + for(size_t i = 0; i < count; i++) + { + wfp_timer_cancel(timer[i]); + } + + wfp_timer_manager_check(manager); + ASSERT_FALSE(triggered); + + for(size_t i = 0; i < count; i++) + { + wfp_timer_dispose(timer[i]); + } + wfp_timer_manager_dispose(manager); +} + +TEST(wfp_timer, multiple_timers) +{ + static size_t const count = 5; + struct wfp_timer_manager * manager = wfp_timer_manager_create(); + struct wfp_timer * timer[count]; + bool triggered[count]; + + for(size_t i = 0; i < count; i++) + { + timer[i] = wfp_timer_create(manager, &on_timeout, reinterpret_cast(&triggered[i])); + triggered[i] = false; + wfp_timer_start(timer[i], (300 - (50 * i))); + } + + for(size_t i = 0; i < count; i++) + { + std::this_thread::sleep_for(100ms); + wfp_timer_manager_check(manager); + } + + for(size_t i = 0; i < count; i++) + { + ASSERT_TRUE(triggered[i]); + wfp_timer_dispose(timer[i]); + } + + wfp_timer_manager_dispose(manager); +} + +TEST(wfp_timer, dont_trigger_null_callback) +{ + struct wfp_timer_manager * manager = wfp_timer_manager_create(); + struct wfp_timer * timer = wfp_timer_create(manager, nullptr, nullptr); + + wfp_timer_start(timer, -1); + wfp_timer_manager_check(manager); + + wfp_timer_dispose(timer); + wfp_timer_manager_dispose(manager); +} diff --git a/test/webfuse_provider/util/test_base64.cc b/test/webfuse_provider/util/test_base64.cc new file mode 100644 index 0000000..c743ca7 --- /dev/null +++ b/test/webfuse_provider/util/test_base64.cc @@ -0,0 +1,122 @@ +#include +#include "webfuse_provider/impl/util/base64.h" + +TEST(Base64, EncodedSize) +{ + ASSERT_EQ(4, wfp_impl_base64_encoded_size(1)); + ASSERT_EQ(4, wfp_impl_base64_encoded_size(2)); + ASSERT_EQ(4, wfp_impl_base64_encoded_size(3)); + + ASSERT_EQ(8, wfp_impl_base64_encoded_size(4)); + ASSERT_EQ(8, wfp_impl_base64_encoded_size(5)); + ASSERT_EQ(8, wfp_impl_base64_encoded_size(6)); + + ASSERT_EQ(120, wfp_impl_base64_encoded_size(90)); +} + +TEST(Base64, Encode) +{ + char buffer[42]; + + std::string in = "Hello"; + size_t length = wfp_impl_base64_encode((uint8_t const*) in.c_str(), in.size(), buffer, 42); + ASSERT_EQ(8, length); + ASSERT_STREQ("SGVsbG8=", buffer); + + in = "Hello\n"; + length = wfp_impl_base64_encode((uint8_t const*) in.c_str(), in.size(), buffer, 42); + ASSERT_EQ(8, length); + ASSERT_STREQ("SGVsbG8K", buffer); + + in = "Blue"; + length = wfp_impl_base64_encode((uint8_t const*) in.c_str(), in.size(), buffer, 42); + ASSERT_EQ(8, length); + ASSERT_STREQ("Qmx1ZQ==", buffer); +} + +TEST(Base64, FailedToEncodeBufferTooSmall) +{ + char buffer[1]; + + std::string in = "Hello"; + size_t length = wfp_impl_base64_encode((uint8_t const*) in.c_str(), in.size(), buffer, 1); + ASSERT_EQ(0, length); +} + +TEST(Base64, DecodedSize) +{ + std::string in = "SGVsbG8="; // Hello + size_t length = wfp_impl_base64_decoded_size(in.c_str(), in.size()); + ASSERT_EQ(5, length); + + in = "SGVsbG8K"; // Hello\n + length = wfp_impl_base64_decoded_size(in.c_str(), in.size()); + ASSERT_EQ(6, length); + + in = "Qmx1ZQ=="; // Blue + length = wfp_impl_base64_decoded_size(in.c_str(), in.size()); + ASSERT_EQ(4, length); +} + +TEST(Base64, IsValid) +{ + std::string in = "SGVsbG8="; // Hello + ASSERT_TRUE(wfp_impl_base64_isvalid(in.c_str(), in.size())); + + in = "SGVsbG8K"; // Hello\n + ASSERT_TRUE(wfp_impl_base64_isvalid(in.c_str(), in.size())); + + in = "Qmx1ZQ=="; // Blue + ASSERT_TRUE(wfp_impl_base64_isvalid(in.c_str(), in.size())); + + in = "Qmx1ZQ=a"; + ASSERT_FALSE(wfp_impl_base64_isvalid(in.c_str(), in.size())); + + in = "Qmx1ZQ"; + ASSERT_FALSE(wfp_impl_base64_isvalid(in.c_str(), in.size())); + + in = "Qmx1ZQ="; + ASSERT_FALSE(wfp_impl_base64_isvalid(in.c_str(), in.size())); + + in = "Qmx1Z==="; + ASSERT_FALSE(wfp_impl_base64_isvalid(in.c_str(), in.size())); + + in = "Qmx1ZQ?="; + ASSERT_FALSE(wfp_impl_base64_isvalid(in.c_str(), in.size())); + + in = "Qm?1ZQ=="; + ASSERT_FALSE(wfp_impl_base64_isvalid(in.c_str(), in.size())); +} + +TEST(Base64, Decode) +{ + char buffer[42]; + + std::string in = "SGVsbG8="; // Hello + size_t length = wfp_impl_base64_decode(in.c_str(), in.size(), (uint8_t*) buffer, 42); + ASSERT_EQ(5, length); + buffer[length] = '\0'; + ASSERT_STREQ("Hello", buffer); + + in = "SGVsbG8K"; // Hello\n + length = wfp_impl_base64_decode(in.c_str(), in.size(), (uint8_t*) buffer, 42); + ASSERT_EQ(6, length); + buffer[length] = '\0'; + ASSERT_STREQ("Hello\n", buffer); + + in = "Qmx1ZQ=="; // Blue + length = wfp_impl_base64_decode(in.c_str(), in.size(), (uint8_t*) buffer, 42); + ASSERT_EQ(4, length); + buffer[length] = '\0'; + ASSERT_STREQ("Blue", buffer); +} + +TEST(Base64, FailToDecodeBufferTooSmall) +{ + char buffer[1]; + + std::string in = "SGVsbG8="; // Hello + size_t length = wfp_impl_base64_decode(in.c_str(), in.size(), (uint8_t*) buffer, 1); + ASSERT_EQ(0, length); +} + diff --git a/test/webfuse_provider/util/test_container_of.cc b/test/webfuse_provider/util/test_container_of.cc new file mode 100644 index 0000000..0fda6b2 --- /dev/null +++ b/test/webfuse_provider/util/test_container_of.cc @@ -0,0 +1,29 @@ +#include +#include "webfuse_provider/impl/util/container_of.h" + +namespace +{ + +struct MyStruct +{ + int first; + int second; +}; + +} + +TEST(ContainerOf, FirstMember) +{ + MyStruct my_struct = {23, 42}; + + int * first = &my_struct.first; + ASSERT_EQ(&my_struct, wfp_container_of(first, MyStruct, first)); +} + +TEST(ContainerOf, SecondMember) +{ + MyStruct my_struct = {23, 42}; + + int * second = &my_struct.second; + ASSERT_EQ(&my_struct, wfp_container_of(second, MyStruct, second)); +} \ No newline at end of file diff --git a/test/webfuse_provider/util/test_message.cc b/test/webfuse_provider/util/test_message.cc new file mode 100644 index 0000000..ae46b2f --- /dev/null +++ b/test/webfuse_provider/util/test_message.cc @@ -0,0 +1,16 @@ +#include +#include +#include "webfuse_provider/impl/message.h" + +TEST(wfp_message, create) +{ + json_t * value = json_object(); + + struct wfp_message * message = wfp_message_create(value); + ASSERT_NE(nullptr, message); + ASSERT_EQ(2, message->length); + ASSERT_TRUE(0 == strncmp("{}", message->data, 2)); + + wfp_message_dispose(message); + json_decref(value); +} diff --git a/test/webfuse_provider/util/test_message_queue.cc b/test/webfuse_provider/util/test_message_queue.cc new file mode 100644 index 0000000..3f135c2 --- /dev/null +++ b/test/webfuse_provider/util/test_message_queue.cc @@ -0,0 +1,52 @@ +#include +#include "webfuse_provider/impl/message_queue.h" +#include "webfuse_provider/impl/message.h" +#include "webfuse_provider/impl/util/slist.h" + +namespace +{ + + struct wfp_slist_item * create_message(char const * content) + { + json_t * value = json_object(); + json_object_set_new(value, "content", json_string(content)); + struct wfp_message * message = wfp_message_create(value); + + json_decref(value); + return &message->item; + } + +} + +TEST(wfp_message_queue, cleanup_empty_list) +{ + struct wfp_slist queue; + wfp_slist_init(&queue); + + wfp_message_queue_cleanup(&queue); + ASSERT_TRUE(wfp_slist_empty(&queue)); +} + +TEST(wfp_message_queue, cleanup_one_element) +{ + struct wfp_slist queue; + wfp_slist_init(&queue); + + wfp_slist_append(&queue, create_message("Hello")); + + wfp_message_queue_cleanup(&queue); + ASSERT_TRUE(wfp_slist_empty(&queue)); +} + +TEST(wfp_message_queue, cleanup_multiple_element) +{ + struct wfp_slist queue; + wfp_slist_init(&queue); + + wfp_slist_append(&queue, create_message("Hello")); + wfp_slist_append(&queue, create_message("World")); + wfp_slist_append(&queue, create_message("!")); + + wfp_message_queue_cleanup(&queue); + ASSERT_TRUE(wfp_slist_empty(&queue)); +} \ No newline at end of file diff --git a/test/webfuse_provider/util/test_slist.cc b/test/webfuse_provider/util/test_slist.cc new file mode 100644 index 0000000..177a585 --- /dev/null +++ b/test/webfuse_provider/util/test_slist.cc @@ -0,0 +1,139 @@ +#include +#include "webfuse_provider/impl/util/slist.h" + +TEST(wfp_slist, init) +{ + struct wfp_slist list; + wfp_slist_init(&list); + + ASSERT_EQ(nullptr, list.head.next); + ASSERT_EQ(nullptr, list.last->next); + ASSERT_EQ(&list.head, list.last); + ASSERT_TRUE(wfp_slist_empty(&list)); + ASSERT_EQ(nullptr, wfp_slist_first(&list)); +} + +TEST(wfp_slist, append) +{ + struct wfp_slist list; + struct wfp_slist_item item[3]; + + wfp_slist_init(&list); + ASSERT_TRUE(wfp_slist_empty(&list)); + + wfp_slist_append(&list, &item[0]); + ASSERT_NE(&list.head, list.last); + ASSERT_FALSE(wfp_slist_empty(&list)); + ASSERT_EQ(&item[0], wfp_slist_first(&list)); + ASSERT_EQ(&item[0], list.head.next); + ASSERT_EQ(&item[0], list.last); + ASSERT_EQ(nullptr, list.last->next); + ASSERT_EQ(nullptr, item[0].next); + + wfp_slist_append(&list, &item[1]); + ASSERT_NE(&list.head, list.last); + ASSERT_FALSE(wfp_slist_empty(&list)); + ASSERT_EQ(&item[0], wfp_slist_first(&list)); + ASSERT_EQ(&item[0], list.head.next); + ASSERT_EQ(&item[1], list.last); + ASSERT_EQ(nullptr, list.last->next); + ASSERT_EQ(&item[1], item[0].next); + ASSERT_EQ(nullptr, item[1].next); + + wfp_slist_append(&list, &item[2]); + ASSERT_NE(&list.head, list.last); + ASSERT_FALSE(wfp_slist_empty(&list)); + ASSERT_EQ(&item[0], wfp_slist_first(&list)); + ASSERT_EQ(&item[0], list.head.next); + ASSERT_EQ(&item[2], list.last); + ASSERT_EQ(nullptr, list.last->next); + ASSERT_EQ(&item[1], item[0].next); + ASSERT_EQ(&item[2], item[1].next); + ASSERT_EQ(nullptr, item[2].next); +} + +TEST(wfp_slist_remove_after, remove_first) +{ + struct wfp_slist list; + struct wfp_slist_item item[3]; + + wfp_slist_init(&list); + wfp_slist_append(&list, &item[0]); + wfp_slist_append(&list, &item[1]); + wfp_slist_append(&list, &item[2]); + + wfp_slist_item * removed; + + removed = wfp_slist_remove_first(&list); + ASSERT_FALSE(wfp_slist_empty(&list)); + ASSERT_EQ(&item[0], removed); + + removed = wfp_slist_remove_first(&list); + ASSERT_FALSE(wfp_slist_empty(&list)); + ASSERT_EQ(&item[1], removed); + + removed = wfp_slist_remove_first(&list); + ASSERT_TRUE(wfp_slist_empty(&list)); + ASSERT_EQ(&item[2], removed); + + ASSERT_EQ(nullptr, list.head.next); + ASSERT_EQ(nullptr, list.last->next); + ASSERT_EQ(&list.head, list.last); + ASSERT_EQ(nullptr, wfp_slist_first(&list)); +} + +TEST(wfp_slist_remove_after, remove_last) +{ + struct wfp_slist list; + struct wfp_slist_item item[3]; + + wfp_slist_init(&list); + wfp_slist_append(&list, &item[0]); + wfp_slist_append(&list, &item[1]); + wfp_slist_append(&list, &item[2]); + + wfp_slist_item * removed; + + removed = wfp_slist_remove_after(&list, &item[1]); + ASSERT_FALSE(wfp_slist_empty(&list)); + ASSERT_EQ(&item[2], removed); + + removed = wfp_slist_remove_after(&list, &item[0]); + ASSERT_FALSE(wfp_slist_empty(&list)); + ASSERT_EQ(&item[1], removed); + + removed = wfp_slist_remove_after(&list, &list.head); + ASSERT_TRUE(wfp_slist_empty(&list)); + ASSERT_EQ(&item[0], removed); + + ASSERT_EQ(nullptr, list.head.next); + ASSERT_EQ(nullptr, list.last->next); + ASSERT_EQ(&list.head, list.last); + ASSERT_EQ(nullptr, wfp_slist_first(&list)); +} + +TEST(wfp_slist_remove_after, remove_after) +{ + struct wfp_slist list; + struct wfp_slist_item item[3]; + + wfp_slist_init(&list); + wfp_slist_append(&list, &item[0]); + wfp_slist_append(&list, &item[1]); + wfp_slist_append(&list, &item[2]); + + wfp_slist_item * removed; + + removed = wfp_slist_remove_after(&list, &item[0]); + ASSERT_FALSE(wfp_slist_empty(&list)); + ASSERT_EQ(&item[1], removed); + + ASSERT_NE(&list.head, list.last); + ASSERT_FALSE(wfp_slist_empty(&list)); + ASSERT_EQ(&item[0], wfp_slist_first(&list)); + ASSERT_EQ(&item[0], list.head.next); + ASSERT_EQ(&item[2], list.last); + ASSERT_EQ(nullptr, list.last->next); + ASSERT_EQ(&item[2], item[0].next); + ASSERT_EQ(nullptr, item[2].next); +} diff --git a/test/webfuse_provider/util/test_status.cc b/test/webfuse_provider/util/test_status.cc new file mode 100644 index 0000000..f9087e0 --- /dev/null +++ b/test/webfuse_provider/util/test_status.cc @@ -0,0 +1,30 @@ +#include +#include "webfuse_provider/impl/status_intern.h" + +TEST(wfp_status, tostring) +{ + ASSERT_STREQ("Good", wfp_status_tostring(WFP_GOOD)); + ASSERT_STREQ("Bad", wfp_status_tostring(WFP_BAD)); + ASSERT_STREQ("Bad (not implemented)", wfp_status_tostring(WFP_BAD_NOTIMPLEMENTED)); + ASSERT_STREQ("Bad (busy)", wfp_status_tostring(WFP_BAD_BUSY)); + ASSERT_STREQ("Bad (timeout)", wfp_status_tostring(WFP_BAD_TIMEOUT)); + ASSERT_STREQ("Bad (format)", wfp_status_tostring(WFP_BAD_FORMAT)); + ASSERT_STREQ("Bad (no entry)", wfp_status_tostring(WFP_BAD_NOENTRY)); + ASSERT_STREQ("Bad (access denied)", wfp_status_tostring(WFP_BAD_ACCESS_DENIED)); + + ASSERT_STREQ("Bad (unknown)", wfp_status_tostring(-1)); +} + +TEST(wfp_status, to_rc) +{ + ASSERT_EQ(0, wfp_status_to_rc(WFP_GOOD)); + ASSERT_EQ(-ENOENT, wfp_status_to_rc(WFP_BAD)); + ASSERT_EQ(-ENOSYS, wfp_status_to_rc(WFP_BAD_NOTIMPLEMENTED)); + ASSERT_EQ(-ENOENT, wfp_status_to_rc(WFP_BAD_BUSY)); + ASSERT_EQ(-ETIMEDOUT, wfp_status_to_rc(WFP_BAD_TIMEOUT)); + ASSERT_EQ(-ENOENT, wfp_status_to_rc(WFP_BAD_FORMAT)); + ASSERT_EQ(-ENOENT, wfp_status_to_rc(WFP_BAD_NOENTRY)); + ASSERT_EQ(-EACCES, wfp_status_to_rc(WFP_BAD_ACCESS_DENIED)); + + ASSERT_EQ(-ENOENT, wfp_status_to_rc(-1)); +} \ No newline at end of file diff --git a/test/webfuse_provider/util/test_url.cc b/test/webfuse_provider/util/test_url.cc new file mode 100644 index 0000000..ea908c4 --- /dev/null +++ b/test/webfuse_provider/util/test_url.cc @@ -0,0 +1,92 @@ +#include + +#include "webfuse_provider/impl/util/url.h" + +TEST(url, ParseWs) +{ + struct wfp_url url; + bool result = wfp_url_init(&url, "ws://localhost/"); + ASSERT_TRUE(result); + ASSERT_EQ(80, url.port); + ASSERT_FALSE(url.use_tls); + ASSERT_STREQ("localhost", url.host); + ASSERT_STREQ("/", url.path); + + wfp_url_cleanup(&url); +} + +TEST(url, ParswWss) +{ + struct wfp_url url; + bool result = wfp_url_init(&url, "wss://localhost/"); + ASSERT_TRUE(result); + ASSERT_EQ(443, url.port); + ASSERT_TRUE(url.use_tls); + ASSERT_STREQ("localhost", url.host); + ASSERT_STREQ("/", url.path); + + wfp_url_cleanup(&url); +} + +TEST(url, ParseIPAdress) +{ + struct wfp_url url; + bool result = wfp_url_init(&url, "ws://127.0.0.1/"); + ASSERT_TRUE(result); + ASSERT_EQ(80, url.port); + ASSERT_STREQ("127.0.0.1", url.host); + ASSERT_STREQ("/", url.path); + + wfp_url_cleanup(&url); +} + +TEST(url, ParsePort) +{ + struct wfp_url url; + bool result = wfp_url_init(&url, "ws://localhost:54321/"); + ASSERT_TRUE(result); + ASSERT_EQ(54321, url.port); + + wfp_url_cleanup(&url); +} + +TEST(url, ParseNonEmptyPath) +{ + struct wfp_url url; + bool result = wfp_url_init(&url, "ws://localhost/some_path?query"); + ASSERT_TRUE(result); + ASSERT_STREQ("/some_path?query", url.path); + + wfp_url_cleanup(&url); +} + +TEST(url, FailToParseUnknownProtocol) +{ + struct wfp_url url; + bool result = wfp_url_init(&url, "unknown://localhost/"); + ASSERT_FALSE(result); + ASSERT_EQ(0, url.port); + ASSERT_EQ(nullptr, url.path); + ASSERT_EQ(nullptr, url.host); +} + +TEST(url, FailToParseMissingProtocol) +{ + struct wfp_url url; + bool result = wfp_url_init(&url, "unknown"); + ASSERT_FALSE(result); + ASSERT_EQ(0, url.port); + ASSERT_EQ(nullptr, url.path); + ASSERT_EQ(nullptr, url.host); +} + +TEST(url, FailToParseMissingPath) +{ + struct wfp_url url; + bool result = wfp_url_init(&url, "ws://localhost"); + ASSERT_FALSE(result); + ASSERT_EQ(0, url.port); + ASSERT_EQ(nullptr, url.path); + ASSERT_EQ(nullptr, url.host); +} + diff --git a/test/webfuse_provider/util/test_util.cc b/test/webfuse_provider/util/test_util.cc new file mode 100644 index 0000000..c9982ea --- /dev/null +++ b/test/webfuse_provider/util/test_util.cc @@ -0,0 +1,72 @@ +#include +#include "webfuse_provider/impl/util/json_util.h" + +TEST(jsonrpc_util, get_int) +{ + json_t * object = json_object(); + json_object_set_new(object, "key", json_integer(23)); + int value = wfp_impl_json_get_int(object, "key", 42); + ASSERT_EQ(23, value); + + json_decref(object); +} + +TEST(jsonrpc_util, failed_to_get_null_object) +{ + int value = wfp_impl_json_get_int(nullptr, "key", 42); + + ASSERT_EQ(42, value); +} + +TEST(jsonrpc_util, failed_to_get_not_object) +{ + json_t * object = json_array(); + int value = wfp_impl_json_get_int(nullptr, "key", 42); + ASSERT_EQ(42, value); + + json_decref(object); +} + +TEST(jsonrpc_util, failed_to_get_invalid_key) +{ + json_t * object = json_object(); + int value = wfp_impl_json_get_int(object, "key", 42); + ASSERT_EQ(42, value); + + json_decref(object); +} + +TEST(jsonrpc_util, failed_to_get_invalid_value_type) +{ + json_t * object = json_object(); + json_object_set_new(object, "key", json_string("42")); + int value = wfp_impl_json_get_int(object, "key", 42); + ASSERT_EQ(42, value); + + json_decref(object); +} + +TEST(jsonrpc_util, get_status_good_if_no_error) +{ + json_t * error = nullptr; + wfp_status status = wfp_impl_jsonrpc_get_status(error); + ASSERT_EQ(WFP_GOOD, status); +} + +TEST(jsonrpc_util, get_status) +{ + json_t * error = json_object(); + json_object_set_new(error, "code", json_integer(WFP_BAD_BUSY)); + wfp_status status = wfp_impl_jsonrpc_get_status(error); + ASSERT_EQ(WFP_BAD_BUSY, status); + json_decref(error); +} + +TEST(jsonrpc_util, get_status_bad_format) +{ + json_t * error = json_array(); + json_array_append_new(error, json_integer(WFP_BAD_BUSY)); + wfp_status status = wfp_impl_jsonrpc_get_status(error); + ASSERT_EQ(WFP_BAD_FORMAT, status); + json_decref(error); +}