From 16996e1f9ad824f2e4a08ce42afc856131d81c8e Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Tue, 9 Jun 2020 22:41:38 +0200 Subject: [PATCH 01/37] added first impression of adapter client API --- include/webfuse/adapter/client.h | 80 ++++++++++++++++ include/webfuse/adapter/client_credentials.h | 28 ++++++ include/webfuse_adapter.h | 4 + lib/webfuse/adapter/api.c | 98 ++++++++++++++++++++ meson.build | 1 + test/webfuse/tests/adapter/test_client.cc | 85 +++++++++++++++++ 6 files changed, 296 insertions(+) create mode 100644 include/webfuse/adapter/client.h create mode 100644 include/webfuse/adapter/client_credentials.h create mode 100644 test/webfuse/tests/adapter/test_client.cc diff --git a/include/webfuse/adapter/client.h b/include/webfuse/adapter/client.h new file mode 100644 index 0000000..2e8273f --- /dev/null +++ b/include/webfuse/adapter/client.h @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file adapter/client.h +/// \brief Adapter client. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WF_ADAPTER_CLIENT_H +#define WF_ADAPTER_CLIENT_H + +#include "webfuse/adapter/api.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define WF_CLIENT_CREATED 0x0001 +#define WF_CLIENT_DISPOSING 0x0002 + +#define WF_CLIENT_CONNECTED 0x0011 +#define WF_CLIENT_DISCONNECTED 0x0012 + +#define WF_CLIENT_AUTHENTICATED 0x0021 +#define WF_CLIENT_AUTHENTICATION_FAILED 0x0022 +#define WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS 0x0023 + +#define WF_CLIENT_FILESYSTEM_ADDED 0x0031 +#define WF_CLIENT_FILESYSTEM_ADD_FAILED 0x0032 + +struct wf_client; + +typedef void wf_client_callback_fn( + struct wf_client * client, + int reason, + void * args); + +extern WF_API struct wf_client * +wf_client_create( + wf_client_callback_fn * callback, + void * user_data); + +extern WF_API void +wf_client_dispose( + struct wf_client * client); + +extern WF_API void * +wf_client_get_userdata( + struct wf_client * client); + +extern WF_API void +wf_client_service( + struct wf_client * client); + +extern WF_API void +wf_client_interrupt( + struct wf_client * client); + +extern WF_API void +wf_client_connect( + struct wf_client * client, + char const * url); + +extern WF_API void +wf_client_disconnect( + struct wf_client * client); + +extern WF_API void +wf_client_authenticate( + struct wf_client * client); + +extern WF_API void +wf_client_add_filesystem( + struct wf_client * client, + char const * local_path, + char const * name); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/include/webfuse/adapter/client_credentials.h b/include/webfuse/adapter/client_credentials.h new file mode 100644 index 0000000..f673317 --- /dev/null +++ b/include/webfuse/adapter/client_credentials.h @@ -0,0 +1,28 @@ +#ifndef WF_ADAPTER_CLIENT_CREDENTIALS_H +#define WF_ADAPTER_CLIENT_CREDENTIALS_H + +#include "webfuse/adapter/api.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_client_credentials; + +extern WF_API void +wf_client_credentials_set_type( + struct wf_client_credentials * credentials, + char const * type); + +extern WF_API void +wf_client_credentials_add( + struct wf_client_credentials * credentials, + char const * key, + char const * value); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/webfuse_adapter.h b/include/webfuse_adapter.h index 1ff048f..da9533c 100644 --- a/include/webfuse_adapter.h +++ b/include/webfuse_adapter.h @@ -17,4 +17,8 @@ #include #include +#include +#include + + #endif diff --git a/lib/webfuse/adapter/api.c b/lib/webfuse/adapter/api.c index 5049e16..66288e6 100644 --- a/lib/webfuse/adapter/api.c +++ b/lib/webfuse/adapter/api.c @@ -178,3 +178,101 @@ wf_mountpoint_set_userdata( { wf_impl_mountpoint_set_userdata(mountpoint, user_data, dispose); } + +// client + +struct wf_client * +wf_client_create( + wf_client_callback_fn * callback, + void * user_data) +{ + (void) callback; + (void) user_data; + + return NULL; +} + +void +wf_client_dispose( + struct wf_client * client) +{ + (void) client; +} + +void * +wf_client_get_userdata( + struct wf_client * client) +{ + (void) client; + return NULL; +} + +void +wf_client_service( + struct wf_client * client) +{ + (void) client; +} + +void +wf_client_interrupt( + struct wf_client * client) +{ + (void) client; +} + +void +wf_client_connect( + struct wf_client * client, + char const * url) +{ + (void) client; + (void) url; +} + +void +wf_client_disconnect( + struct wf_client * client) +{ + (void) client; +} + +void +wf_client_authenticate( + struct wf_client * client) +{ + (void) client; +} + +void +wf_client_add_filesystem( + struct wf_client * client, + char const * local_path, + char const * name) +{ + (void) client; + (void) local_path; + (void) name; +} + +// client credentials + +void +wf_client_credentials_set_type( + struct wf_client_credentials * credentials, + char const * type) +{ + (void) credentials; + (void) type; +} + +void +wf_client_credentials_add( + struct wf_client_credentials * credentials, + char const * key, + char const * value) +{ + (void) credentials; + (void) key; + (void) value; +} \ No newline at end of file diff --git a/meson.build b/meson.build index e08f828..42edd6f 100644 --- a/meson.build +++ b/meson.build @@ -250,6 +250,7 @@ alltests = executable('alltests', 'test/webfuse/tests/integration/file.cc', 'test/webfuse/tests/integration/server.cc', 'test/webfuse/tests/integration/provider.cc', + 'test/webfuse/tests/adapter/test_client.cc', link_args: [ '-Wl,--wrap=wf_timer_manager_create', '-Wl,--wrap=wf_timer_manager_dispose', diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc new file mode 100644 index 0000000..d972447 --- /dev/null +++ b/test/webfuse/tests/adapter/test_client.cc @@ -0,0 +1,85 @@ +#include + +#include "webfuse/adapter/client.h" +#include "webfuse/adapter/client_credentials.h" + +namespace +{ + +enum class connection_state +{ + disconnected, + connected, + connecting +}; + +struct context +{ + connection_state state; +}; + +void callback( + wf_client * client, + int reason, + void * args) +{ + auto * ctx = reinterpret_cast(wf_client_get_userdata(client)); + + switch (reason) + { + case WF_CLIENT_CREATED: + ctx->state = connection_state::connecting; + wf_client_connect(client, "ws://dummy-server/"); + break; + case WF_CLIENT_CONNECTED: + ctx->state = connection_state::connected; + wf_client_authenticate(client); + break; + case WF_CLIENT_AUTHENTICATED: + wf_client_add_filesystem(client, ".", "test"); + break; + case WF_CLIENT_AUTHENTICATION_FAILED: + wf_client_disconnect(client); + break; + case WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS: + { + auto * credentials = reinterpret_cast(args); + wf_client_credentials_set_type(credentials, "username"); + wf_client_credentials_add(credentials, "user", "bob"); + wf_client_credentials_add(credentials, "password", "secret"); + } + break; + case WF_CLIENT_FILESYSTEM_ADDED: + // operational + break; + case WF_CLIENT_FILESYSTEM_ADD_FAILED: + wf_client_disconnect(client); + break; + case WF_CLIENT_DISCONNECTED: + ctx->state = connection_state::disconnected; + break; + default: + break; + } +} + +} + +TEST(client, general_usage) +{ + context ctx; + ctx.state = connection_state::connecting; + + wf_client * client = wf_client_create( + &callback, reinterpret_cast(&ctx)); + + if (nullptr != client) + { + while (ctx.state != connection_state::disconnected) + { + wf_client_service(client); + } + + wf_client_dispose(client); + } +} \ No newline at end of file From b9eff8d100d8f077f79adbd083ee6573b5a18b31 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Wed, 10 Jun 2020 22:18:34 +0200 Subject: [PATCH 02/37] change: make credentials const in wf_authenticate_fn (breaking change) --- include/webfuse/adapter/authenticate.h | 2 +- test/webfuse/mocks/mock_authenticator.cc | 6 +++--- test/webfuse/mocks/mock_authenticator.hpp | 10 +++++----- test/webfuse/tests/adapter/test_server_config.cc | 2 +- test/webfuse/tests/integration/test_lowlevel.cc | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/webfuse/adapter/authenticate.h b/include/webfuse/adapter/authenticate.h index e11e300..0146768 100644 --- a/include/webfuse/adapter/authenticate.h +++ b/include/webfuse/adapter/authenticate.h @@ -31,7 +31,7 @@ struct wf_credentials; /// \see wf_server_protocol_add_authenticator //------------------------------------------------------------------------------ typedef bool wf_authenticate_fn( - struct wf_credentials * credentials, + struct wf_credentials const * credentials, void * user_data); #ifdef __cplusplus diff --git a/test/webfuse/mocks/mock_authenticator.cc b/test/webfuse/mocks/mock_authenticator.cc index 35648ed..4aee1bb 100644 --- a/test/webfuse/mocks/mock_authenticator.cc +++ b/test/webfuse/mocks/mock_authenticator.cc @@ -23,17 +23,17 @@ void set_authenticator(size_t i, Authenticator * authenticator) g_authenticators[i] = authenticator; } -bool authenticate(struct wf_credentials * creds, void * user_data) +bool authenticate(struct wf_credentials const * creds, void * user_data) { return g_authenticators[0]->authenticate(creds, user_data); } -bool authenticate_1(struct wf_credentials * creds, void * user_data) +bool authenticate_1(struct wf_credentials const * creds, void * user_data) { return g_authenticators[1]->authenticate(creds, user_data); } -bool authenticate_2(struct wf_credentials * creds, void * user_data) +bool authenticate_2(struct wf_credentials const * creds, void * user_data) { return g_authenticators[2]->authenticate(creds, user_data); } diff --git a/test/webfuse/mocks/mock_authenticator.hpp b/test/webfuse/mocks/mock_authenticator.hpp index bb38be3..ac7f2bf 100644 --- a/test/webfuse/mocks/mock_authenticator.hpp +++ b/test/webfuse/mocks/mock_authenticator.hpp @@ -12,22 +12,22 @@ class Authenticator public: virtual ~Authenticator() { } virtual bool authenticate( - struct wf_credentials * credentials, + struct wf_credentials const * credentials, void * user_data) = 0; }; class MockAuthenticator: public Authenticator { public: - MOCK_METHOD2(authenticate, bool (struct wf_credentials * credentials, void * user_data)); + MOCK_METHOD2(authenticate, bool (struct wf_credentials const * credentials, void * user_data)); }; void set_authenticator(Authenticator * authenticator); void set_authenticator(size_t index, Authenticator * authenticator); -bool authenticate(struct wf_credentials * creds, void * user_data); -bool authenticate_1(struct wf_credentials * creds, void * user_data); -bool authenticate_2(struct wf_credentials * creds, void * user_data); +bool authenticate(struct wf_credentials const * creds, void * user_data); +bool authenticate_1(struct wf_credentials const * creds, void * user_data); +bool authenticate_2(struct wf_credentials const * creds, void * user_data); } diff --git a/test/webfuse/tests/adapter/test_server_config.cc b/test/webfuse/tests/adapter/test_server_config.cc index b9687be..ceec97d 100644 --- a/test/webfuse/tests/adapter/test_server_config.cc +++ b/test/webfuse/tests/adapter/test_server_config.cc @@ -20,7 +20,7 @@ wf_mountpoint * create_mountpoint( } bool authenticate( - wf_credentials * credentials, + wf_credentials const * credentials, void * user_data) { (void) credentials; diff --git a/test/webfuse/tests/integration/test_lowlevel.cc b/test/webfuse/tests/integration/test_lowlevel.cc index 050b23a..0c49192 100644 --- a/test/webfuse/tests/integration/test_lowlevel.cc +++ b/test/webfuse/tests/integration/test_lowlevel.cc @@ -37,7 +37,7 @@ wf_test_integration_lowlevel_on_disconnected( bool wf_test_integration_lowlevel_authenticate( - struct wf_credentials * credentials, + struct wf_credentials const * credentials, void * ) { char const * username = wf_credentials_get(credentials, "username"); From f60079dadbf9e60f6e1f56727583b317d99156e0 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Wed, 10 Jun 2020 22:19:22 +0200 Subject: [PATCH 03/37] change: make credentials const in wf_authenticate_fn (breaking change) --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index 51379dc..0619d3b 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,7 @@ ### Breaking Changes * Remove CMake support (change build system to meson) +* Make argument credentials const in wf_authenticate_fn ### Fixes From 81fd41f46af63ba5e6ce78cdeaafd764e4b299a1 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Wed, 10 Jun 2020 22:42:26 +0200 Subject: [PATCH 04/37] feature: added wf_credentials_set_type and wf_credentials_add --- include/webfuse/adapter/client_credentials.h | 28 --------------- include/webfuse/adapter/credentials.h | 22 ++++++++++++ include/webfuse_adapter.h | 1 - lib/webfuse/adapter/api.c | 36 ++++++++----------- lib/webfuse/adapter/impl/credentials.c | 24 +++++++++++++ lib/webfuse/adapter/impl/credentials.h | 12 +++++++ test/webfuse/tests/adapter/test_client.cc | 10 +++--- .../webfuse/tests/adapter/test_credentials.cc | 28 +++++++++++++++ 8 files changed, 106 insertions(+), 55 deletions(-) delete mode 100644 include/webfuse/adapter/client_credentials.h diff --git a/include/webfuse/adapter/client_credentials.h b/include/webfuse/adapter/client_credentials.h deleted file mode 100644 index f673317..0000000 --- a/include/webfuse/adapter/client_credentials.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef WF_ADAPTER_CLIENT_CREDENTIALS_H -#define WF_ADAPTER_CLIENT_CREDENTIALS_H - -#include "webfuse/adapter/api.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - -struct wf_client_credentials; - -extern WF_API void -wf_client_credentials_set_type( - struct wf_client_credentials * credentials, - char const * type); - -extern WF_API void -wf_client_credentials_add( - struct wf_client_credentials * credentials, - char const * key, - char const * value); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/include/webfuse/adapter/credentials.h b/include/webfuse/adapter/credentials.h index e3e65fc..ca404f7 100644 --- a/include/webfuse/adapter/credentials.h +++ b/include/webfuse/adapter/credentials.h @@ -65,6 +65,28 @@ extern WF_API char const * wf_credentials_get( struct wf_credentials const * credentials, char const * key); +//------------------------------------------------------------------------------ +/// \brief Sets the type of the credentials. +/// +/// \param credentials Pointer to credentials object. +/// \param type Type of credentials. +//------------------------------------------------------------------------------ +extern WF_API void wf_credentials_set_type( + struct wf_credentials * credentials, + char const * type); + +//------------------------------------------------------------------------------ +/// \brief Adds an item to credentials +/// +/// \param credentials Pointer to credentials object. +/// \param key String to identify the item. +/// \param key Value of the item. +//------------------------------------------------------------------------------ +extern WF_API void wf_credentials_add( + struct wf_credentials * credentials, + char const * key, + char const * value); + #ifdef __cplusplus } #endif diff --git a/include/webfuse_adapter.h b/include/webfuse_adapter.h index da9533c..954f988 100644 --- a/include/webfuse_adapter.h +++ b/include/webfuse_adapter.h @@ -18,7 +18,6 @@ #include #include -#include #endif diff --git a/lib/webfuse/adapter/api.c b/lib/webfuse/adapter/api.c index 66288e6..613aaf7 100644 --- a/lib/webfuse/adapter/api.c +++ b/lib/webfuse/adapter/api.c @@ -147,6 +147,21 @@ char const * wf_credentials_get( return wf_impl_credentials_get(credentials, key); } +void wf_credentials_set_type( + struct wf_credentials * credentials, + char const * type) +{ + wf_impl_credentials_set_type(credentials, type); +} + +void wf_credentials_add( + struct wf_credentials * credentials, + char const * key, + char const * value) +{ + wf_impl_credentials_add(credentials, key, value); +} + // mountpoint struct wf_mountpoint * @@ -255,24 +270,3 @@ wf_client_add_filesystem( (void) name; } -// client credentials - -void -wf_client_credentials_set_type( - struct wf_client_credentials * credentials, - char const * type) -{ - (void) credentials; - (void) type; -} - -void -wf_client_credentials_add( - struct wf_client_credentials * credentials, - char const * key, - char const * value) -{ - (void) credentials; - (void) key; - (void) value; -} \ No newline at end of file diff --git a/lib/webfuse/adapter/impl/credentials.c b/lib/webfuse/adapter/impl/credentials.c index 040638a..d4c9461 100644 --- a/lib/webfuse/adapter/impl/credentials.c +++ b/lib/webfuse/adapter/impl/credentials.c @@ -1,6 +1,13 @@ #include "webfuse/adapter/impl/credentials.h" #include +void wf_impl_credentials_init_default( + struct wf_credentials * credentials) +{ + credentials->type = NULL; + credentials->data = json_object(); +} + void wf_impl_credentials_init( struct wf_credentials * credentials, char const * type, @@ -38,3 +45,20 @@ char const * wf_impl_credentials_get( return result; } + +void wf_impl_credentials_set_type( + struct wf_credentials * credentials, + char const * type) +{ + free(credentials->type); + credentials->type = strdup(type); +} + +void wf_impl_credentials_add( + struct wf_credentials * credentials, + char const * key, + char const * value) +{ + json_object_set_new(credentials->data, key, json_string(value)); +} + diff --git a/lib/webfuse/adapter/impl/credentials.h b/lib/webfuse/adapter/impl/credentials.h index a2cd647..59b4c0c 100644 --- a/lib/webfuse/adapter/impl/credentials.h +++ b/lib/webfuse/adapter/impl/credentials.h @@ -19,6 +19,9 @@ extern void wf_impl_credentials_init( char const * type, json_t * data); +extern void wf_impl_credentials_init_default( + struct wf_credentials * credentials); + extern void wf_impl_credentials_cleanup( struct wf_credentials * credentials); @@ -29,6 +32,15 @@ extern char const * wf_impl_credentials_get( struct wf_credentials const * credentials, char const * key); +extern void wf_impl_credentials_set_type( + struct wf_credentials * credentials, + char const * type); + +extern void wf_impl_credentials_add( + struct wf_credentials * credentials, + char const * key, + char const * value); + #ifdef __cplusplus } #endif diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index d972447..d64e57b 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -1,7 +1,7 @@ #include #include "webfuse/adapter/client.h" -#include "webfuse/adapter/client_credentials.h" +#include "webfuse/adapter/credentials.h" namespace { @@ -43,10 +43,10 @@ void callback( break; case WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS: { - auto * credentials = reinterpret_cast(args); - wf_client_credentials_set_type(credentials, "username"); - wf_client_credentials_add(credentials, "user", "bob"); - wf_client_credentials_add(credentials, "password", "secret"); + auto * credentials = reinterpret_cast(args); + wf_credentials_set_type(credentials, "username"); + wf_credentials_add(credentials, "user", "bob"); + wf_credentials_add(credentials, "password", "secret"); } break; case WF_CLIENT_FILESYSTEM_ADDED: diff --git a/test/webfuse/tests/adapter/test_credentials.cc b/test/webfuse/tests/adapter/test_credentials.cc index 88e2552..2e11a79 100644 --- a/test/webfuse/tests/adapter/test_credentials.cc +++ b/test/webfuse/tests/adapter/test_credentials.cc @@ -83,3 +83,31 @@ TEST(Credentials, FailedToGetWrongElementDataType) wf_impl_credentials_cleanup(&creds); json_decref(data); } + +TEST(Credentials, SetType) +{ + struct wf_credentials creds; + wf_impl_credentials_init_default(&creds); + + wf_credentials_set_type(&creds, "username"); + ASSERT_STREQ("username", wf_credentials_type(&creds)); + + wf_impl_credentials_cleanup(&creds); +} + +TEST(Credentials, Add) +{ + struct wf_credentials creds; + wf_impl_credentials_init_default(&creds); + + wf_credentials_add(&creds, "a.value", "a"); + ASSERT_STREQ("a", wf_credentials_get(&creds, "a.value")); + + wf_credentials_add(&creds, "b.value", "b"); + ASSERT_STREQ("b", wf_credentials_get(&creds, "b.value")); + + wf_credentials_add(&creds, "a.value", "A"); + ASSERT_STREQ("A", wf_credentials_get(&creds, "a.value")); + + wf_impl_credentials_cleanup(&creds); +} From dcbe4f075a4658cab475ec50f058c2e2f20b56f7 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Thu, 11 Jun 2020 09:10:14 +0200 Subject: [PATCH 05/37] added stub of client implementation --- include/webfuse/adapter/client.h | 21 +----- include/webfuse/adapter/client_callback.h | 35 +++++++++ include/webfuse_adapter.h | 1 + lib/webfuse/adapter/api.c | 27 +++---- lib/webfuse/adapter/impl/client.c | 87 +++++++++++++++++++++++ lib/webfuse/adapter/impl/client.h | 57 +++++++++++++++ meson.build | 1 + 7 files changed, 194 insertions(+), 35 deletions(-) create mode 100644 include/webfuse/adapter/client_callback.h create mode 100644 lib/webfuse/adapter/impl/client.c create mode 100644 lib/webfuse/adapter/impl/client.h diff --git a/include/webfuse/adapter/client.h b/include/webfuse/adapter/client.h index 2e8273f..1971106 100644 --- a/include/webfuse/adapter/client.h +++ b/include/webfuse/adapter/client.h @@ -6,33 +6,16 @@ #ifndef WF_ADAPTER_CLIENT_H #define WF_ADAPTER_CLIENT_H -#include "webfuse/adapter/api.h" +#include +#include #ifdef __cplusplus extern "C" { #endif -#define WF_CLIENT_CREATED 0x0001 -#define WF_CLIENT_DISPOSING 0x0002 - -#define WF_CLIENT_CONNECTED 0x0011 -#define WF_CLIENT_DISCONNECTED 0x0012 - -#define WF_CLIENT_AUTHENTICATED 0x0021 -#define WF_CLIENT_AUTHENTICATION_FAILED 0x0022 -#define WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS 0x0023 - -#define WF_CLIENT_FILESYSTEM_ADDED 0x0031 -#define WF_CLIENT_FILESYSTEM_ADD_FAILED 0x0032 - struct wf_client; -typedef void wf_client_callback_fn( - struct wf_client * client, - int reason, - void * args); - extern WF_API struct wf_client * wf_client_create( wf_client_callback_fn * callback, diff --git a/include/webfuse/adapter/client_callback.h b/include/webfuse/adapter/client_callback.h new file mode 100644 index 0000000..b603a1c --- /dev/null +++ b/include/webfuse/adapter/client_callback.h @@ -0,0 +1,35 @@ +#ifndef WF_ADAPTER_CLIENT_CALLBACK_H +#define WF_ADAPTER_CLIENT_CALLBACK_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + +#define WF_CLIENT_CREATED 0x0001 +#define WF_CLIENT_DISPOSING 0x0002 + +#define WF_CLIENT_CONNECTED 0x0011 +#define WF_CLIENT_DISCONNECTED 0x0012 + +#define WF_CLIENT_AUTHENTICATED 0x0021 +#define WF_CLIENT_AUTHENTICATION_FAILED 0x0022 +#define WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS 0x0023 + +#define WF_CLIENT_FILESYSTEM_ADDED 0x0031 +#define WF_CLIENT_FILESYSTEM_ADD_FAILED 0x0032 + +struct wf_client; + +typedef void wf_client_callback_fn( + struct wf_client * client, + int reason, + void * args); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/include/webfuse_adapter.h b/include/webfuse_adapter.h index 954f988..a198e8b 100644 --- a/include/webfuse_adapter.h +++ b/include/webfuse_adapter.h @@ -18,6 +18,7 @@ #include #include +#include #endif diff --git a/lib/webfuse/adapter/api.c b/lib/webfuse/adapter/api.c index 613aaf7..382a6c4 100644 --- a/lib/webfuse/adapter/api.c +++ b/lib/webfuse/adapter/api.c @@ -8,6 +8,8 @@ #include "webfuse/core/util.h" +#include "webfuse/adapter/impl/client.h" + // server struct wf_server * wf_server_create( @@ -201,39 +203,35 @@ wf_client_create( wf_client_callback_fn * callback, void * user_data) { - (void) callback; - (void) user_data; - - return NULL; + return wf_impl_client_create(callback, user_data); } void wf_client_dispose( struct wf_client * client) { - (void) client; + wf_impl_client_dispose(client); } void * wf_client_get_userdata( struct wf_client * client) { - (void) client; - return NULL; + return wf_impl_client_get_userdata(client); } void wf_client_service( struct wf_client * client) { - (void) client; + wf_impl_client_service(client); } void wf_client_interrupt( struct wf_client * client) { - (void) client; + wf_impl_client_interrupt(client); } void @@ -241,22 +239,21 @@ wf_client_connect( struct wf_client * client, char const * url) { - (void) client; - (void) url; + wf_impl_client_connect(client, url); } void wf_client_disconnect( struct wf_client * client) { - (void) client; + wf_impl_client_disconnect(client); } void wf_client_authenticate( struct wf_client * client) { - (void) client; + wf_impl_client_authenticate(client); } void @@ -265,8 +262,6 @@ wf_client_add_filesystem( char const * local_path, char const * name) { - (void) client; - (void) local_path; - (void) name; + wf_impl_client_add_filesystem(client, local_path, name); } diff --git a/lib/webfuse/adapter/impl/client.c b/lib/webfuse/adapter/impl/client.c new file mode 100644 index 0000000..cefe39c --- /dev/null +++ b/lib/webfuse/adapter/impl/client.c @@ -0,0 +1,87 @@ +#include "webfuse/adapter/impl/client.h" + +#include + +struct wf_client +{ + wf_client_callback_fn * callback; + void * user_data; +}; + +struct wf_client * +wf_impl_client_create( + wf_client_callback_fn * callback, + void * user_data) +{ + struct wf_client * client = malloc(sizeof(struct wf_client)); + client->callback = callback; + client->user_data = user_data; + + client->callback(client, WF_CLIENT_CREATED, NULL); + + return client; +} + +void +wf_impl_client_dispose( + struct wf_client * client) +{ + client->callback(client, WF_CLIENT_DISPOSING, NULL); + free(client); +} + +void * +wf_impl_client_get_userdata( + struct wf_client * client) +{ + return client->user_data; +} + +void +wf_impl_client_service( + struct wf_client * client) +{ + (void) client; +} + +void +wf_impl_client_interrupt( + struct wf_client * client) +{ + (void) client; +} + +void +wf_impl_client_connect( + struct wf_client * client, + char const * url) +{ + (void) url; + client->callback(client, WF_CLIENT_DISCONNECTED, NULL); +} + +void +wf_impl_client_disconnect( + struct wf_client * client) +{ + (void) client; +} + +void +wf_impl_client_authenticate( + struct wf_client * client) +{ + client->callback(client, WF_CLIENT_AUTHENTICATION_FAILED, NULL); +} + +void +wf_impl_client_add_filesystem( + struct wf_client * client, + char const * local_path, + char const * name) +{ + (void) local_path; + (void) name; + + client->callback(client, WF_CLIENT_FILESYSTEM_ADD_FAILED, NULL); +} diff --git a/lib/webfuse/adapter/impl/client.h b/lib/webfuse/adapter/impl/client.h new file mode 100644 index 0000000..62d1299 --- /dev/null +++ b/lib/webfuse/adapter/impl/client.h @@ -0,0 +1,57 @@ +#ifndef WF_ADAPTER_IMPL_CLIENT_H +#define WF_ADAPTER_IMPL_CLIENT_H + + +#include "webfuse/adapter/client_callback.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern struct wf_client * +wf_impl_client_create( + wf_client_callback_fn * callback, + void * user_data); + +extern void +wf_impl_client_dispose( + struct wf_client * client); + +extern void * +wf_impl_client_get_userdata( + struct wf_client * client); + +extern void +wf_impl_client_service( + struct wf_client * client); + +extern void +wf_impl_client_interrupt( + struct wf_client * client); + +extern void +wf_impl_client_connect( + struct wf_client * client, + char const * url); + +extern void +wf_impl_client_disconnect( + struct wf_client * client); + +extern void +wf_impl_client_authenticate( + struct wf_client * client); + +extern void +wf_impl_client_add_filesystem( + struct wf_client * client, + char const * local_path, + char const * name); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/meson.build b/meson.build index 42edd6f..4f58ab0 100644 --- a/meson.build +++ b/meson.build @@ -132,6 +132,7 @@ webfuse_adapter_static = static_library('webfuse_adapter', 'lib/webfuse/adapter/impl/operation/open.c', 'lib/webfuse/adapter/impl/operation/close.c', 'lib/webfuse/adapter/impl/operation/read.c', + 'lib/webfuse/adapter/impl/client.c', c_args: ['-fvisibility=hidden'], include_directories: private_inc_dir, dependencies: [webfuse_core_dep, libfuse_dep]) From 06a24e09da2724ea02b201e4afafb1096d42f38e Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Thu, 11 Jun 2020 18:07:42 +0200 Subject: [PATCH 06/37] added wf_client_tlsconfig --- include/webfuse/adapter/client_callback.h | 7 +- include/webfuse/adapter/client_tlsconfig.h | 32 +++++++++ include/webfuse/core/protocol_names.h | 12 ++++ include/webfuse_adapter.h | 1 + lib/webfuse/adapter/api.c | 26 +++++++ lib/webfuse/adapter/impl/client.c | 58 ++++++++++++++-- lib/webfuse/adapter/impl/client_protocol.c | 55 +++++++++++++++ lib/webfuse/adapter/impl/client_protocol.h | 51 ++++++++++++++ lib/webfuse/adapter/impl/client_tlsconfig.c | 55 +++++++++++++++ lib/webfuse/adapter/impl/client_tlsconfig.h | 52 ++++++++++++++ meson.build | 3 + .../tests/adapter/test_client_tlsconfig.cc | 69 +++++++++++++++++++ 12 files changed, 412 insertions(+), 9 deletions(-) create mode 100644 include/webfuse/adapter/client_tlsconfig.h create mode 100644 lib/webfuse/adapter/impl/client_protocol.c create mode 100644 lib/webfuse/adapter/impl/client_protocol.h create mode 100644 lib/webfuse/adapter/impl/client_tlsconfig.c create mode 100644 lib/webfuse/adapter/impl/client_tlsconfig.h create mode 100644 test/webfuse/tests/adapter/test_client_tlsconfig.cc diff --git a/include/webfuse/adapter/client_callback.h b/include/webfuse/adapter/client_callback.h index b603a1c..90aabab 100644 --- a/include/webfuse/adapter/client_callback.h +++ b/include/webfuse/adapter/client_callback.h @@ -7,8 +7,9 @@ extern "C" #endif -#define WF_CLIENT_CREATED 0x0001 -#define WF_CLIENT_DISPOSING 0x0002 +#define WF_CLIENT_INIT 0x0001 +#define WF_CLIENT_CLEANUP 0x0002 +#define WF_CLIENT_CREATED 0x0003 #define WF_CLIENT_CONNECTED 0x0011 #define WF_CLIENT_DISCONNECTED 0x0012 @@ -20,6 +21,8 @@ extern "C" #define WF_CLIENT_FILESYSTEM_ADDED 0x0031 #define WF_CLIENT_FILESYSTEM_ADD_FAILED 0x0032 +#define WF_CLIENT_GET_TLS_CONFIG 0x0041 + struct wf_client; typedef void wf_client_callback_fn( diff --git a/include/webfuse/adapter/client_tlsconfig.h b/include/webfuse/adapter/client_tlsconfig.h new file mode 100644 index 0000000..c2b5298 --- /dev/null +++ b/include/webfuse/adapter/client_tlsconfig.h @@ -0,0 +1,32 @@ +#ifndef WF_ADAPTER_CLIENT_TLSCONFIG_H +#define WF_ADAPTER_CLIENT_TLSCONFIG_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_client_tlsconfig; + +extern WF_API void +wf_client_tlsconfig_set_keypath( + struct wf_client_tlsconfig * config, + char const * key_path); + +extern WF_API void +wf_client_tlsconfig_set_certpath( + struct wf_client_tlsconfig * config, + char const * cert_path); + +extern WF_API void +wf_client_tlsconfig_set_cafilepath( + struct wf_client_tlsconfig * config, + char const * cafile_path); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/webfuse/core/protocol_names.h b/include/webfuse/core/protocol_names.h index 4de8c4a..aa9abfe 100644 --- a/include/webfuse/core/protocol_names.h +++ b/include/webfuse/core/protocol_names.h @@ -12,10 +12,22 @@ //------------------------------------------------------------------------------ #define WF_PROTOCOL_NAME_ADAPTER_SERVER ("webfuse-adapter-server") +//------------------------------------------------------------------------------ +/// \def WF_PROTOCOL_NAME_ADAPTER_CLIENT +/// \brief Name of the websocket protocol an adapter client is running. +//------------------------------------------------------------------------------ +#define WF_PROTOCOL_NAME_ADAPTER_CLIENT ("webfuse-adapter-client") + //------------------------------------------------------------------------------ /// \def WF_PROTOCOL_NAME_PROVIDER_CLIENT /// \brief Name of the websocket protocol an provider client is running. //------------------------------------------------------------------------------ #define WF_PROTOCOL_NAME_PROVIDER_CLIENT ("webfuse-provider-client") +//------------------------------------------------------------------------------ +/// \def WF_PROTOCOL_NAME_PROVIDER_SERVER +/// \brief Name of the websocket protocol an provider server is running. +//------------------------------------------------------------------------------ +#define WF_PROTOCOL_NAME_PROVIDER_SERVER ("webfuse-provider-server") + #endif diff --git a/include/webfuse_adapter.h b/include/webfuse_adapter.h index a198e8b..093fd06 100644 --- a/include/webfuse_adapter.h +++ b/include/webfuse_adapter.h @@ -19,6 +19,7 @@ #include #include +#include #endif diff --git a/lib/webfuse/adapter/api.c b/lib/webfuse/adapter/api.c index 382a6c4..cdef689 100644 --- a/lib/webfuse/adapter/api.c +++ b/lib/webfuse/adapter/api.c @@ -9,6 +9,7 @@ #include "webfuse/core/util.h" #include "webfuse/adapter/impl/client.h" +#include "webfuse/adapter/impl/client_tlsconfig.h" // server @@ -265,3 +266,28 @@ wf_client_add_filesystem( wf_impl_client_add_filesystem(client, local_path, name); } +// client_tlsconfig + +void +wf_client_tlsconfig_set_keypath( + struct wf_client_tlsconfig * config, + char const * key_path) +{ + wf_impl_client_tlsconfig_set_keypath(config, key_path); +} + +void +wf_client_tlsconfig_set_certpath( + struct wf_client_tlsconfig * config, + char const * cert_path) +{ + wf_impl_client_tlsconfig_set_certpath(config, cert_path); +} + +void +wf_client_tlsconfig_set_cafilepath( + struct wf_client_tlsconfig * config, + char const * cafile_path) +{ + wf_impl_client_tlsconfig_set_cafilepath(config, cafile_path); +} diff --git a/lib/webfuse/adapter/impl/client.c b/lib/webfuse/adapter/impl/client.c index cefe39c..58f00b5 100644 --- a/lib/webfuse/adapter/impl/client.c +++ b/lib/webfuse/adapter/impl/client.c @@ -1,10 +1,22 @@ #include "webfuse/adapter/impl/client.h" +#include "webfuse/adapter/impl/client_protocol.h" +#include "webfuse/adapter/impl/client_tlsconfig.h" +#include "webfuse/core/lws_log.h" + +#include #include +#include + +#define WF_CLIENT_PROTOCOL_COUNT 2 struct wf_client { - wf_client_callback_fn * callback; + struct wf_client_protocol protocol; + struct lws_context_creation_info info; + struct lws_protocols protocols[WF_CLIENT_PROTOCOL_COUNT]; + struct wf_client_tlsconfig tls; + struct lws_context * context; void * user_data; }; @@ -13,12 +25,42 @@ wf_impl_client_create( wf_client_callback_fn * callback, void * user_data) { + wf_lwslog_disable(); + struct wf_client * client = malloc(sizeof(struct wf_client)); - client->callback = callback; + wf_impl_client_tlsconfig_init(&client->tls); client->user_data = user_data; + wf_impl_client_protocol_init(&client->protocol, + (wf_client_callback_fn*) callback, (void*) client); - client->callback(client, WF_CLIENT_CREATED, NULL); + memset(client->protocols, 0, sizeof(struct lws_protocols) * WF_CLIENT_PROTOCOL_COUNT); + wf_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; + + wf_impl_client_protocol_callback(&client->protocol, WF_CLIENT_GET_TLS_CONFIG, &client->tls); + if (wf_impl_client_tlsconfig_isset(&client->tls)) + { + client->info.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS; + } + + client->context = lws_create_context(&client->info); + + if (wf_impl_client_tlsconfig_isset(&client->tls)) + { + 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 = client->tls.cert_path; + client->info.client_ssl_private_key_filepath = client->tls.key_path; + client->info.client_ssl_ca_filepath = client->tls.cafile_path; + lws_init_vhost_client_ssl(&client->info, vhost); + } + + wf_impl_client_protocol_callback(&client->protocol, WF_CLIENT_CREATED ,NULL); return client; } @@ -26,7 +68,9 @@ void wf_impl_client_dispose( struct wf_client * client) { - client->callback(client, WF_CLIENT_DISPOSING, NULL); + lws_context_destroy(client->context); + wf_impl_client_protocol_cleanup(&client->protocol); + wf_impl_client_tlsconfig_cleanup(&client->tls); free(client); } @@ -57,7 +101,7 @@ wf_impl_client_connect( char const * url) { (void) url; - client->callback(client, WF_CLIENT_DISCONNECTED, NULL); + wf_impl_client_protocol_callback(&client->protocol, WF_CLIENT_DISCONNECTED, NULL); } void @@ -71,7 +115,7 @@ void wf_impl_client_authenticate( struct wf_client * client) { - client->callback(client, WF_CLIENT_AUTHENTICATION_FAILED, NULL); + wf_impl_client_protocol_callback(&client->protocol, WF_CLIENT_AUTHENTICATION_FAILED, NULL); } void @@ -83,5 +127,5 @@ wf_impl_client_add_filesystem( (void) local_path; (void) name; - client->callback(client, WF_CLIENT_FILESYSTEM_ADD_FAILED, NULL); + wf_impl_client_protocol_callback(&client->protocol, WF_CLIENT_FILESYSTEM_ADD_FAILED, NULL); } diff --git a/lib/webfuse/adapter/impl/client_protocol.c b/lib/webfuse/adapter/impl/client_protocol.c new file mode 100644 index 0000000..d197ceb --- /dev/null +++ b/lib/webfuse/adapter/impl/client_protocol.c @@ -0,0 +1,55 @@ +#include "webfuse/adapter/impl/client_protocol.h" +#include "webfuse/adapter/client_callback.h" +#include "webfuse/core/protocol_names.h" +#include "webfuse/core/util.h" + +#include +#include + +static int wf_impl_client_protocol_lws_callback( + struct lws * WF_UNUSED_PARAM(wsi), + enum lws_callback_reasons WF_UNUSED_PARAM(reason), + void * WF_UNUSED_PARAM(user), + void * WF_UNUSED_PARAM(in), + size_t WF_UNUSED_PARAM(len)) +{ + return 0; +} + +void +wf_impl_client_protocol_init( + struct wf_client_protocol * protocol, + wf_client_callback_fn * callback, + void * user_data) +{ + protocol->callback = callback; + protocol->user_data = user_data; + protocol->callback(protocol->user_data, WF_CLIENT_INIT, NULL); +} + +void +wf_impl_client_protocol_cleanup( + struct wf_client_protocol * protocol) +{ + protocol->callback(protocol->user_data, WF_CLIENT_CLEANUP, NULL); +} + +void +wf_impl_client_protocol_callback( + struct wf_client_protocol * protocol, + int reason, + void * arg) +{ + protocol->callback(protocol->user_data, reason, arg); +} + +void +wf_impl_client_protocol_init_lws( + struct wf_client_protocol * protocol, + struct lws_protocols * lws_protocol) +{ + lws_protocol->name = WF_PROTOCOL_NAME_ADAPTER_CLIENT; + lws_protocol->callback = &wf_impl_client_protocol_lws_callback; + lws_protocol->per_session_data_size = 0; + lws_protocol->user = protocol; +} diff --git a/lib/webfuse/adapter/impl/client_protocol.h b/lib/webfuse/adapter/impl/client_protocol.h new file mode 100644 index 0000000..4f00bb5 --- /dev/null +++ b/lib/webfuse/adapter/impl/client_protocol.h @@ -0,0 +1,51 @@ +#ifndef WF_ADAPTER_IMPL_CLIENT_PROTOCOL_H +#define WF_ADAPTER_IMPL_CLIENT_PROTOCOL_H + +#include "webfuse/adapter/client_callback.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct lws_protocols; + +typedef void +wf_client_protocol_callback_fn( + void * user_data, + int reason, + void * arg); + +struct wf_client_protocol +{ + wf_client_callback_fn * callback; + void * user_data; +}; + +extern void +wf_impl_client_protocol_init( + struct wf_client_protocol * protocol, + wf_client_callback_fn * callback, + void * user_data); + +extern void +wf_impl_client_protocol_cleanup( + struct wf_client_protocol * protocol); + +extern void +wf_impl_client_protocol_callback( + struct wf_client_protocol * protocol, + int reason, + void * arg); + +extern void +wf_impl_client_protocol_init_lws( + struct wf_client_protocol * protocol, + struct lws_protocols * lws_protocol); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/lib/webfuse/adapter/impl/client_tlsconfig.c b/lib/webfuse/adapter/impl/client_tlsconfig.c new file mode 100644 index 0000000..0c1c419 --- /dev/null +++ b/lib/webfuse/adapter/impl/client_tlsconfig.c @@ -0,0 +1,55 @@ +#include "webfuse/adapter/impl/client_tlsconfig.h" +#include +#include + +void +wf_impl_client_tlsconfig_init( + struct wf_client_tlsconfig * config) +{ + config->key_path = NULL; + config->cert_path = NULL; + config->cafile_path = NULL; +} + +void +wf_impl_client_tlsconfig_cleanup( + struct wf_client_tlsconfig * config) +{ + free(config->key_path); + free(config->cert_path); + free(config->cafile_path); +} + +void +wf_impl_client_tlsconfig_set_keypath( + struct wf_client_tlsconfig * config, + char const * key_path) +{ + free(config->key_path); + config->key_path = strdup(key_path); +} + +void +wf_impl_client_tlsconfig_set_certpath( + struct wf_client_tlsconfig * config, + char const * cert_path) +{ + free(config->cert_path); + config->cert_path = strdup(cert_path); +} + +void +wf_impl_client_tlsconfig_set_cafilepath( + struct wf_client_tlsconfig * config, + char const * cafile_path) +{ + free(config->cafile_path); + config->cafile_path = strdup(cafile_path); +} + +bool +wf_impl_client_tlsconfig_isset( + struct wf_client_tlsconfig const * config) +{ + return (NULL != config->cert_path) && (NULL != config->key_path); +} diff --git a/lib/webfuse/adapter/impl/client_tlsconfig.h b/lib/webfuse/adapter/impl/client_tlsconfig.h new file mode 100644 index 0000000..1bbddeb --- /dev/null +++ b/lib/webfuse/adapter/impl/client_tlsconfig.h @@ -0,0 +1,52 @@ +#ifndef WF_ADAPTER_IMPL_CLIENT_TLSCONFIG_H +#define WF_ADAPTER_IMPL_CLIENT_TLSCONFIG_H + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_client_tlsconfig +{ + char * key_path; + char * cert_path; + char * cafile_path; +}; + +extern void +wf_impl_client_tlsconfig_init( + struct wf_client_tlsconfig * config); + +extern void +wf_impl_client_tlsconfig_cleanup( + struct wf_client_tlsconfig * config); + +extern void +wf_impl_client_tlsconfig_set_keypath( + struct wf_client_tlsconfig * config, + char const * key_path); + +extern void +wf_impl_client_tlsconfig_set_certpath( + struct wf_client_tlsconfig * config, + char const * cert_path); + +extern void +wf_impl_client_tlsconfig_set_cafilepath( + struct wf_client_tlsconfig * config, + char const * cafile_path); + +extern bool +wf_impl_client_tlsconfig_isset( + struct wf_client_tlsconfig const * config); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/meson.build b/meson.build index 4f58ab0..76ac684 100644 --- a/meson.build +++ b/meson.build @@ -133,6 +133,8 @@ webfuse_adapter_static = static_library('webfuse_adapter', 'lib/webfuse/adapter/impl/operation/close.c', 'lib/webfuse/adapter/impl/operation/read.c', 'lib/webfuse/adapter/impl/client.c', + 'lib/webfuse/adapter/impl/client_protocol.c', + 'lib/webfuse/adapter/impl/client_tlsconfig.c', c_args: ['-fvisibility=hidden'], include_directories: private_inc_dir, dependencies: [webfuse_core_dep, libfuse_dep]) @@ -252,6 +254,7 @@ alltests = executable('alltests', 'test/webfuse/tests/integration/server.cc', 'test/webfuse/tests/integration/provider.cc', 'test/webfuse/tests/adapter/test_client.cc', + 'test/webfuse/tests/adapter/test_client_tlsconfig.cc', link_args: [ '-Wl,--wrap=wf_timer_manager_create', '-Wl,--wrap=wf_timer_manager_dispose', diff --git a/test/webfuse/tests/adapter/test_client_tlsconfig.cc b/test/webfuse/tests/adapter/test_client_tlsconfig.cc new file mode 100644 index 0000000..788b906 --- /dev/null +++ b/test/webfuse/tests/adapter/test_client_tlsconfig.cc @@ -0,0 +1,69 @@ +#include +#include "webfuse/adapter/client_tlsconfig.h" +#include "webfuse/adapter/impl/client_tlsconfig.h" + +TEST(ClientTlsConfig, InitAndCleanup) +{ + wf_client_tlsconfig config; + + wf_impl_client_tlsconfig_init(&config); + wf_impl_client_tlsconfig_cleanup(&config); +} + +TEST(ClientTlsConfig, SetKeyPath) +{ + wf_client_tlsconfig config; + wf_impl_client_tlsconfig_init(&config); + + wf_client_tlsconfig_set_keypath(&config, "/path/to/key.pem"); + ASSERT_STREQ("/path/to/key.pem", config.key_path); + + wf_impl_client_tlsconfig_cleanup(&config); +} + +TEST(ClientTlsConfig, SetCertPath) +{ + wf_client_tlsconfig config; + wf_impl_client_tlsconfig_init(&config); + + wf_client_tlsconfig_set_certpath(&config, "/path/to/cert.pem"); + ASSERT_STREQ("/path/to/cert.pem", config.cert_path); + + wf_impl_client_tlsconfig_cleanup(&config); +} + +TEST(ClientTlsConfig, SetCafilePath) +{ + wf_client_tlsconfig config; + wf_impl_client_tlsconfig_init(&config); + + wf_client_tlsconfig_set_cafilepath(&config, "/path/to/cafile.pem"); + ASSERT_STREQ("/path/to/cafile.pem", config.cafile_path); + + wf_impl_client_tlsconfig_cleanup(&config); +} + +TEST(ClientTslConfig, IsSet) +{ + wf_client_tlsconfig config; + + wf_impl_client_tlsconfig_init(&config); + ASSERT_FALSE(wf_impl_client_tlsconfig_isset(&config)); + wf_impl_client_tlsconfig_cleanup(&config); + + wf_impl_client_tlsconfig_init(&config); + wf_client_tlsconfig_set_keypath(&config, "/path/to/key.pem"); + ASSERT_FALSE(wf_impl_client_tlsconfig_isset(&config)); + wf_impl_client_tlsconfig_cleanup(&config); + + wf_impl_client_tlsconfig_init(&config); + wf_client_tlsconfig_set_certpath(&config, "/path/to/cert.pem"); + ASSERT_FALSE(wf_impl_client_tlsconfig_isset(&config)); + wf_impl_client_tlsconfig_cleanup(&config); + + wf_impl_client_tlsconfig_init(&config); + wf_client_tlsconfig_set_keypath(&config, "/path/to/key.pem"); + wf_client_tlsconfig_set_certpath(&config, "/path/to/cert.pem"); + ASSERT_TRUE(wf_impl_client_tlsconfig_isset(&config)); + wf_impl_client_tlsconfig_cleanup(&config); +} \ No newline at end of file From f2bbebd670e68b5c8043c99aa4a02e2e15d834f6 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Thu, 11 Jun 2020 19:12:07 +0200 Subject: [PATCH 07/37] moved wf_url to core --- lib/webfuse/adapter/impl/client.c | 4 +- lib/webfuse/adapter/impl/client_protocol.c | 15 ++++++ lib/webfuse/adapter/impl/client_protocol.h | 9 ++++ lib/webfuse/adapter/impl/client_tlsconfig.c | 2 + lib/webfuse/{provider/impl => core}/url.c | 46 +++++++++---------- lib/webfuse/{provider/impl => core}/url.h | 14 +++--- lib/webfuse/provider/impl/client_protocol.c | 8 ++-- meson.build | 4 +- .../tests/{provider => core}/test_url.cc | 44 +++++++++--------- 9 files changed, 86 insertions(+), 60 deletions(-) rename lib/webfuse/{provider/impl => core}/url.c (64%) rename lib/webfuse/{provider/impl => core}/url.h (51%) rename test/webfuse/tests/{provider => core}/test_url.cc (56%) diff --git a/lib/webfuse/adapter/impl/client.c b/lib/webfuse/adapter/impl/client.c index 58f00b5..a088d1c 100644 --- a/lib/webfuse/adapter/impl/client.c +++ b/lib/webfuse/adapter/impl/client.c @@ -85,14 +85,14 @@ void wf_impl_client_service( struct wf_client * client) { - (void) client; + lws_service(client->context, 0); } void wf_impl_client_interrupt( struct wf_client * client) { - (void) client; + lws_cancel_service(client->context); } void diff --git a/lib/webfuse/adapter/impl/client_protocol.c b/lib/webfuse/adapter/impl/client_protocol.c index d197ceb..62d22c9 100644 --- a/lib/webfuse/adapter/impl/client_protocol.c +++ b/lib/webfuse/adapter/impl/client_protocol.c @@ -53,3 +53,18 @@ wf_impl_client_protocol_init_lws( lws_protocol->per_session_data_size = 0; lws_protocol->user = protocol; } + +void +wf_impl_client_protocol_connect( + struct wf_client_protocol * protocol, + char const * url) +{ + +} + +void +wf_impl_client_protocol_disconnect( + struct wf_client_protocol * protocol) +{ + +} diff --git a/lib/webfuse/adapter/impl/client_protocol.h b/lib/webfuse/adapter/impl/client_protocol.h index 4f00bb5..565bf33 100644 --- a/lib/webfuse/adapter/impl/client_protocol.h +++ b/lib/webfuse/adapter/impl/client_protocol.h @@ -43,6 +43,15 @@ wf_impl_client_protocol_init_lws( struct wf_client_protocol * protocol, struct lws_protocols * lws_protocol); +extern void +wf_impl_client_protocol_connect( + struct wf_client_protocol * protocol, + char const * url); + +extern void +wf_impl_client_protocol_disconnect( + struct wf_client_protocol * protocol); + #ifdef __cplusplus } #endif diff --git a/lib/webfuse/adapter/impl/client_tlsconfig.c b/lib/webfuse/adapter/impl/client_tlsconfig.c index 0c1c419..d7b5bce 100644 --- a/lib/webfuse/adapter/impl/client_tlsconfig.c +++ b/lib/webfuse/adapter/impl/client_tlsconfig.c @@ -1,4 +1,6 @@ #include "webfuse/adapter/impl/client_tlsconfig.h" +#include "webfuse/core/url.h" + #include #include diff --git a/lib/webfuse/provider/impl/url.c b/lib/webfuse/core/url.c similarity index 64% rename from lib/webfuse/provider/impl/url.c rename to lib/webfuse/core/url.c index dc6813c..6556362 100644 --- a/lib/webfuse/provider/impl/url.c +++ b/lib/webfuse/core/url.c @@ -1,9 +1,9 @@ -#include "webfuse/provider/impl/url.h" +#include "webfuse/core/url.h" #include #include -struct wfp_impl_url_protocol +struct wf_url_protocol { char const * name; size_t name_length; @@ -11,11 +11,11 @@ struct wfp_impl_url_protocol bool use_tls; }; -static bool wfp_impl_url_readprotocol( - struct wfp_impl_url * url, +static bool wf_url_readprotocol( + struct wf_url * url, char const * * data) { - static struct wfp_impl_url_protocol const known_protocols[] = + static struct wf_url_protocol const known_protocols[] = { {"ws://", 5, 80, false}, {"wss://", 6, 443, true} @@ -25,7 +25,7 @@ static bool wfp_impl_url_readprotocol( bool found = false; for(size_t i = 0; (!found) && (i < count); i++) { - struct wfp_impl_url_protocol const * protocol = &known_protocols[i]; + struct wf_url_protocol const * protocol = &known_protocols[i]; if (0 == strncmp(*data, protocol->name, protocol->name_length)) { url->port = protocol->default_port; @@ -38,8 +38,8 @@ static bool wfp_impl_url_readprotocol( return found; } -static bool wfp_impl_url_readhost( - struct wfp_impl_url * url, +static bool wf_url_readhost( + struct wf_url * url, char const * * data) { char * end = strpbrk(*data, ":/"); @@ -55,8 +55,8 @@ static bool wfp_impl_url_readhost( return result; } -static bool wfp_impl_url_readport( - struct wfp_impl_url * url, +static bool wf_url_readport( + struct wf_url * url, char const * * data) { bool result; @@ -81,8 +81,8 @@ static bool wfp_impl_url_readport( return result; } -static bool wfp_impl_url_readpath( - struct wfp_impl_url * url, +static bool wf_url_readpath( + struct wf_url * url, char const * * data) { bool const result = ('/' == **data); @@ -93,33 +93,33 @@ static bool wfp_impl_url_readpath( } -bool wfp_impl_url_init( - struct wfp_impl_url * url, +bool wf_url_init( + struct wf_url * url, char const * value) { - memset(url, 0, sizeof(struct wfp_impl_url)); + memset(url, 0, sizeof(struct wf_url)); char const * data = value; bool const result = - wfp_impl_url_readprotocol(url, &data) && - wfp_impl_url_readhost(url, &data) && - wfp_impl_url_readport(url, &data) && - wfp_impl_url_readpath(url, &data) + wf_url_readprotocol(url, &data) && + wf_url_readhost(url, &data) && + wf_url_readport(url, &data) && + wf_url_readpath(url, &data) ; if (!result) { - wfp_impl_url_cleanup(url); + wf_url_cleanup(url); } return result; } -void wfp_impl_url_cleanup( - struct wfp_impl_url * url) +void wf_url_cleanup( + struct wf_url * url) { free(url->host); free(url->path); - memset(url, 0, sizeof(struct wfp_impl_url)); + memset(url, 0, sizeof(struct wf_url)); } diff --git a/lib/webfuse/provider/impl/url.h b/lib/webfuse/core/url.h similarity index 51% rename from lib/webfuse/provider/impl/url.h rename to lib/webfuse/core/url.h index 7755e5c..4f75d8d 100644 --- a/lib/webfuse/provider/impl/url.h +++ b/lib/webfuse/core/url.h @@ -1,5 +1,5 @@ -#ifndef WF_PROVIDER_IMPL_URL_H -#define WF_PROVIDER_IMPL_URL_H +#ifndef WF_URL_H +#define WF_URL_H #ifndef __cplusplus #include @@ -9,7 +9,7 @@ extern "C" { #endif -struct wfp_impl_url +struct wf_url { char * host; int port; @@ -17,12 +17,12 @@ struct wfp_impl_url bool use_tls; }; -extern bool wfp_impl_url_init( - struct wfp_impl_url * url, +extern bool wf_url_init( + struct wf_url * url, char const * value); -extern void wfp_impl_url_cleanup( - struct wfp_impl_url * url); +extern void wf_url_cleanup( + struct wf_url * url); #ifdef __cplusplus diff --git a/lib/webfuse/provider/impl/client_protocol.c b/lib/webfuse/provider/impl/client_protocol.c index db78137..7ce8c2e 100644 --- a/lib/webfuse/provider/impl/client_protocol.c +++ b/lib/webfuse/provider/impl/client_protocol.c @@ -13,7 +13,7 @@ #include "webfuse/core/message.h" #include "webfuse/core/message_queue.h" #include "webfuse/core/container_of.h" -#include "webfuse/provider/impl/url.h" +#include "webfuse/core/url.h" #include "webfuse/core/protocol_names.h" #include "webfuse/core/timer/manager.h" @@ -297,8 +297,8 @@ void wfp_impl_client_protocol_connect( struct lws_context * context, char const * url) { - struct wfp_impl_url url_data; - bool const success = wfp_impl_url_init(&url_data, url); + struct wf_url url_data; + bool const success = wf_url_init(&url_data, url); if (success) { struct lws_client_connect_info info; @@ -316,7 +316,7 @@ void wfp_impl_client_protocol_connect( lws_client_connect_via_info(&info); - wfp_impl_url_cleanup(&url_data); + wf_url_cleanup(&url_data); } else { diff --git a/meson.build b/meson.build index 76ac684..b1d4f2d 100644 --- a/meson.build +++ b/meson.build @@ -29,6 +29,7 @@ webfuse_core = static_library('webfuse_core', 'lib/webfuse/core/base64.c', 'lib/webfuse/core/lws_log.c', 'lib/webfuse/core/json_util.c', + 'lib/webfuse/core/url.c', 'lib/webfuse/core/timer/manager.c', 'lib/webfuse/core/timer/timepoint.c', 'lib/webfuse/core/timer/timer.c', @@ -56,7 +57,6 @@ if not without_provider webfuse_provider_static = static_library('webfuse_provider', 'lib/webfuse/provider/api.c', - 'lib/webfuse/provider/impl/url.c', 'lib/webfuse/provider/impl/client.c', 'lib/webfuse/provider/impl/client_config.c', 'lib/webfuse/provider/impl/client_protocol.c', @@ -226,6 +226,7 @@ alltests = executable('alltests', 'test/webfuse/tests/core/test_status.cc', 'test/webfuse/tests/core/test_message.cc', 'test/webfuse/tests/core/test_message_queue.cc', + 'test/webfuse/tests/core/test_url.cc', 'test/webfuse/tests/adapter/test_server.cc', 'test/webfuse/tests/adapter/test_server_config.cc', 'test/webfuse/tests/adapter/test_credentials.cc', @@ -240,7 +241,6 @@ alltests = executable('alltests', 'test/webfuse/tests/adapter/operation/test_readdir.cc', 'test/webfuse/tests/adapter/operation/test_getattr.cc', 'test/webfuse/tests/adapter/operation/test_lookup.cc', - 'test/webfuse/tests/provider/test_url.cc', 'test/webfuse/tests/provider/test_client_protocol.cc', 'test/webfuse/tests/provider/operation/test_close.cc', 'test/webfuse/tests/provider/operation/test_getattr.cc', diff --git a/test/webfuse/tests/provider/test_url.cc b/test/webfuse/tests/core/test_url.cc similarity index 56% rename from test/webfuse/tests/provider/test_url.cc rename to test/webfuse/tests/core/test_url.cc index 7f6a5e7..ba5ba76 100644 --- a/test/webfuse/tests/provider/test_url.cc +++ b/test/webfuse/tests/core/test_url.cc @@ -1,69 +1,69 @@ #include -#include "webfuse/provider/impl/url.h" +#include "webfuse/core/url.h" TEST(url, ParseWs) { - struct wfp_impl_url url; - bool result = wfp_impl_url_init(&url, "ws://localhost/"); + struct wf_url url; + bool result = wf_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_impl_url_cleanup(&url); + wf_url_cleanup(&url); } TEST(url, ParswWss) { - struct wfp_impl_url url; - bool result = wfp_impl_url_init(&url, "wss://localhost/"); + struct wf_url url; + bool result = wf_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_impl_url_cleanup(&url); + wf_url_cleanup(&url); } TEST(url, ParseIPAdress) { - struct wfp_impl_url url; - bool result = wfp_impl_url_init(&url, "ws://127.0.0.1/"); + struct wf_url url; + bool result = wf_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_impl_url_cleanup(&url); + wf_url_cleanup(&url); } TEST(url, ParsePort) { - struct wfp_impl_url url; - bool result = wfp_impl_url_init(&url, "ws://localhost:54321/"); + struct wf_url url; + bool result = wf_url_init(&url, "ws://localhost:54321/"); ASSERT_TRUE(result); ASSERT_EQ(54321, url.port); - wfp_impl_url_cleanup(&url); + wf_url_cleanup(&url); } TEST(url, ParseNonEmptyPath) { - struct wfp_impl_url url; - bool result = wfp_impl_url_init(&url, "ws://localhost/some_path?query"); + struct wf_url url; + bool result = wf_url_init(&url, "ws://localhost/some_path?query"); ASSERT_TRUE(result); ASSERT_STREQ("/some_path?query", url.path); - wfp_impl_url_cleanup(&url); + wf_url_cleanup(&url); } TEST(url, FailToParseUnknownProtocol) { - struct wfp_impl_url url; - bool result = wfp_impl_url_init(&url, "unknown://localhost/"); + struct wf_url url; + bool result = wf_url_init(&url, "unknown://localhost/"); ASSERT_FALSE(result); ASSERT_EQ(0, url.port); ASSERT_EQ(nullptr, url.path); @@ -72,8 +72,8 @@ TEST(url, FailToParseUnknownProtocol) TEST(url, FailToParseMissingProtocol) { - struct wfp_impl_url url; - bool result = wfp_impl_url_init(&url, "unknown"); + struct wf_url url; + bool result = wf_url_init(&url, "unknown"); ASSERT_FALSE(result); ASSERT_EQ(0, url.port); ASSERT_EQ(nullptr, url.path); @@ -82,8 +82,8 @@ TEST(url, FailToParseMissingProtocol) TEST(url, FailToParseMissingPath) { - struct wfp_impl_url url; - bool result = wfp_impl_url_init(&url, "ws://localhost"); + struct wf_url url; + bool result = wf_url_init(&url, "ws://localhost"); ASSERT_FALSE(result); ASSERT_EQ(0, url.port); ASSERT_EQ(nullptr, url.path); From eb48dbecc5565906073209eccc312519d1a072e8 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Thu, 11 Jun 2020 22:57:56 +0200 Subject: [PATCH 08/37] added implementation of wf_client_connect and wf_client_disconnect --- lib/webfuse/adapter/impl/client.c | 5 +- lib/webfuse/adapter/impl/client_protocol.c | 83 ++++++++- lib/webfuse/adapter/impl/client_protocol.h | 9 + lib/webfuse/adapter/impl/client_tlsconfig.c | 1 - meson.build | 1 + test/webfuse/tests/adapter/test_client.cc | 65 ++++++- test/webfuse/utils/threaded_ws_server.cc | 197 ++++++++++++++++++++ test/webfuse/utils/threaded_ws_server.h | 26 +++ 8 files changed, 371 insertions(+), 16 deletions(-) create mode 100644 test/webfuse/utils/threaded_ws_server.cc create mode 100644 test/webfuse/utils/threaded_ws_server.h diff --git a/lib/webfuse/adapter/impl/client.c b/lib/webfuse/adapter/impl/client.c index a088d1c..ed3863d 100644 --- a/lib/webfuse/adapter/impl/client.c +++ b/lib/webfuse/adapter/impl/client.c @@ -100,15 +100,14 @@ wf_impl_client_connect( struct wf_client * client, char const * url) { - (void) url; - wf_impl_client_protocol_callback(&client->protocol, WF_CLIENT_DISCONNECTED, NULL); + wf_impl_client_protocol_connect(&client->protocol, client->context, url); } void wf_impl_client_disconnect( struct wf_client * client) { - (void) client; + wf_impl_client_protocol_disconnect(&client->protocol); } void diff --git a/lib/webfuse/adapter/impl/client_protocol.c b/lib/webfuse/adapter/impl/client_protocol.c index 62d22c9..894ec0d 100644 --- a/lib/webfuse/adapter/impl/client_protocol.c +++ b/lib/webfuse/adapter/impl/client_protocol.c @@ -1,19 +1,56 @@ #include "webfuse/adapter/impl/client_protocol.h" #include "webfuse/adapter/client_callback.h" #include "webfuse/core/protocol_names.h" +#include "webfuse/core/url.h" #include "webfuse/core/util.h" #include #include static int wf_impl_client_protocol_lws_callback( - struct lws * WF_UNUSED_PARAM(wsi), - enum lws_callback_reasons WF_UNUSED_PARAM(reason), + struct lws * wsi, + enum lws_callback_reasons reason, void * WF_UNUSED_PARAM(user), - void * WF_UNUSED_PARAM(in), + void * in, size_t WF_UNUSED_PARAM(len)) { - return 0; + int result = 0; + struct lws_protocols const * ws_protocol = lws_get_protocol(wsi); + struct wf_client_protocol * protocol = (NULL != ws_protocol) ? ws_protocol->user : NULL; + + if (NULL != protocol) + { + switch (reason) + { + case LWS_CALLBACK_CLIENT_ESTABLISHED: + protocol->is_connected = true; + protocol->callback(protocol->user_data, WF_CLIENT_CONNECTED, NULL); + break; + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + protocol->is_connected = false; + protocol->callback(protocol->user_data, WF_CLIENT_DISCONNECTED, NULL); + break; + case LWS_CALLBACK_CLIENT_CLOSED: + protocol->is_connected = false; + protocol->callback(protocol->user_data, WF_CLIENT_DISCONNECTED, NULL); + protocol->wsi = NULL; + break; + case LWS_CALLBACK_SERVER_WRITEABLE: + // fall-through + case LWS_CALLBACK_CLIENT_WRITEABLE: + if (wsi == protocol->wsi) + { + if (protocol->is_shutdown_requested) + { + result = 1; + } + } + default: + break; + } + } + + return result; } void @@ -22,6 +59,9 @@ wf_impl_client_protocol_init( wf_client_callback_fn * callback, void * user_data) { + protocol->is_connected = false, + protocol->is_shutdown_requested = false; + protocol->wsi = NULL; protocol->callback = callback; protocol->user_data = user_data; protocol->callback(protocol->user_data, WF_CLIENT_INIT, NULL); @@ -57,14 +97,47 @@ wf_impl_client_protocol_init_lws( void wf_impl_client_protocol_connect( struct wf_client_protocol * protocol, + struct lws_context * context, char const * url) { + struct wf_url url_data; + bool const success = wf_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 = WF_PROTOCOL_NAME_PROVIDER_SERVER; + info.local_protocol_name = WF_PROTOCOL_NAME_ADAPTER_CLIENT; + info.pwsi = &protocol->wsi; + lws_client_connect_via_info(&info); + wf_url_cleanup(&url_data); + } + else + { + protocol->callback(protocol->user_data, WF_CLIENT_DISCONNECTED, NULL); + } } void wf_impl_client_protocol_disconnect( struct wf_client_protocol * protocol) { - + if (protocol->is_connected) + { + protocol->is_shutdown_requested = true; + lws_callback_on_writable(protocol->wsi); + } + else + { + protocol->callback(protocol->user_data, WF_CLIENT_DISCONNECTED, NULL); + } + } diff --git a/lib/webfuse/adapter/impl/client_protocol.h b/lib/webfuse/adapter/impl/client_protocol.h index 565bf33..29606f4 100644 --- a/lib/webfuse/adapter/impl/client_protocol.h +++ b/lib/webfuse/adapter/impl/client_protocol.h @@ -3,12 +3,17 @@ #include "webfuse/adapter/client_callback.h" +#ifndef __cplusplus +#include +#endif + #ifdef __cplusplus extern "C" { #endif struct lws_protocols; +struct lws_context; typedef void wf_client_protocol_callback_fn( @@ -18,6 +23,9 @@ wf_client_protocol_callback_fn( struct wf_client_protocol { + bool is_connected; + bool is_shutdown_requested; + struct lws * wsi; wf_client_callback_fn * callback; void * user_data; }; @@ -46,6 +54,7 @@ wf_impl_client_protocol_init_lws( extern void wf_impl_client_protocol_connect( struct wf_client_protocol * protocol, + struct lws_context * conext, char const * url); extern void diff --git a/lib/webfuse/adapter/impl/client_tlsconfig.c b/lib/webfuse/adapter/impl/client_tlsconfig.c index d7b5bce..561b4b2 100644 --- a/lib/webfuse/adapter/impl/client_tlsconfig.c +++ b/lib/webfuse/adapter/impl/client_tlsconfig.c @@ -1,5 +1,4 @@ #include "webfuse/adapter/impl/client_tlsconfig.h" -#include "webfuse/core/url.h" #include #include diff --git a/meson.build b/meson.build index b1d4f2d..8d9ffe1 100644 --- a/meson.build +++ b/meson.build @@ -210,6 +210,7 @@ alltests = executable('alltests', 'test/webfuse/utils/path.c', 'test/webfuse/utils/static_filesystem.c', 'test/webfuse/utils/ws_server.cc', + 'test/webfuse/utils/threaded_ws_server.cc', 'test/webfuse/mocks/fake_invokation_context.cc', 'test/webfuse/mocks/mock_authenticator.cc', 'test/webfuse/mocks/mock_request.cc', diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index d64e57b..c548b85 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -2,6 +2,9 @@ #include "webfuse/adapter/client.h" #include "webfuse/adapter/credentials.h" +#include "webfuse/utils/threaded_ws_server.h" + +using webfuse_test::ThreadedWsServer; namespace { @@ -63,23 +66,71 @@ void callback( } } +void callback2( + wf_client * client, + int reason, + void * args) +{ + auto * ctx = reinterpret_cast(wf_client_get_userdata(client)); + + switch (reason) + { + case WF_CLIENT_CREATED: + ctx->state = connection_state::connecting; + break; + case WF_CLIENT_CONNECTED: + ctx->state = connection_state::connected; + break; + case WF_CLIENT_DISCONNECTED: + ctx->state = connection_state::disconnected; + break; + default: + break; + } +} + + } TEST(client, general_usage) { + ThreadedWsServer server(54321); + context ctx; ctx.state = connection_state::connecting; wf_client * client = wf_client_create( &callback, reinterpret_cast(&ctx)); - if (nullptr != client) + while (ctx.state != connection_state::disconnected) { - while (ctx.state != connection_state::disconnected) - { - wf_client_service(client); - } - - wf_client_dispose(client); + wf_client_service(client); } + + wf_client_dispose(client); +} + +TEST(client, connect) +{ + ThreadedWsServer server(54321); + + context ctx; + ctx.state = connection_state::connecting; + + wf_client * client = wf_client_create( + &callback2, reinterpret_cast(&ctx)); + + wf_client_connect(client, "ws://localhost:54321/"); + while (ctx.state != connection_state::connected) + { + wf_client_service(client); + } + + wf_client_disconnect(client); + while (ctx.state != connection_state::disconnected) + { + wf_client_service(client); + } + + wf_client_dispose(client); } \ No newline at end of file diff --git a/test/webfuse/utils/threaded_ws_server.cc b/test/webfuse/utils/threaded_ws_server.cc new file mode 100644 index 0000000..40f2e43 --- /dev/null +++ b/test/webfuse/utils/threaded_ws_server.cc @@ -0,0 +1,197 @@ +#include "webfuse/utils/threaded_ws_server.h" +#include "webfuse/core/protocol_names.h" +#include "webfuse/core/lws_log.h" + +#include +#include +#include +#include +#include + + +#define TIMEOUT (std::chrono::milliseconds(10 * 1000)) + +namespace +{ + +class IServer +{ +public: + virtual ~IServer() = default; + virtual void OnConnected(lws * wsi) = 0; + virtual void OnConnectionClosed(lws * wsi) = 0; +}; + +} + +namespace webfuse_test +{ + +class ThreadedWsServer::Private : IServer +{ + Private(Private const &) = delete; + Private & operator=(Private const &) = delete; +public: + explicit Private(int port); + ~Private(); + void WaitForConnection(); + void OnConnected(lws * wsi) override; + void OnConnectionClosed(lws * wsi) override; + +private: + static void run(Private * self); + 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::condition_variable convar; +}; + +} + +extern "C" +{ + +static int wf_test_utils_threaded_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; + default: + break; + } + } + + return result; +} + +} + + +namespace webfuse_test +{ + +ThreadedWsServer::ThreadedWsServer(int port) +: d(new Private(port)) +{ + +} + +ThreadedWsServer::~ThreadedWsServer() +{ + delete d; +} + +void ThreadedWsServer::WaitForConnection() +{ + d->WaitForConnection(); +} + +ThreadedWsServer::Private::Private(int port) +: port_(port) +, is_connected(false) +, is_shutdown_requested(false) +, wsi_(nullptr) +{ + wf_lwslog_disable(); + IServer * server = this; + memset(ws_protocols, 0, sizeof(struct lws_protocols) * 2 ); + + ws_protocols[0].name = WF_PROTOCOL_NAME_PROVIDER_SERVER; + ws_protocols[0].callback = &wf_test_utils_threaded_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; + + ws_context = lws_create_context(&info); + + context = std::thread(&run, this); +} + +ThreadedWsServer::Private::~Private() +{ + { + std::unique_lock lock(mutex); + is_shutdown_requested = true; + } + + lws_cancel_service(ws_context); + context.join(); + lws_context_destroy(ws_context); +} + +void ThreadedWsServer::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; + } + } +} + +void ThreadedWsServer::Private::WaitForConnection() +{ + std::unique_lock lock(mutex); + while (!is_connected) + { + auto status = convar.wait_for(lock, TIMEOUT); + if (std::cv_status::timeout == status) + { + throw std::runtime_error("timeout"); + } + } +} + +void ThreadedWsServer::Private::OnConnected(lws * wsi) +{ + std::unique_lock lock(mutex); + is_connected = true; + wsi_ = wsi; + convar.notify_all(); +} + +void ThreadedWsServer::Private::OnConnectionClosed(lws * wsi) +{ + std::unique_lock lock(mutex); + if (wsi == wsi_) + { + is_connected = false; + wsi_ = wsi; + convar.notify_all(); + } +} + + +} \ No newline at end of file diff --git a/test/webfuse/utils/threaded_ws_server.h b/test/webfuse/utils/threaded_ws_server.h new file mode 100644 index 0000000..4e908d3 --- /dev/null +++ b/test/webfuse/utils/threaded_ws_server.h @@ -0,0 +1,26 @@ +#ifndef WF_TEST_UTILS_THREADED_WS_SERVER_HPP +#define WF_TEST_UTILS_THREADED_WS_SERVER_HPP + +#include +#include + +namespace webfuse_test +{ + +class ThreadedWsServer +{ + ThreadedWsServer(ThreadedWsServer const &) = delete; + ThreadedWsServer & operator=(ThreadedWsServer const &) = delete; +public: + explicit ThreadedWsServer(int port); + ~ThreadedWsServer(); + void WaitForConnection(); +private: + class Private; + Private * d; +}; + +} + + +#endif From e72e78180e5abd6194f3bdb27f91c644eaeadee2 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Fri, 12 Jun 2020 10:17:17 +0200 Subject: [PATCH 09/37] let system choose port of test server --- test/webfuse/tests/adapter/test_client.cc | 4 ++-- test/webfuse/utils/threaded_ws_server.cc | 19 +++++++++++++++++++ test/webfuse/utils/threaded_ws_server.h | 4 +++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index c548b85..11d30e6 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -112,7 +112,7 @@ TEST(client, general_usage) TEST(client, connect) { - ThreadedWsServer server(54321); + ThreadedWsServer server; context ctx; ctx.state = connection_state::connecting; @@ -120,7 +120,7 @@ TEST(client, connect) wf_client * client = wf_client_create( &callback2, reinterpret_cast(&ctx)); - wf_client_connect(client, "ws://localhost:54321/"); + wf_client_connect(client, server.GetUrl().c_str()); while (ctx.state != connection_state::connected) { wf_client_service(client); diff --git a/test/webfuse/utils/threaded_ws_server.cc b/test/webfuse/utils/threaded_ws_server.cc index 40f2e43..6d1df1b 100644 --- a/test/webfuse/utils/threaded_ws_server.cc +++ b/test/webfuse/utils/threaded_ws_server.cc @@ -7,6 +7,7 @@ #include #include #include +#include #define TIMEOUT (std::chrono::milliseconds(10 * 1000)) @@ -35,6 +36,7 @@ public: explicit Private(int port); ~Private(); void WaitForConnection(); + std::string GetUrl() const; void OnConnected(lws * wsi) override; void OnConnectionClosed(lws * wsi) override; @@ -108,6 +110,12 @@ void ThreadedWsServer::WaitForConnection() d->WaitForConnection(); } +std::string ThreadedWsServer::GetUrl() const +{ + return d->GetUrl(); +} + + ThreadedWsServer::Private::Private(int port) : port_(port) , is_connected(false) @@ -130,9 +138,13 @@ ThreadedWsServer::Private::Private(int port) 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); } @@ -193,5 +205,12 @@ void ThreadedWsServer::Private::OnConnectionClosed(lws * wsi) } } +std::string ThreadedWsServer::Private::GetUrl() const +{ + std::ostringstream stream; + stream << "ws://localhost:" << port_ << "/"; + return stream.str(); +} + } \ No newline at end of file diff --git a/test/webfuse/utils/threaded_ws_server.h b/test/webfuse/utils/threaded_ws_server.h index 4e908d3..127dcf4 100644 --- a/test/webfuse/utils/threaded_ws_server.h +++ b/test/webfuse/utils/threaded_ws_server.h @@ -3,6 +3,7 @@ #include #include +#include namespace webfuse_test { @@ -12,9 +13,10 @@ class ThreadedWsServer ThreadedWsServer(ThreadedWsServer const &) = delete; ThreadedWsServer & operator=(ThreadedWsServer const &) = delete; public: - explicit ThreadedWsServer(int port); + explicit ThreadedWsServer(int port = 0); ~ThreadedWsServer(); void WaitForConnection(); + std::string GetUrl() const; private: class Private; Private * d; From 8a03f16aa5563e82fd0818d114301b330f308baa Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Fri, 12 Jun 2020 13:32:31 +0200 Subject: [PATCH 10/37] allow system to choose port of webfuse server --- changelog.md | 6 +++++- include/webfuse/adapter/server.h | 14 ++++++++++++++ include/webfuse/adapter/server_config.h | 2 ++ lib/webfuse/adapter/api.c | 5 +++++ lib/webfuse/adapter/impl/server.c | 12 +++++++++++- lib/webfuse/adapter/impl/server.h | 3 +++ test/webfuse/tests/integration/server.cc | 16 +++++++++++++++- test/webfuse/tests/integration/server.hpp | 3 +++ .../tests/integration/test_integration.cc | 2 +- 9 files changed, 59 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index 0619d3b..e62c3f8 100644 --- a/changelog.md +++ b/changelog.md @@ -5,7 +5,11 @@ ### Breaking Changes * Remove CMake support (change build system to meson) -* Make argument credentials const in wf_authenticate_fn +* Make argument credentials const in `wf_authenticate_fn` + +### New Features + +* Allow system to choose port of webfuse server (by setting port in `wf_server_config` to 0) ### Fixes diff --git a/include/webfuse/adapter/server.h b/include/webfuse/adapter/server.h index f46f3ef..dabfedd 100644 --- a/include/webfuse/adapter/server.h +++ b/include/webfuse/adapter/server.h @@ -65,6 +65,20 @@ extern WF_API void wf_server_service( extern WF_API void wf_server_interrupt( struct wf_server * server); +//------------------------------------------------------------------------------ +/// \brief Returns the port number used by the server +/// +/// This function can be used to determine the port number of the server, +/// if it was configured to let the system choose a free port. +// +/// \param server pointer to server +/// \return Port number used by the server. +/// +/// \see wf_server_config_set_port +//------------------------------------------------------------------------------ +extern WF_API int wf_server_get_port( + struct wf_server const * server); + #ifdef __cplusplus } #endif diff --git a/include/webfuse/adapter/server_config.h b/include/webfuse/adapter/server_config.h index e140c4d..6e0bfbb 100644 --- a/include/webfuse/adapter/server_config.h +++ b/include/webfuse/adapter/server_config.h @@ -116,6 +116,8 @@ extern WF_API void wf_server_config_set_vhostname( //------------------------------------------------------------------------------ /// \brief Sets the port number of the websockets server. /// +/// Note: Set port number to 0 to let system choose a free port. +/// /// \param config pointer of configuration object /// \param port port number of the websockets server //------------------------------------------------------------------------------ diff --git a/lib/webfuse/adapter/api.c b/lib/webfuse/adapter/api.c index cdef689..38b38c1 100644 --- a/lib/webfuse/adapter/api.c +++ b/lib/webfuse/adapter/api.c @@ -37,6 +37,11 @@ void wf_server_interrupt( wf_impl_server_interrupt(server); } +int wf_server_get_port( + struct wf_server const * server) +{ + return wf_impl_server_get_port(server); +} // server protocol diff --git a/lib/webfuse/adapter/impl/server.c b/lib/webfuse/adapter/impl/server.c index 8f9a01d..441b736 100644 --- a/lib/webfuse/adapter/impl/server.c +++ b/lib/webfuse/adapter/impl/server.c @@ -22,6 +22,7 @@ struct wf_server struct lws_context * context; struct lws_http_mount mount; struct lws_context_creation_info info; + int port; }; static bool wf_impl_server_tls_enabled( @@ -55,6 +56,7 @@ static struct lws_context * wf_impl_server_context_create( server->info.vhost_name = server->config.vhost_name; server->info.ws_ping_pong_interval = 10; server->info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; + server->info.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS; if (NULL == server->config.document_root) { @@ -71,8 +73,11 @@ static struct lws_context * wf_impl_server_context_create( } struct lws_context * const context = lws_create_context(&server->info); - return context; + struct lws_vhost * const vhost = lws_create_vhost(context, &server->info); + server->port = lws_get_vhost_port(vhost); + + return context; } struct wf_server * wf_impl_server_create( @@ -119,3 +124,8 @@ void wf_impl_server_interrupt( lws_cancel_service(server->context); } +extern int wf_impl_server_get_port( + struct wf_server const * server) +{ + return server->port; +} diff --git a/lib/webfuse/adapter/impl/server.h b/lib/webfuse/adapter/impl/server.h index 56dab8d..8216635 100644 --- a/lib/webfuse/adapter/impl/server.h +++ b/lib/webfuse/adapter/impl/server.h @@ -28,6 +28,9 @@ extern void wf_impl_server_service( extern void wf_impl_server_interrupt( struct wf_server * server); +extern int wf_impl_server_get_port( + struct wf_server const * server); + #ifdef __cplusplus } #endif diff --git a/test/webfuse/tests/integration/server.cc b/test/webfuse/tests/integration/server.cc index 50badcc..f175210 100644 --- a/test/webfuse/tests/integration/server.cc +++ b/test/webfuse/tests/integration/server.cc @@ -1,6 +1,7 @@ #include "webfuse/tests/integration/server.hpp" #include #include +#include #include #include #include @@ -59,7 +60,7 @@ public: config = wf_server_config_create(); - wf_server_config_set_port(config, 8080); + wf_server_config_set_port(config, 0); wf_server_config_set_mountpoint_factory(config, &webfuse_test_server_create_mountpoint, reinterpret_cast(base_dir)); @@ -92,6 +93,14 @@ public: return is_shutdown_requested; } + std::string GetUrl(void) const + { + int const port = wf_server_get_port(server); + std::ostringstream stream; + stream << "wss://localhost:" << port << "/"; + return stream.str(); + } + private: void RequestShutdown() { @@ -136,5 +145,10 @@ char const * Server::GetBaseDir(void) const return d->base_dir; } +std::string Server::GetUrl(void) const +{ + return d->GetUrl(); +} + } \ No newline at end of file diff --git a/test/webfuse/tests/integration/server.hpp b/test/webfuse/tests/integration/server.hpp index 64f949d..de7864c 100644 --- a/test/webfuse/tests/integration/server.hpp +++ b/test/webfuse/tests/integration/server.hpp @@ -1,6 +1,8 @@ #ifndef WF_TEST_INTEGRATION_SERVER_HPP #define WF_TEST_INTEGRATION_SERVER_HPP +#include + namespace webfuse_test { @@ -12,6 +14,7 @@ public: void Start(void); void Stop(void); char const * GetBaseDir(void) const; + std::string GetUrl(void) const; private: class Private; Private * d; diff --git a/test/webfuse/tests/integration/test_integration.cc b/test/webfuse/tests/integration/test_integration.cc index e7e8215..ae9033c 100644 --- a/test/webfuse/tests/integration/test_integration.cc +++ b/test/webfuse/tests/integration/test_integration.cc @@ -38,7 +38,7 @@ namespace void SetUp() { server = new Server(); - provider = new Provider("wss://localhost:8080/"); + provider = new Provider(server->GetUrl().c_str()); } void TearDown() From 4d277701cb14ddbede1e1c7ca3311b6c3160cbc1 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Fri, 12 Jun 2020 16:48:15 +0200 Subject: [PATCH 11/37] removed single threaded test server --- meson.build | 1 - test/webfuse/tests/adapter/test_client.cc | 5 +- .../tests/provider/test_client_protocol.cc | 65 +++-- test/webfuse/utils/threaded_ws_server.cc | 141 ++++++++-- test/webfuse/utils/threaded_ws_server.h | 6 +- test/webfuse/utils/ws_server.cc | 266 ------------------ test/webfuse/utils/ws_server.hpp | 29 -- 7 files changed, 171 insertions(+), 342 deletions(-) delete mode 100644 test/webfuse/utils/ws_server.cc delete mode 100644 test/webfuse/utils/ws_server.hpp diff --git a/meson.build b/meson.build index 8d9ffe1..b95bf0a 100644 --- a/meson.build +++ b/meson.build @@ -209,7 +209,6 @@ alltests = executable('alltests', 'test/webfuse/utils/timeout_watcher.cc', 'test/webfuse/utils/path.c', 'test/webfuse/utils/static_filesystem.c', - 'test/webfuse/utils/ws_server.cc', 'test/webfuse/utils/threaded_ws_server.cc', 'test/webfuse/mocks/fake_invokation_context.cc', 'test/webfuse/mocks/mock_authenticator.cc', diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index 11d30e6..086f5a0 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -2,6 +2,7 @@ #include "webfuse/adapter/client.h" #include "webfuse/adapter/credentials.h" +#include "webfuse/core/protocol_names.h" #include "webfuse/utils/threaded_ws_server.h" using webfuse_test::ThreadedWsServer; @@ -94,8 +95,6 @@ void callback2( TEST(client, general_usage) { - ThreadedWsServer server(54321); - context ctx; ctx.state = connection_state::connecting; @@ -112,7 +111,7 @@ TEST(client, general_usage) TEST(client, connect) { - ThreadedWsServer server; + ThreadedWsServer server(WF_PROTOCOL_NAME_PROVIDER_SERVER); context ctx; ctx.state = connection_state::connecting; diff --git a/test/webfuse/tests/provider/test_client_protocol.cc b/test/webfuse/tests/provider/test_client_protocol.cc index 2035b0f..5aa1301 100644 --- a/test/webfuse/tests/provider/test_client_protocol.cc +++ b/test/webfuse/tests/provider/test_client_protocol.cc @@ -3,20 +3,25 @@ #include #include -#include "webfuse/utils/ws_server.hpp" +#include "webfuse/utils/threaded_ws_server.h" #include "webfuse/mocks/mock_provider_client.hpp" +#include "webfuse/core/protocol_names.h" +#include "webfuse/utils/timeout_watcher.hpp" #include #include #include -using webfuse_test::WebsocketServer; +using webfuse_test::ThreadedWsServer; 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 { @@ -27,29 +32,42 @@ class ClientProtocolFixture public: explicit ClientProtocolFixture(IProviderClient& client, bool enableAuthentication = false) { + server = new ThreadedWsServer(WF_PROTOCOL_NAME_ADAPTER_SERVER); + config = wfp_client_config_create(); client.AttachTo(config, enableAuthentication); - protocol = wfp_client_protocol_create(config); - struct lws_protocols client_protocol; - memset(&client_protocol, 0, sizeof(struct lws_protocols)); - wfp_client_protocol_init_lws(protocol, &client_protocol); + memset(protocols, 0, sizeof(struct lws_protocols) * 2); + wfp_client_protocol_init_lws(protocol, protocols); - server = new WebsocketServer(54321, &client_protocol, 1); + 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() { - delete server; + lws_context_destroy(context); wfp_client_protocol_dispose(protocol); wfp_client_config_dispose(config); + delete server; } void Connect() { - wfp_client_protocol_connect(protocol, server->getContext(), "ws://localhost:54321/"); - server->waitForConnection(); + 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() @@ -59,19 +77,28 @@ public: void SendToClient(json_t * request) { - server->sendMessage(request); + server->SendMessage(request); } json_t * ReceiveMessageFromClient() { - return server->receiveMessage(); + 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 = server->receiveMessage(); + json_t * request = ReceiveMessageFromClient(); ASSERT_TRUE(json_is_object(request)); json_t * method = json_object_get(request, "method"); @@ -103,14 +130,14 @@ public: json_t * response = json_object(); json_object_set_new(response, "result", json_object()); json_object_set(response, "id", id); - server->sendMessage(response); + SendToClient(response); json_decref(request); } void AwaitAddFilesystem(std::string& filesystemName) { - json_t * addFilesystemRequest = server->receiveMessage(); + json_t * addFilesystemRequest = ReceiveMessageFromClient(); ASSERT_NE(nullptr, addFilesystemRequest); ASSERT_TRUE(json_is_object(addFilesystemRequest)); @@ -135,15 +162,19 @@ public: json_object_set_new(response, "result", result); json_object_set(response, "id", id); - server->sendMessage(response); + SendToClient(response); json_decref(addFilesystemRequest); } private: - WebsocketServer * server; + ThreadedWsServer * 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) diff --git a/test/webfuse/utils/threaded_ws_server.cc b/test/webfuse/utils/threaded_ws_server.cc index 6d1df1b..a0ea1ae 100644 --- a/test/webfuse/utils/threaded_ws_server.cc +++ b/test/webfuse/utils/threaded_ws_server.cc @@ -1,13 +1,12 @@ #include "webfuse/utils/threaded_ws_server.h" -#include "webfuse/core/protocol_names.h" #include "webfuse/core/lws_log.h" #include #include #include -#include #include #include +#include #define TIMEOUT (std::chrono::milliseconds(10 * 1000)) @@ -21,6 +20,8 @@ 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; }; } @@ -33,15 +34,20 @@ class ThreadedWsServer::Private : IServer Private(Private const &) = delete; Private & operator=(Private const &) = delete; public: - explicit Private(int port); + Private(std::string const & protocol, int port); ~Private(); - void WaitForConnection(); + 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; @@ -51,7 +57,8 @@ private: lws_context_creation_info info; std::thread context; std::mutex mutex; - std::condition_variable convar; + std::queue writeQueue; + std::queue recvQueue; }; } @@ -80,6 +87,15 @@ static int wf_test_utils_threaded_ws_server_callback( 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; } @@ -94,8 +110,8 @@ static int wf_test_utils_threaded_ws_server_callback( namespace webfuse_test { -ThreadedWsServer::ThreadedWsServer(int port) -: d(new Private(port)) +ThreadedWsServer::ThreadedWsServer(std::string const & protocol, int port) +: d(new Private(protocol, port)) { } @@ -105,9 +121,19 @@ ThreadedWsServer::~ThreadedWsServer() delete d; } -void ThreadedWsServer::WaitForConnection() +bool ThreadedWsServer::IsConnected() { - d->WaitForConnection(); + return d->IsConnected(); +} + +void ThreadedWsServer::SendMessage(json_t * message) +{ + d->SendMessage(message); +} + +json_t * ThreadedWsServer::ReceiveMessage() +{ + return d->ReceiveMessage(); } std::string ThreadedWsServer::GetUrl() const @@ -116,8 +142,9 @@ std::string ThreadedWsServer::GetUrl() const } -ThreadedWsServer::Private::Private(int port) -: port_(port) +ThreadedWsServer::Private::Private(std::string const & protocol, int port) +: protocol_(protocol) +, port_(port) , is_connected(false) , is_shutdown_requested(false) , wsi_(nullptr) @@ -126,7 +153,7 @@ ThreadedWsServer::Private::Private(int port) IServer * server = this; memset(ws_protocols, 0, sizeof(struct lws_protocols) * 2 ); - ws_protocols[0].name = WF_PROTOCOL_NAME_PROVIDER_SERVER; + ws_protocols[0].name = protocol_.c_str(); ws_protocols[0].callback = &wf_test_utils_threaded_ws_server_callback; ws_protocols[0].per_session_data_size = 0; ws_protocols[0].user = reinterpret_cast(server); @@ -173,17 +200,10 @@ void ThreadedWsServer::Private::run(Private * self) } } -void ThreadedWsServer::Private::WaitForConnection() +bool ThreadedWsServer::Private::IsConnected() { std::unique_lock lock(mutex); - while (!is_connected) - { - auto status = convar.wait_for(lock, TIMEOUT); - if (std::cv_status::timeout == status) - { - throw std::runtime_error("timeout"); - } - } + return is_connected; } void ThreadedWsServer::Private::OnConnected(lws * wsi) @@ -191,7 +211,6 @@ void ThreadedWsServer::Private::OnConnected(lws * wsi) std::unique_lock lock(mutex); is_connected = true; wsi_ = wsi; - convar.notify_all(); } void ThreadedWsServer::Private::OnConnectionClosed(lws * wsi) @@ -200,11 +219,85 @@ void ThreadedWsServer::Private::OnConnectionClosed(lws * wsi) if (wsi == wsi_) { is_connected = false; - wsi_ = wsi; - convar.notify_all(); + wsi_ = nullptr; } } +void ThreadedWsServer::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 ThreadedWsServer::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 ThreadedWsServer::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 * ThreadedWsServer::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 ThreadedWsServer::Private::GetUrl() const { std::ostringstream stream; diff --git a/test/webfuse/utils/threaded_ws_server.h b/test/webfuse/utils/threaded_ws_server.h index 127dcf4..fe6467e 100644 --- a/test/webfuse/utils/threaded_ws_server.h +++ b/test/webfuse/utils/threaded_ws_server.h @@ -13,10 +13,12 @@ class ThreadedWsServer ThreadedWsServer(ThreadedWsServer const &) = delete; ThreadedWsServer & operator=(ThreadedWsServer const &) = delete; public: - explicit ThreadedWsServer(int port = 0); + ThreadedWsServer(std::string const & protocol, int port = 0); ~ThreadedWsServer(); - void WaitForConnection(); + bool IsConnected(); std::string GetUrl() const; + void SendMessage(json_t * message); + json_t * ReceiveMessage(); private: class Private; Private * d; diff --git a/test/webfuse/utils/ws_server.cc b/test/webfuse/utils/ws_server.cc deleted file mode 100644 index 48e2588..0000000 --- a/test/webfuse/utils/ws_server.cc +++ /dev/null @@ -1,266 +0,0 @@ -#include "webfuse/utils/ws_server.hpp" -#include "webfuse/utils/timeout_watcher.hpp" - -#include "webfuse/core/util.h" -#include "webfuse/core/protocol_names.h" -#include -#include -#include -#include -#include - -using webfuse_test::TimeoutWatcher; - -#define DEFAULT_TIMEOUT (std::chrono::milliseconds(5 * 1000)) - -namespace -{ - -class IServer -{ -public: - virtual ~IServer() = default; - virtual void onConnectionEstablished(struct lws * wsi) = 0; - virtual void onConnectionClosed(struct lws * wsi) = 0; - virtual void onMessageReceived(struct lws * wsi, char const * data, size_t length) = 0; - virtual void onWritable(struct lws * wsi) = 0; -}; - -} - -extern "C" -{ - -static int wf_test_utils_ws_server_callback( - struct lws * wsi, - enum lws_callback_reasons reason, - void * WF_UNUSED_PARAM(user), - void * in, - size_t len) -{ - struct lws_protocols const * ws_protocol = lws_get_protocol(wsi); - if (NULL == ws_protocol) - { - return 0; - } - - auto * server = reinterpret_cast(ws_protocol->user); - switch(reason) - { - case LWS_CALLBACK_ESTABLISHED: - server->onConnectionEstablished(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 0; -} - -} - -namespace webfuse_test -{ - -class WebsocketServer::Private: public IServer -{ -public: - Private(int port, struct lws_protocols * additionalProtocols, size_t additionalProtocolsCount) - : client_wsi(nullptr) - { - ws_protocols = new struct lws_protocols[2 + additionalProtocolsCount]; - memset(ws_protocols, 0, sizeof(struct lws_protocols) * (2 + additionalProtocolsCount)); - - ws_protocols[0].name = WF_PROTOCOL_NAME_ADAPTER_SERVER; - ws_protocols[0].callback = &wf_test_utils_ws_server_callback; - ws_protocols[0].per_session_data_size = 0; - ws_protocols[0].user = reinterpret_cast(this); - - if (0 < additionalProtocolsCount) - { - memcpy(&ws_protocols[additionalProtocolsCount], additionalProtocols, sizeof(struct lws_protocols) * additionalProtocolsCount); - } - - 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; - - context = lws_create_context(&info); - - } - - virtual ~Private() - { - lws_context_destroy(context); - delete[] ws_protocols; - } - - struct lws_context * getContext() - { - return context; - } - - void waitForConnection() - { - TimeoutWatcher watcher(DEFAULT_TIMEOUT); - - while (nullptr == client_wsi) - { - watcher.check(); - lws_service(context, 100); - } - } - - void sendMessage(json_t * message) - { - char* message_text = json_dumps(message, JSON_COMPACT); - writeQueue.push(message_text); - json_decref(message); - free(message_text); - - if (nullptr != client_wsi) - { - lws_callback_on_writable(client_wsi); - - TimeoutWatcher watcher(DEFAULT_TIMEOUT); - while (!writeQueue.empty()) - { - watcher.check(); - lws_service(context, 100); - } - } - } - - json_t * receiveMessage() - { - TimeoutWatcher watcher(DEFAULT_TIMEOUT); - - while (recvQueue.empty()) - { - watcher.check(); - lws_service(context, 100); - } - - std::string const & message_text = recvQueue.front(); - json_t * message = json_loads(message_text.c_str(), JSON_DECODE_ANY, nullptr); - recvQueue.pop(); - - return message; - } - - void onConnectionEstablished(struct lws * wsi) override - { - client_wsi = wsi; - } - - void onConnectionClosed(struct lws * wsi) override - { - if (wsi == client_wsi) - { - client_wsi = nullptr; - } - } - - void onMessageReceived(struct lws * wsi, char const * data, size_t length) override - { - if (wsi == client_wsi) - { - recvQueue.push(std::string(data, length)); - } - } - - void onWritable(struct lws * wsi) override - { - 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(); - if (!writeQueue.empty()) - { - lws_callback_on_writable(wsi); - } - } - } - - -private: - void send(std::string const & message) - { - if (nullptr != client_wsi) - { - writeQueue.push(message); - lws_callback_on_writable(client_wsi); - } - } - - struct lws * client_wsi; - - struct lws_protocols * ws_protocols; - struct lws_context_creation_info info; - struct lws_context * context; - std::queue writeQueue; - std::queue recvQueue; - -}; - -WebsocketServer::WebsocketServer(int port) -: d(new Private(port, nullptr, 0)) -{ - -} - -WebsocketServer::WebsocketServer(int port, struct lws_protocols * additionalProtocols, std::size_t additionalProtocolsCount) -: d(new Private(port, additionalProtocols, additionalProtocolsCount)) -{ - -} - -WebsocketServer::~WebsocketServer() -{ - delete d; -} - -struct lws_context * WebsocketServer::getContext() -{ - return d->getContext(); -} - -void WebsocketServer::waitForConnection() -{ - d->waitForConnection(); -} - -void WebsocketServer::sendMessage(json_t * message) -{ - d->sendMessage(message); -} - -json_t * WebsocketServer::receiveMessage() -{ - return d->receiveMessage(); -} - - -} \ No newline at end of file diff --git a/test/webfuse/utils/ws_server.hpp b/test/webfuse/utils/ws_server.hpp deleted file mode 100644 index e9d825a..0000000 --- a/test/webfuse/utils/ws_server.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef WF_TEST_UTILS_WS_SERVER_HPP -#define WF_TEST_UTILS_WS_SERVER_HPP - -#include -#include - -namespace webfuse_test -{ - -class WebsocketServer -{ - WebsocketServer(WebsocketServer const &) = delete; - WebsocketServer & operator=(WebsocketServer const &) = delete; -public: - explicit WebsocketServer(int port); - WebsocketServer(int port, struct lws_protocols * additionalProtocols, std::size_t additionalProtocolsCount); - ~WebsocketServer(); - struct lws_context * getContext(); - void waitForConnection(); - void sendMessage(json_t * message); - json_t * receiveMessage(); -private: - class Private; - Private * d; -}; - -} - -#endif From cd76427f8300e00496aa4dff8a9ef0c761171303 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Fri, 12 Jun 2020 16:53:36 +0200 Subject: [PATCH 12/37] renamed ThreadedWsServer -> WsServer --- meson.build | 2 +- test/webfuse/tests/adapter/test_client.cc | 6 +-- .../tests/provider/test_client_protocol.cc | 8 ++-- test/webfuse/utils/threaded_ws_server.h | 30 ------------- .../{threaded_ws_server.cc => ws_server.cc} | 42 +++++++++---------- test/webfuse/utils/ws_server.h | 30 +++++++++++++ 6 files changed, 59 insertions(+), 59 deletions(-) delete mode 100644 test/webfuse/utils/threaded_ws_server.h rename test/webfuse/utils/{threaded_ws_server.cc => ws_server.cc} (83%) create mode 100644 test/webfuse/utils/ws_server.h diff --git a/meson.build b/meson.build index b95bf0a..b1d4f2d 100644 --- a/meson.build +++ b/meson.build @@ -209,7 +209,7 @@ alltests = executable('alltests', 'test/webfuse/utils/timeout_watcher.cc', 'test/webfuse/utils/path.c', 'test/webfuse/utils/static_filesystem.c', - 'test/webfuse/utils/threaded_ws_server.cc', + 'test/webfuse/utils/ws_server.cc', 'test/webfuse/mocks/fake_invokation_context.cc', 'test/webfuse/mocks/mock_authenticator.cc', 'test/webfuse/mocks/mock_request.cc', diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index 086f5a0..bc0923b 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -3,9 +3,9 @@ #include "webfuse/adapter/client.h" #include "webfuse/adapter/credentials.h" #include "webfuse/core/protocol_names.h" -#include "webfuse/utils/threaded_ws_server.h" +#include "webfuse/utils/ws_server.h" -using webfuse_test::ThreadedWsServer; +using webfuse_test::WsServer; namespace { @@ -111,7 +111,7 @@ TEST(client, general_usage) TEST(client, connect) { - ThreadedWsServer server(WF_PROTOCOL_NAME_PROVIDER_SERVER); + WsServer server(WF_PROTOCOL_NAME_PROVIDER_SERVER); context ctx; ctx.state = connection_state::connecting; diff --git a/test/webfuse/tests/provider/test_client_protocol.cc b/test/webfuse/tests/provider/test_client_protocol.cc index 5aa1301..be5e596 100644 --- a/test/webfuse/tests/provider/test_client_protocol.cc +++ b/test/webfuse/tests/provider/test_client_protocol.cc @@ -3,7 +3,7 @@ #include #include -#include "webfuse/utils/threaded_ws_server.h" +#include "webfuse/utils/ws_server.h" #include "webfuse/mocks/mock_provider_client.hpp" #include "webfuse/core/protocol_names.h" #include "webfuse/utils/timeout_watcher.hpp" @@ -12,7 +12,7 @@ #include #include -using webfuse_test::ThreadedWsServer; +using webfuse_test::WsServer; using webfuse_test::MockProviderClient; using webfuse_test::IProviderClient; using webfuse_test::TimeoutWatcher; @@ -32,7 +32,7 @@ class ClientProtocolFixture public: explicit ClientProtocolFixture(IProviderClient& client, bool enableAuthentication = false) { - server = new ThreadedWsServer(WF_PROTOCOL_NAME_ADAPTER_SERVER); + server = new WsServer(WF_PROTOCOL_NAME_ADAPTER_SERVER); config = wfp_client_config_create(); client.AttachTo(config, enableAuthentication); @@ -168,7 +168,7 @@ public: } private: - ThreadedWsServer * server; + WsServer * server; wfp_client_config * config; wfp_client_protocol * protocol; struct lws_context_creation_info info; diff --git a/test/webfuse/utils/threaded_ws_server.h b/test/webfuse/utils/threaded_ws_server.h deleted file mode 100644 index fe6467e..0000000 --- a/test/webfuse/utils/threaded_ws_server.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef WF_TEST_UTILS_THREADED_WS_SERVER_HPP -#define WF_TEST_UTILS_THREADED_WS_SERVER_HPP - -#include -#include -#include - -namespace webfuse_test -{ - -class ThreadedWsServer -{ - ThreadedWsServer(ThreadedWsServer const &) = delete; - ThreadedWsServer & operator=(ThreadedWsServer const &) = delete; -public: - ThreadedWsServer(std::string const & protocol, int port = 0); - ~ThreadedWsServer(); - 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/utils/threaded_ws_server.cc b/test/webfuse/utils/ws_server.cc similarity index 83% rename from test/webfuse/utils/threaded_ws_server.cc rename to test/webfuse/utils/ws_server.cc index a0ea1ae..40f30c6 100644 --- a/test/webfuse/utils/threaded_ws_server.cc +++ b/test/webfuse/utils/ws_server.cc @@ -1,4 +1,4 @@ -#include "webfuse/utils/threaded_ws_server.h" +#include "webfuse/utils/ws_server.h" #include "webfuse/core/lws_log.h" #include @@ -29,7 +29,7 @@ public: namespace webfuse_test { -class ThreadedWsServer::Private : IServer +class WsServer::Private : IServer { Private(Private const &) = delete; Private & operator=(Private const &) = delete; @@ -66,7 +66,7 @@ private: extern "C" { -static int wf_test_utils_threaded_ws_server_callback( +static int wf_test_utils_ws_server_callback( struct lws * wsi, enum lws_callback_reasons reason, void * user, @@ -110,39 +110,39 @@ static int wf_test_utils_threaded_ws_server_callback( namespace webfuse_test { -ThreadedWsServer::ThreadedWsServer(std::string const & protocol, int port) +WsServer::WsServer(std::string const & protocol, int port) : d(new Private(protocol, port)) { } -ThreadedWsServer::~ThreadedWsServer() +WsServer::~WsServer() { delete d; } -bool ThreadedWsServer::IsConnected() +bool WsServer::IsConnected() { return d->IsConnected(); } -void ThreadedWsServer::SendMessage(json_t * message) +void WsServer::SendMessage(json_t * message) { d->SendMessage(message); } -json_t * ThreadedWsServer::ReceiveMessage() +json_t * WsServer::ReceiveMessage() { return d->ReceiveMessage(); } -std::string ThreadedWsServer::GetUrl() const +std::string WsServer::GetUrl() const { return d->GetUrl(); } -ThreadedWsServer::Private::Private(std::string const & protocol, int port) +WsServer::Private::Private(std::string const & protocol, int port) : protocol_(protocol) , port_(port) , is_connected(false) @@ -154,7 +154,7 @@ ThreadedWsServer::Private::Private(std::string const & protocol, int port) memset(ws_protocols, 0, sizeof(struct lws_protocols) * 2 ); ws_protocols[0].name = protocol_.c_str(); - ws_protocols[0].callback = &wf_test_utils_threaded_ws_server_callback; + ws_protocols[0].callback = &wf_test_utils_ws_server_callback; ws_protocols[0].per_session_data_size = 0; ws_protocols[0].user = reinterpret_cast(server); @@ -175,7 +175,7 @@ ThreadedWsServer::Private::Private(std::string const & protocol, int port) context = std::thread(&run, this); } -ThreadedWsServer::Private::~Private() +WsServer::Private::~Private() { { std::unique_lock lock(mutex); @@ -187,7 +187,7 @@ ThreadedWsServer::Private::~Private() lws_context_destroy(ws_context); } -void ThreadedWsServer::Private::run(Private * self) +void WsServer::Private::run(Private * self) { bool is_running = true; while (is_running) @@ -200,20 +200,20 @@ void ThreadedWsServer::Private::run(Private * self) } } -bool ThreadedWsServer::Private::IsConnected() +bool WsServer::Private::IsConnected() { std::unique_lock lock(mutex); return is_connected; } -void ThreadedWsServer::Private::OnConnected(lws * wsi) +void WsServer::Private::OnConnected(lws * wsi) { std::unique_lock lock(mutex); is_connected = true; wsi_ = wsi; } -void ThreadedWsServer::Private::OnConnectionClosed(lws * wsi) +void WsServer::Private::OnConnectionClosed(lws * wsi) { std::unique_lock lock(mutex); if (wsi == wsi_) @@ -223,7 +223,7 @@ void ThreadedWsServer::Private::OnConnectionClosed(lws * wsi) } } -void ThreadedWsServer::Private::OnWritable(struct lws * wsi) +void WsServer::Private::OnWritable(struct lws * wsi) { bool notify = false; @@ -251,7 +251,7 @@ void ThreadedWsServer::Private::OnWritable(struct lws * wsi) } -void ThreadedWsServer::Private::SendMessage(json_t * message) +void WsServer::Private::SendMessage(json_t * message) { lws * wsi = nullptr; @@ -274,7 +274,7 @@ void ThreadedWsServer::Private::SendMessage(json_t * message) } } -void ThreadedWsServer::Private::OnMessageReceived(struct lws * wsi, char const * data, size_t length) +void WsServer::Private::OnMessageReceived(struct lws * wsi, char const * data, size_t length) { std::unique_lock lock(mutex); if (wsi == wsi_) @@ -283,7 +283,7 @@ void ThreadedWsServer::Private::OnMessageReceived(struct lws * wsi, char const * } } -json_t * ThreadedWsServer::Private::ReceiveMessage() +json_t * WsServer::Private::ReceiveMessage() { std::unique_lock lock(mutex); @@ -298,7 +298,7 @@ json_t * ThreadedWsServer::Private::ReceiveMessage() return result; } -std::string ThreadedWsServer::Private::GetUrl() const +std::string WsServer::Private::GetUrl() const { std::ostringstream stream; stream << "ws://localhost:" << port_ << "/"; diff --git a/test/webfuse/utils/ws_server.h b/test/webfuse/utils/ws_server.h new file mode 100644 index 0000000..128cff7 --- /dev/null +++ b/test/webfuse/utils/ws_server.h @@ -0,0 +1,30 @@ +#ifndef WF_TEST_UTILS_WS_SERVER_HPP +#define WF_TEST_UTILS_WS_SERVER_HPP + +#include +#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 From 175384135362d76bd4a06457d5993aad9428069f Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Fri, 12 Jun 2020 19:00:28 +0200 Subject: [PATCH 13/37] replaced callback by mock object --- meson.build | 1 + .../mocks/mock_adapter_client_callback.cc | 44 +++++++++++++ .../mocks/mock_adapter_client_callback.hpp | 22 +++++++ test/webfuse/tests/adapter/test_client.cc | 66 ++++++++++--------- 4 files changed, 101 insertions(+), 32 deletions(-) create mode 100644 test/webfuse/mocks/mock_adapter_client_callback.cc create mode 100644 test/webfuse/mocks/mock_adapter_client_callback.hpp diff --git a/meson.build b/meson.build index b1d4f2d..54bdb39 100644 --- a/meson.build +++ b/meson.build @@ -218,6 +218,7 @@ alltests = executable('alltests', 'test/webfuse/mocks/mock_fuse.cc', 'test/webfuse/mocks/mock_operation_context.cc', 'test/webfuse/mocks/mock_jsonrpc_proxy.cc', + 'test/webfuse/mocks/mock_adapter_client_callback.cc', 'test/webfuse//tests/core/test_util.cc', 'test/webfuse/tests/core/test_container_of.cc', 'test/webfuse/tests/core/test_string.cc', diff --git a/test/webfuse/mocks/mock_adapter_client_callback.cc b/test/webfuse/mocks/mock_adapter_client_callback.cc new file mode 100644 index 0000000..4407497 --- /dev/null +++ b/test/webfuse/mocks/mock_adapter_client_callback.cc @@ -0,0 +1,44 @@ +#include "webfuse/mocks/mock_adapter_client_callback.hpp" +#include "webfuse/adapter/client.h" + +extern "C" +{ + +static void +webfuse_test_MockAdapterClientCallback_callback( + wf_client * client, + int reason, + void * args) +{ + void * user_data = wf_client_get_userdata(client); + auto * callback = reinterpret_cast(user_data); + + callback->Invoke(client, reason, args); +} + +} + +namespace webfuse_test +{ + +MockAdapterClientCallback::MockAdapterClientCallback() +{ + +} + +MockAdapterClientCallback::~MockAdapterClientCallback() +{ + +} + +void * MockAdapterClientCallback::GetUserData() +{ + return reinterpret_cast(this); +} + +wf_client_callback_fn * MockAdapterClientCallback::GetCallbackFn() +{ + return &webfuse_test_MockAdapterClientCallback_callback; +} + +} \ No newline at end of file diff --git a/test/webfuse/mocks/mock_adapter_client_callback.hpp b/test/webfuse/mocks/mock_adapter_client_callback.hpp new file mode 100644 index 0000000..cd66c7e --- /dev/null +++ b/test/webfuse/mocks/mock_adapter_client_callback.hpp @@ -0,0 +1,22 @@ +#ifndef WF_MOCK_ADAPTER_CLIENT_CALLBACK_HPP +#define WF_MOCK_ADAPTER_CLIENT_CALLBACK_HPP + +#include +#include "webfuse/adapter/client_callback.h" + +namespace webfuse_test +{ + +class MockAdapterClientCallback +{ +public: + MockAdapterClientCallback(); + virtual ~MockAdapterClientCallback(); + MOCK_METHOD3(Invoke, void (wf_client *, int, void *)); + void * GetUserData(); + wf_client_callback_fn * GetCallbackFn(); +}; + +} + +#endif diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index bc0923b..5b24a46 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -1,11 +1,15 @@ #include +#include #include "webfuse/adapter/client.h" #include "webfuse/adapter/credentials.h" #include "webfuse/core/protocol_names.h" #include "webfuse/utils/ws_server.h" +#include "webfuse/mocks/mock_adapter_client_callback.hpp" using webfuse_test::WsServer; +using webfuse_test::MockAdapterClientCallback; +using testing::_; namespace { @@ -67,33 +71,9 @@ void callback( } } -void callback2( - wf_client * client, - int reason, - void * args) -{ - auto * ctx = reinterpret_cast(wf_client_get_userdata(client)); - - switch (reason) - { - case WF_CLIENT_CREATED: - ctx->state = connection_state::connecting; - break; - case WF_CLIENT_CONNECTED: - ctx->state = connection_state::connected; - break; - case WF_CLIENT_DISCONNECTED: - ctx->state = connection_state::disconnected; - break; - default: - break; - } } - -} - -TEST(client, general_usage) +TEST(AdapterClient, GeneralUsage) { context ctx; ctx.state = connection_state::connecting; @@ -109,24 +89,46 @@ TEST(client, general_usage) wf_client_dispose(client); } -TEST(client, connect) +TEST(AdapterClient, CreateAndDispose) { - WsServer server(WF_PROTOCOL_NAME_PROVIDER_SERVER); + MockAdapterClientCallback callback; - context ctx; - ctx.state = connection_state::connecting; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_INIT, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CREATED, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_GET_TLS_CONFIG, _)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CLEANUP, nullptr)).Times(1); wf_client * client = wf_client_create( - &callback2, reinterpret_cast(&ctx)); + callback.GetCallbackFn(), callback.GetUserData()); + + wf_client_dispose(client); +} + +TEST(AdapterClient, Connect) +{ + WsServer server(WF_PROTOCOL_NAME_PROVIDER_SERVER); + MockAdapterClientCallback callback; + + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_INIT, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CREATED, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_GET_TLS_CONFIG, _)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CLEANUP, nullptr)).Times(1); + + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1); + + + wf_client * client = wf_client_create( + callback.GetCallbackFn(), callback.GetUserData()); wf_client_connect(client, server.GetUrl().c_str()); - while (ctx.state != connection_state::connected) + while (!server.IsConnected()) { wf_client_service(client); } wf_client_disconnect(client); - while (ctx.state != connection_state::disconnected) + while (server.IsConnected()) { wf_client_service(client); } From 021e056960f36513d95fd56b6396ea4f11c138ce Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Fri, 12 Jun 2020 19:05:10 +0200 Subject: [PATCH 14/37] added timeout watcher to observe loops --- test/webfuse/tests/adapter/test_client.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index 5b24a46..16d650f 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -6,11 +6,15 @@ #include "webfuse/core/protocol_names.h" #include "webfuse/utils/ws_server.h" #include "webfuse/mocks/mock_adapter_client_callback.hpp" +#include "webfuse/utils/timeout_watcher.hpp" using webfuse_test::WsServer; using webfuse_test::MockAdapterClientCallback; +using webfuse_test::TimeoutWatcher; using testing::_; +#define TIMEOUT (std::chrono::milliseconds(10 * 1000)) + namespace { @@ -106,6 +110,8 @@ TEST(AdapterClient, CreateAndDispose) TEST(AdapterClient, Connect) { + TimeoutWatcher watcher(TIMEOUT); + WsServer server(WF_PROTOCOL_NAME_PROVIDER_SERVER); MockAdapterClientCallback callback; @@ -124,12 +130,14 @@ TEST(AdapterClient, Connect) wf_client_connect(client, server.GetUrl().c_str()); while (!server.IsConnected()) { + watcher.check(); wf_client_service(client); } wf_client_disconnect(client); while (server.IsConnected()) { + watcher.check(); wf_client_service(client); } From adaec875d96c8d9252c1fafd5db44bc6110031e1 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Fri, 12 Jun 2020 20:38:20 +0200 Subject: [PATCH 15/37] added implementation of wf_client_authenticate --- lib/webfuse/adapter/impl/client.c | 2 +- lib/webfuse/adapter/impl/client_protocol.c | 105 +++++++++++++++++++++ lib/webfuse/adapter/impl/client_protocol.h | 11 +++ test/webfuse/tests/adapter/test_client.cc | 91 ++++++++++++++++++ 4 files changed, 208 insertions(+), 1 deletion(-) diff --git a/lib/webfuse/adapter/impl/client.c b/lib/webfuse/adapter/impl/client.c index ed3863d..c7a72e6 100644 --- a/lib/webfuse/adapter/impl/client.c +++ b/lib/webfuse/adapter/impl/client.c @@ -114,7 +114,7 @@ void wf_impl_client_authenticate( struct wf_client * client) { - wf_impl_client_protocol_callback(&client->protocol, WF_CLIENT_AUTHENTICATION_FAILED, NULL); + wf_impl_client_protocol_authenticate(&client->protocol); } void diff --git a/lib/webfuse/adapter/impl/client_protocol.c b/lib/webfuse/adapter/impl/client_protocol.c index 894ec0d..fb392c8 100644 --- a/lib/webfuse/adapter/impl/client_protocol.c +++ b/lib/webfuse/adapter/impl/client_protocol.c @@ -1,12 +1,73 @@ #include "webfuse/adapter/impl/client_protocol.h" #include "webfuse/adapter/client_callback.h" +#include "webfuse/adapter/impl/credentials.h" #include "webfuse/core/protocol_names.h" #include "webfuse/core/url.h" #include "webfuse/core/util.h" +#include "webfuse/core/timer/manager.h" +#include "webfuse/core/jsonrpc/response.h" +#include "webfuse/core/jsonrpc/proxy.h" + +#include "webfuse/core/message.h" +#include "webfuse/core/message_queue.h" +#include "webfuse/core/container_of.h" + #include #include +#define WF_DEFAULT_TIMEOUT (10 * 1000) + +static void +wf_impl_client_protocol_process( + struct wf_client_protocol * protocol, + char const * data, + size_t length) +{ + json_t * message = json_loadb(data, length, 0, NULL); + if (NULL != message) + { + if (wf_jsonrpc_is_response(message)) + { + wf_jsonrpc_proxy_onresult(protocol->proxy, message); + } + + json_decref(message); + } +} + +static bool +wf_impl_client_protocol_send( + json_t * request, + void * user_data) +{ + bool result = false; + struct wf_client_protocol * protocol = user_data; + + struct wf_message * message = wf_message_create(request); + if (NULL != message) + { + wf_slist_append(&protocol->messages, &message->item); + lws_callback_on_writable(protocol->wsi); + result = true; + } + + return result; +} + + +static void +wf_impl_client_protocol_on_authenticate_finished( + void * user_data, + json_t const * result, + json_t const * WF_UNUSED_PARAM(error)) +{ + struct wf_client_protocol * protocol = user_data; + int const reason = (NULL != result) ? WF_CLIENT_AUTHENTICATED : WF_CLIENT_AUTHENTICATION_FAILED; + + protocol->callback(protocol->user_data, reason, NULL); +} + static int wf_impl_client_protocol_lws_callback( struct lws * wsi, enum lws_callback_reasons reason, @@ -20,6 +81,8 @@ static int wf_impl_client_protocol_lws_callback( if (NULL != protocol) { + wf_timer_manager_check(protocol->timer_manager); + switch (reason) { case LWS_CALLBACK_CLIENT_ESTABLISHED: @@ -35,6 +98,8 @@ static int wf_impl_client_protocol_lws_callback( protocol->callback(protocol->user_data, WF_CLIENT_DISCONNECTED, NULL); protocol->wsi = NULL; break; + case LWS_CALLBACK_CLIENT_RECEIVE: + wf_impl_client_protocol_process(protocol, in, len); case LWS_CALLBACK_SERVER_WRITEABLE: // fall-through case LWS_CALLBACK_CLIENT_WRITEABLE: @@ -44,6 +109,18 @@ static int wf_impl_client_protocol_lws_callback( { result = 1; } + else if (!wf_slist_empty(&protocol->messages)) + { + struct wf_slist_item * item = wf_slist_remove_first(&protocol->messages); + struct wf_message * message = wf_container_of(item, struct wf_message, item); + lws_write(wsi, (unsigned char*) message->data, message->length, LWS_WRITE_TEXT); + wf_message_dispose(message); + + if (!wf_slist_empty(&protocol->messages)) + { + lws_callback_on_writable(wsi); + } + } } default: break; @@ -65,6 +142,11 @@ wf_impl_client_protocol_init( protocol->callback = callback; protocol->user_data = user_data; protocol->callback(protocol->user_data, WF_CLIENT_INIT, NULL); + + wf_slist_init(&protocol->messages); + protocol->timer_manager = wf_timer_manager_create(); + protocol->proxy = wf_jsonrpc_proxy_create(protocol->timer_manager, WF_DEFAULT_TIMEOUT, &wf_impl_client_protocol_send, protocol); + } void @@ -72,6 +154,9 @@ wf_impl_client_protocol_cleanup( struct wf_client_protocol * protocol) { protocol->callback(protocol->user_data, WF_CLIENT_CLEANUP, NULL); + wf_jsonrpc_proxy_dispose(protocol->proxy); + wf_timer_manager_dispose(protocol->timer_manager); + wf_message_queue_cleanup(&protocol->messages); } void @@ -141,3 +226,23 @@ wf_impl_client_protocol_disconnect( } } + +void +wf_impl_client_protocol_authenticate( + struct wf_client_protocol * protocol) +{ + struct wf_credentials creds; + wf_impl_credentials_init_default(&creds); + protocol->callback(protocol->user_data, WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS, &creds); + + json_incref(creds.data); + wf_jsonrpc_proxy_invoke( + protocol->proxy, + &wf_impl_client_protocol_on_authenticate_finished, + protocol, + "authenticate", + "sj", + creds.type, creds.data); + + wf_impl_credentials_cleanup(&creds); +} diff --git a/lib/webfuse/adapter/impl/client_protocol.h b/lib/webfuse/adapter/impl/client_protocol.h index 29606f4..02e8b5b 100644 --- a/lib/webfuse/adapter/impl/client_protocol.h +++ b/lib/webfuse/adapter/impl/client_protocol.h @@ -2,6 +2,7 @@ #define WF_ADAPTER_IMPL_CLIENT_PROTOCOL_H #include "webfuse/adapter/client_callback.h" +#include "webfuse/core/slist.h" #ifndef __cplusplus #include @@ -15,6 +16,9 @@ extern "C" struct lws_protocols; struct lws_context; +struct wf_jsonrpc_proxy; +struct wf_timer_manager; + typedef void wf_client_protocol_callback_fn( void * user_data, @@ -28,6 +32,9 @@ struct wf_client_protocol struct lws * wsi; wf_client_callback_fn * callback; void * user_data; + struct wf_timer_manager * timer_manager; + struct wf_jsonrpc_proxy * proxy; + struct wf_slist messages; }; extern void @@ -61,6 +68,10 @@ extern void wf_impl_client_protocol_disconnect( struct wf_client_protocol * protocol); +extern void +wf_impl_client_protocol_authenticate( + struct wf_client_protocol * protocol); + #ifdef __cplusplus } #endif diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index 16d650f..835cdf4 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -3,6 +3,7 @@ #include "webfuse/adapter/client.h" #include "webfuse/adapter/credentials.h" +#include "webfuse/adapter/credentials.h" #include "webfuse/core/protocol_names.h" #include "webfuse/utils/ws_server.h" #include "webfuse/mocks/mock_adapter_client_callback.hpp" @@ -12,12 +13,21 @@ using webfuse_test::WsServer; using webfuse_test::MockAdapterClientCallback; using webfuse_test::TimeoutWatcher; using testing::_; +using testing::Invoke; #define TIMEOUT (std::chrono::milliseconds(10 * 1000)) namespace { +void GetCredentials(wf_client *, int, void * arg) +{ + auto * creds = reinterpret_cast(arg); + wf_credentials_set_type(creds, "username"); + wf_credentials_add(creds, "username", "Bob"); + wf_credentials_add(creds, "password", "secret"); +} + enum class connection_state { disconnected, @@ -141,5 +151,86 @@ TEST(AdapterClient, Connect) wf_client_service(client); } + wf_client_dispose(client); +} + +TEST(AdapterClient, Authenticate) +{ + TimeoutWatcher watcher(TIMEOUT); + + WsServer server(WF_PROTOCOL_NAME_PROVIDER_SERVER); + MockAdapterClientCallback callback; + + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_INIT, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CREATED, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_GET_TLS_CONFIG, _)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CLEANUP, nullptr)).Times(1); + + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1); + + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS, _)).Times(1) + .WillOnce(Invoke(GetCredentials)); + bool called = false; + bool * p_called = &called; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_AUTHENTICATED, nullptr)).Times(1) + .WillOnce(Invoke([p_called] (wf_client *, int, void *) { + *p_called = true; + })); + + wf_client * client = wf_client_create( + callback.GetCallbackFn(), callback.GetUserData()); + + wf_client_connect(client, server.GetUrl().c_str()); + while (!server.IsConnected()) + { + watcher.check(); + wf_client_service(client); + } + + wf_client_authenticate(client); + json_t * request = server.ReceiveMessage(); + while (nullptr == request) + { + watcher.check(); + wf_client_service(client); + request = server.ReceiveMessage(); + } + json_t * id = json_object_get(request, "id"); + ASSERT_TRUE(json_is_integer(id)); + json_t * method = json_object_get(request, "method"); + ASSERT_TRUE(json_is_string(method)); + ASSERT_STREQ("authenticate", json_string_value(method)); + 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)); + json_t * creds = json_array_get(params, 1); + ASSERT_TRUE(json_is_object(creds)); + json_t * username = json_object_get(creds, "username"); + ASSERT_TRUE(json_is_string(username)); + ASSERT_STREQ("Bob", json_string_value(username)); + json_t * password = json_object_get(creds, "password"); + ASSERT_TRUE(json_is_string(password)); + ASSERT_STREQ("secret", json_string_value(password)); + + json_t * response = json_object(); + json_object_set(response, "id", id); + json_object_set_new(response, "result", json_object()); + server.SendMessage(response); + json_decref(request); + while (!called) { + watcher.check(); + wf_client_service(client); + } + + wf_client_disconnect(client); + while (server.IsConnected()) + { + watcher.check(); + wf_client_service(client); + } + wf_client_dispose(client); } \ No newline at end of file From 0c702ff25f71b994ace618bed7a1345248adf216 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Fri, 12 Jun 2020 22:53:42 +0200 Subject: [PATCH 16/37] refactor: make filesystem independent of session --- lib/webfuse/adapter/impl/filesystem.c | 14 ++++++++------ lib/webfuse/adapter/impl/filesystem.h | 5 +++-- lib/webfuse/adapter/impl/operation/context.c | 10 +--------- lib/webfuse/adapter/impl/operation/context.h | 3 +-- lib/webfuse/adapter/impl/session.c | 2 +- .../tests/adapter/operation/test_context.cc | 6 ++---- 6 files changed, 16 insertions(+), 24 deletions(-) diff --git a/lib/webfuse/adapter/impl/filesystem.c b/lib/webfuse/adapter/impl/filesystem.c index a033190..4fa1d38 100644 --- a/lib/webfuse/adapter/impl/filesystem.c +++ b/lib/webfuse/adapter/impl/filesystem.c @@ -52,7 +52,8 @@ static void wf_impl_filesystem_cleanup( static bool wf_impl_filesystem_init( struct wf_impl_filesystem * filesystem, - struct wf_impl_session * session, + struct lws * session_wsi, + struct wf_jsonrpc_proxy * proxy, char const * name, struct wf_mountpoint * mountpoint) { @@ -63,7 +64,7 @@ static bool wf_impl_filesystem_init( filesystem->args.argv = argv; filesystem->args.allocated = 0; - filesystem->user_data.session = session; + filesystem->user_data.proxy = proxy; filesystem->user_data.timeout = 1.0; filesystem->user_data.name = strdup(name); memset(&filesystem->buffer, 0, sizeof(struct fuse_buf)); @@ -85,8 +86,8 @@ static bool wf_impl_filesystem_init( { lws_sock_file_fd_type fd; fd.filefd = fuse_session_fd(filesystem->session); - struct lws_protocols const * protocol = lws_get_protocol(session->wsi); - filesystem->wsi = lws_adopt_descriptor_vhost(lws_get_vhost(session->wsi), LWS_ADOPT_RAW_FILE_DESC, fd, protocol->name, session->wsi); + struct lws_protocols const * protocol = lws_get_protocol(session_wsi); + filesystem->wsi = lws_adopt_descriptor_vhost(lws_get_vhost(session_wsi), LWS_ADOPT_RAW_FILE_DESC, fd, protocol->name, session_wsi); if (NULL == filesystem->wsi) { @@ -100,12 +101,13 @@ static bool wf_impl_filesystem_init( } struct wf_impl_filesystem * wf_impl_filesystem_create( - struct wf_impl_session * session, + struct lws * session_wsi, + struct wf_jsonrpc_proxy * proxy, char const * name, struct wf_mountpoint * mountpoint) { struct wf_impl_filesystem * filesystem = malloc(sizeof(struct wf_impl_filesystem)); - bool success = wf_impl_filesystem_init(filesystem, session, name, mountpoint); + bool success = wf_impl_filesystem_init(filesystem, session_wsi, proxy, name, mountpoint); if (!success) { free(filesystem); diff --git a/lib/webfuse/adapter/impl/filesystem.h b/lib/webfuse/adapter/impl/filesystem.h index 791170a..95825f2 100644 --- a/lib/webfuse/adapter/impl/filesystem.h +++ b/lib/webfuse/adapter/impl/filesystem.h @@ -15,7 +15,7 @@ extern "C" #endif struct wf_mountpoint; -struct wf_impl_session; +struct wf_jsonrpc_proxy; struct lws; struct wf_impl_filesystem @@ -30,7 +30,8 @@ struct wf_impl_filesystem }; extern struct wf_impl_filesystem * wf_impl_filesystem_create( - struct wf_impl_session * session, + struct lws * session_wsi, + struct wf_jsonrpc_proxy * proxy, char const * name, struct wf_mountpoint * mountpoint); diff --git a/lib/webfuse/adapter/impl/operation/context.c b/lib/webfuse/adapter/impl/operation/context.c index a39978f..3e9829d 100644 --- a/lib/webfuse/adapter/impl/operation/context.c +++ b/lib/webfuse/adapter/impl/operation/context.c @@ -6,13 +6,5 @@ struct wf_jsonrpc_proxy * wf_impl_operation_context_get_proxy( struct wf_impl_operation_context * context) { - struct wf_jsonrpc_proxy * proxy = NULL; - - struct wf_impl_session * session = context->session; - if (NULL != session) - { - proxy = session->rpc; - } - - return proxy; + return context->proxy; } diff --git a/lib/webfuse/adapter/impl/operation/context.h b/lib/webfuse/adapter/impl/operation/context.h index 51d886f..1384f72 100644 --- a/lib/webfuse/adapter/impl/operation/context.h +++ b/lib/webfuse/adapter/impl/operation/context.h @@ -7,12 +7,11 @@ extern "C" { #endif -struct wf_impl_session; struct wf_jsonrpc_proxy; struct wf_impl_operation_context { - struct wf_impl_session * session; + struct wf_jsonrpc_proxy * proxy; double timeout; char * name; }; diff --git a/lib/webfuse/adapter/impl/session.c b/lib/webfuse/adapter/impl/session.c index 159f43a..aa950a7 100644 --- a/lib/webfuse/adapter/impl/session.c +++ b/lib/webfuse/adapter/impl/session.c @@ -108,7 +108,7 @@ bool wf_impl_session_add_filesystem( if (result) { - struct wf_impl_filesystem * filesystem = wf_impl_filesystem_create(session, name, mountpoint); + struct wf_impl_filesystem * filesystem = wf_impl_filesystem_create(session->wsi, session->rpc, name, mountpoint); result = (NULL != filesystem); if (result) { diff --git a/test/webfuse/tests/adapter/operation/test_context.cc b/test/webfuse/tests/adapter/operation/test_context.cc index 77aa287..75e619e 100644 --- a/test/webfuse/tests/adapter/operation/test_context.cc +++ b/test/webfuse/tests/adapter/operation/test_context.cc @@ -5,10 +5,8 @@ TEST(wf_impl_operation_context, get_proxy) { wf_jsonrpc_proxy * proxy = reinterpret_cast(42); - wf_impl_session session; - session.rpc = proxy; wf_impl_operation_context context; - context.session = &session; + context.proxy = proxy; ASSERT_EQ(proxy, wf_impl_operation_context_get_proxy(&context)); } @@ -16,7 +14,7 @@ TEST(wf_impl_operation_context, get_proxy) TEST(wf_impl_operation_context, get_proxy_fail_no_session) { wf_impl_operation_context context; - context.session = nullptr; + context.proxy = nullptr; ASSERT_EQ(nullptr, wf_impl_operation_context_get_proxy(&context)); From 081304dee68c17233cea333a3e2c48a4c3345c91 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sat, 13 Jun 2020 10:17:20 +0200 Subject: [PATCH 17/37] added implementation of wf_client_add_filesystem --- lib/webfuse/adapter/impl/client.c | 7 +- lib/webfuse/adapter/impl/client_protocol.c | 79 +++++++++++++++++++++- lib/webfuse/adapter/impl/client_protocol.h | 12 +++- 3 files changed, 89 insertions(+), 9 deletions(-) diff --git a/lib/webfuse/adapter/impl/client.c b/lib/webfuse/adapter/impl/client.c index c7a72e6..745250f 100644 --- a/lib/webfuse/adapter/impl/client.c +++ b/lib/webfuse/adapter/impl/client.c @@ -31,7 +31,7 @@ wf_impl_client_create( wf_impl_client_tlsconfig_init(&client->tls); client->user_data = user_data; wf_impl_client_protocol_init(&client->protocol, - (wf_client_callback_fn*) callback, (void*) client); + (wf_client_protocol_callback_fn*) callback, (void*) client); memset(client->protocols, 0, sizeof(struct lws_protocols) * WF_CLIENT_PROTOCOL_COUNT); wf_impl_client_protocol_init_lws(&client->protocol, &client->protocols[0]); @@ -123,8 +123,5 @@ wf_impl_client_add_filesystem( char const * local_path, char const * name) { - (void) local_path; - (void) name; - - wf_impl_client_protocol_callback(&client->protocol, WF_CLIENT_FILESYSTEM_ADD_FAILED, NULL); + wf_impl_client_protocol_add_filesystem(&client->protocol, local_path, name); } diff --git a/lib/webfuse/adapter/impl/client_protocol.c b/lib/webfuse/adapter/impl/client_protocol.c index fb392c8..3bffaad 100644 --- a/lib/webfuse/adapter/impl/client_protocol.c +++ b/lib/webfuse/adapter/impl/client_protocol.c @@ -1,6 +1,8 @@ #include "webfuse/adapter/impl/client_protocol.h" #include "webfuse/adapter/client_callback.h" #include "webfuse/adapter/impl/credentials.h" +#include "webfuse/adapter/impl/filesystem.h" +#include "webfuse/adapter/impl/mountpoint.h" #include "webfuse/core/protocol_names.h" #include "webfuse/core/url.h" #include "webfuse/core/util.h" @@ -18,6 +20,12 @@ #define WF_DEFAULT_TIMEOUT (10 * 1000) +struct wf_impl_client_protocol_add_filesystem_context +{ + struct wf_client_protocol * protocol; + char * local_path; +}; + static void wf_impl_client_protocol_process( struct wf_client_protocol * protocol, @@ -68,6 +76,40 @@ wf_impl_client_protocol_on_authenticate_finished( protocol->callback(protocol->user_data, reason, NULL); } +static void +wf_impl_client_protocol_on_add_filesystem_finished( + void * user_data, + json_t const * result, + json_t const * WF_UNUSED_PARAM(error)) +{ + struct wf_impl_client_protocol_add_filesystem_context * context = user_data; + struct wf_client_protocol * protocol = context->protocol; + + int reason = WF_CLIENT_FILESYSTEM_ADD_FAILED; + if (NULL == protocol->filesystem) + { + json_t * id = json_object_get(result, "id"); + if (json_is_string(id)) + { + char const * name = json_string_value(id); + struct wf_mountpoint * mountpoint = wf_mountpoint_create(context->local_path); + protocol->filesystem = wf_impl_filesystem_create(protocol->wsi,protocol->proxy, name, mountpoint); + if (NULL != protocol->filesystem) + { + reason = WF_CLIENT_FILESYSTEM_ADDED; + } + else + { + wf_mountpoint_dispose(mountpoint); + } + } + } + + free(context->local_path); + free(context); + protocol->callback(protocol->user_data, reason, NULL); +} + static int wf_impl_client_protocol_lws_callback( struct lws * wsi, enum lws_callback_reasons reason, @@ -133,7 +175,7 @@ static int wf_impl_client_protocol_lws_callback( void wf_impl_client_protocol_init( struct wf_client_protocol * protocol, - wf_client_callback_fn * callback, + wf_client_protocol_callback_fn * callback, void * user_data) { protocol->is_connected = false, @@ -141,18 +183,24 @@ wf_impl_client_protocol_init( protocol->wsi = NULL; protocol->callback = callback; protocol->user_data = user_data; - protocol->callback(protocol->user_data, WF_CLIENT_INIT, NULL); + protocol->filesystem = NULL; wf_slist_init(&protocol->messages); protocol->timer_manager = wf_timer_manager_create(); protocol->proxy = wf_jsonrpc_proxy_create(protocol->timer_manager, WF_DEFAULT_TIMEOUT, &wf_impl_client_protocol_send, protocol); + protocol->callback(protocol->user_data, WF_CLIENT_INIT, NULL); } void wf_impl_client_protocol_cleanup( struct wf_client_protocol * protocol) { + if (NULL != protocol->filesystem) + { + wf_impl_filesystem_dispose(protocol->filesystem); + } + protocol->callback(protocol->user_data, WF_CLIENT_CLEANUP, NULL); wf_jsonrpc_proxy_dispose(protocol->proxy); wf_timer_manager_dispose(protocol->timer_manager); @@ -246,3 +294,30 @@ wf_impl_client_protocol_authenticate( wf_impl_credentials_cleanup(&creds); } + +void +wf_impl_client_protocol_add_filesystem( + struct wf_client_protocol * protocol, + char const * local_path, + char const * name) +{ + if (NULL == protocol->filesystem) + { + struct wf_impl_client_protocol_add_filesystem_context * context = malloc(sizeof(struct wf_impl_client_protocol_add_filesystem_context)); + context->protocol = protocol; + context->local_path = strdup(local_path); + + wf_jsonrpc_proxy_invoke( + protocol->proxy, + &wf_impl_client_protocol_on_add_filesystem_finished, + context, + "add_filesystem", + "s", + name); + } + else + { + protocol->callback(protocol->user_data, WF_CLIENT_FILESYSTEM_ADD_FAILED, NULL); + } +} + diff --git a/lib/webfuse/adapter/impl/client_protocol.h b/lib/webfuse/adapter/impl/client_protocol.h index 02e8b5b..00c7c26 100644 --- a/lib/webfuse/adapter/impl/client_protocol.h +++ b/lib/webfuse/adapter/impl/client_protocol.h @@ -16,6 +16,7 @@ extern "C" struct lws_protocols; struct lws_context; +struct wf_impl_filesystem; struct wf_jsonrpc_proxy; struct wf_timer_manager; @@ -30,7 +31,8 @@ struct wf_client_protocol bool is_connected; bool is_shutdown_requested; struct lws * wsi; - wf_client_callback_fn * callback; + wf_client_protocol_callback_fn * callback; + struct wf_impl_filesystem * filesystem; void * user_data; struct wf_timer_manager * timer_manager; struct wf_jsonrpc_proxy * proxy; @@ -40,7 +42,7 @@ struct wf_client_protocol extern void wf_impl_client_protocol_init( struct wf_client_protocol * protocol, - wf_client_callback_fn * callback, + wf_client_protocol_callback_fn * callback, void * user_data); extern void @@ -72,6 +74,12 @@ extern void wf_impl_client_protocol_authenticate( struct wf_client_protocol * protocol); +extern void +wf_impl_client_protocol_add_filesystem( + struct wf_client_protocol * protocol, + char const * local_path, + char const * name); + #ifdef __cplusplus } #endif From f12d3e99c0416ad82e5d0428541ed9187bb34b80 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sat, 13 Jun 2020 11:35:39 +0200 Subject: [PATCH 18/37] removed unused include --- test/webfuse/tests/provider/test_client_protocol.cc | 2 ++ test/webfuse/utils/ws_server.cc | 3 --- test/webfuse/utils/ws_server.h | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/test/webfuse/tests/provider/test_client_protocol.cc b/test/webfuse/tests/provider/test_client_protocol.cc index be5e596..e2fe6b6 100644 --- a/test/webfuse/tests/provider/test_client_protocol.cc +++ b/test/webfuse/tests/provider/test_client_protocol.cc @@ -8,6 +8,8 @@ #include "webfuse/core/protocol_names.h" #include "webfuse/utils/timeout_watcher.hpp" +#include + #include #include #include diff --git a/test/webfuse/utils/ws_server.cc b/test/webfuse/utils/ws_server.cc index 40f30c6..2c3dcfe 100644 --- a/test/webfuse/utils/ws_server.cc +++ b/test/webfuse/utils/ws_server.cc @@ -8,9 +8,6 @@ #include #include - -#define TIMEOUT (std::chrono::milliseconds(10 * 1000)) - namespace { diff --git a/test/webfuse/utils/ws_server.h b/test/webfuse/utils/ws_server.h index 128cff7..a51f695 100644 --- a/test/webfuse/utils/ws_server.h +++ b/test/webfuse/utils/ws_server.h @@ -1,7 +1,6 @@ #ifndef WF_TEST_UTILS_WS_SERVER_HPP #define WF_TEST_UTILS_WS_SERVER_HPP -#include #include #include From 5a439f4ef9ec79358f6cb12f1c38b1484e81b3e2 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sat, 13 Jun 2020 11:36:02 +0200 Subject: [PATCH 19/37] removed unused code --- test/webfuse/tests/adapter/test_client.cc | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index 835cdf4..9c5872c 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -14,6 +14,7 @@ using webfuse_test::MockAdapterClientCallback; using webfuse_test::TimeoutWatcher; using testing::_; using testing::Invoke; +using testing::AnyNumber; #define TIMEOUT (std::chrono::milliseconds(10 * 1000)) @@ -161,21 +162,13 @@ TEST(AdapterClient, Authenticate) WsServer server(WF_PROTOCOL_NAME_PROVIDER_SERVER); MockAdapterClientCallback callback; - EXPECT_CALL(callback, Invoke(_, WF_CLIENT_INIT, nullptr)).Times(1); - EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CREATED, nullptr)).Times(1); - EXPECT_CALL(callback, Invoke(_, WF_CLIENT_GET_TLS_CONFIG, _)).Times(1); - EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CLEANUP, nullptr)).Times(1); - - EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1); - EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1); - + EXPECT_CALL(callback, Invoke(_, _, _)).Times(AnyNumber()); EXPECT_CALL(callback, Invoke(_, WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS, _)).Times(1) .WillOnce(Invoke(GetCredentials)); bool called = false; - bool * p_called = &called; EXPECT_CALL(callback, Invoke(_, WF_CLIENT_AUTHENTICATED, nullptr)).Times(1) - .WillOnce(Invoke([p_called] (wf_client *, int, void *) { - *p_called = true; + .WillOnce(Invoke([&called] (wf_client *, int, void *) mutable { + called = true; })); wf_client * client = wf_client_create( From 55de95caf7f693cb2aa3d6fbf60100d25661fdee Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sat, 13 Jun 2020 14:59:58 +0200 Subject: [PATCH 20/37] added another server to simplify testing --- meson.build | 1 + .../webfuse/mocks/mock_invokation_handler.hpp | 18 ++ test/webfuse/tests/adapter/test_client.cc | 92 ++++-- test/webfuse/utils/ws_server2.cc | 305 ++++++++++++++++++ test/webfuse/utils/ws_server2.hpp | 36 +++ 5 files changed, 419 insertions(+), 33 deletions(-) create mode 100644 test/webfuse/mocks/mock_invokation_handler.hpp create mode 100644 test/webfuse/utils/ws_server2.cc create mode 100644 test/webfuse/utils/ws_server2.hpp diff --git a/meson.build b/meson.build index 54bdb39..3a7a1cd 100644 --- a/meson.build +++ b/meson.build @@ -210,6 +210,7 @@ alltests = executable('alltests', 'test/webfuse/utils/path.c', 'test/webfuse/utils/static_filesystem.c', 'test/webfuse/utils/ws_server.cc', + 'test/webfuse/utils/ws_server2.cc', 'test/webfuse/mocks/fake_invokation_context.cc', 'test/webfuse/mocks/mock_authenticator.cc', 'test/webfuse/mocks/mock_request.cc', diff --git a/test/webfuse/mocks/mock_invokation_handler.hpp b/test/webfuse/mocks/mock_invokation_handler.hpp new file mode 100644 index 0000000..48d3b8b --- /dev/null +++ b/test/webfuse/mocks/mock_invokation_handler.hpp @@ -0,0 +1,18 @@ +#ifndef WF_MOCK_INVOKATION_HANDLER_HPP +#define WF_MOCK_INVOKATION_HANDLER_HPP + +#include "webfuse/utils/ws_server2.hpp" +#include + +namespace webfuse_test +{ + +class MockInvokationHander: public IIvokationHandler +{ +public: + MOCK_METHOD2(Invoke, std::string(char const * method, json_t * params)); +}; + +} + +#endif diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index 9c5872c..e8ab17c 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -5,18 +5,23 @@ #include "webfuse/adapter/credentials.h" #include "webfuse/adapter/credentials.h" #include "webfuse/core/protocol_names.h" -#include "webfuse/utils/ws_server.h" +#include "webfuse/utils/ws_server2.hpp" #include "webfuse/mocks/mock_adapter_client_callback.hpp" +#include "webfuse/mocks/mock_invokation_handler.hpp" #include "webfuse/utils/timeout_watcher.hpp" -using webfuse_test::WsServer; +using webfuse_test::WsServer2; +using webfuse_test::MockInvokationHander; using webfuse_test::MockAdapterClientCallback; using webfuse_test::TimeoutWatcher; using testing::_; using testing::Invoke; using testing::AnyNumber; +using testing::Return; +using testing::Throw; +using testing::StrEq; -#define TIMEOUT (std::chrono::milliseconds(10 * 1000)) +#define TIMEOUT (std::chrono::milliseconds(30 * 1000)) namespace { @@ -123,9 +128,11 @@ TEST(AdapterClient, Connect) { TimeoutWatcher watcher(TIMEOUT); - WsServer server(WF_PROTOCOL_NAME_PROVIDER_SERVER); - MockAdapterClientCallback callback; + MockInvokationHander handler; + WsServer2 server(handler, WF_PROTOCOL_NAME_PROVIDER_SERVER); + EXPECT_CALL(handler, Invoke(_,_)).Times(0); + MockAdapterClientCallback callback; EXPECT_CALL(callback, Invoke(_, WF_CLIENT_INIT, nullptr)).Times(1); EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CREATED, nullptr)).Times(1); EXPECT_CALL(callback, Invoke(_, WF_CLIENT_GET_TLS_CONFIG, _)).Times(1); @@ -159,9 +166,12 @@ TEST(AdapterClient, Authenticate) { TimeoutWatcher watcher(TIMEOUT); - WsServer server(WF_PROTOCOL_NAME_PROVIDER_SERVER); - MockAdapterClientCallback callback; + MockInvokationHander handler; + WsServer2 server(handler, WF_PROTOCOL_NAME_PROVIDER_SERVER); + EXPECT_CALL(handler, Invoke(StrEq("authenticate"),_)).Times(1) + .WillOnce(Return("{}")); + MockAdapterClientCallback callback; EXPECT_CALL(callback, Invoke(_, _, _)).Times(AnyNumber()); EXPECT_CALL(callback, Invoke(_, WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS, _)).Times(1) .WillOnce(Invoke(GetCredentials)); @@ -174,6 +184,7 @@ TEST(AdapterClient, Authenticate) wf_client * client = wf_client_create( callback.GetCallbackFn(), callback.GetUserData()); + wf_client_connect(client, server.GetUrl().c_str()); while (!server.IsConnected()) { @@ -182,37 +193,52 @@ TEST(AdapterClient, Authenticate) } wf_client_authenticate(client); - json_t * request = server.ReceiveMessage(); - while (nullptr == request) + while (!called) { + watcher.check(); + wf_client_service(client); + } + + wf_client_disconnect(client); + while (server.IsConnected()) { watcher.check(); wf_client_service(client); - request = server.ReceiveMessage(); } - json_t * id = json_object_get(request, "id"); - ASSERT_TRUE(json_is_integer(id)); - json_t * method = json_object_get(request, "method"); - ASSERT_TRUE(json_is_string(method)); - ASSERT_STREQ("authenticate", json_string_value(method)); - 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)); - json_t * creds = json_array_get(params, 1); - ASSERT_TRUE(json_is_object(creds)); - json_t * username = json_object_get(creds, "username"); - ASSERT_TRUE(json_is_string(username)); - ASSERT_STREQ("Bob", json_string_value(username)); - json_t * password = json_object_get(creds, "password"); - ASSERT_TRUE(json_is_string(password)); - ASSERT_STREQ("secret", json_string_value(password)); - json_t * response = json_object(); - json_object_set(response, "id", id); - json_object_set_new(response, "result", json_object()); - server.SendMessage(response); - json_decref(request); + wf_client_dispose(client); +} + +TEST(AdapterClient, AuthenticationFailed) +{ + TimeoutWatcher watcher(TIMEOUT); + + MockInvokationHander handler; + WsServer2 server(handler, WF_PROTOCOL_NAME_PROVIDER_SERVER); + EXPECT_CALL(handler, Invoke(StrEq("authenticate"),_)).Times(1) + .WillOnce(Throw(std::runtime_error("authentication failed"))); + + MockAdapterClientCallback callback; + EXPECT_CALL(callback, Invoke(_, _, _)).Times(AnyNumber()); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS, _)).Times(1) + .WillOnce(Invoke(GetCredentials)); + bool called = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_AUTHENTICATION_FAILED, nullptr)).Times(1) + .WillOnce(Invoke([&called] (wf_client *, int, void *) mutable { + called = true; + })); + + wf_client * client = wf_client_create( + callback.GetCallbackFn(), callback.GetUserData()); + + + wf_client_connect(client, server.GetUrl().c_str()); + while (!server.IsConnected()) + { + watcher.check(); + wf_client_service(client); + } + + wf_client_authenticate(client); while (!called) { watcher.check(); wf_client_service(client); diff --git a/test/webfuse/utils/ws_server2.cc b/test/webfuse/utils/ws_server2.cc new file mode 100644 index 0000000..df1b772 --- /dev/null +++ b/test/webfuse/utils/ws_server2.cc @@ -0,0 +1,305 @@ +#include "webfuse/utils/ws_server2.hpp" +#include "webfuse/core/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; +}; + +} + +extern "C" +{ + +static int wf_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 +{ + +class WsServer2::Private : IServer +{ + Private(Private const &) = delete; + Private & operator=(Private const &) = delete; +public: + Private(IIvokationHandler & handler, std::string const & protocol, int port); + ~Private(); + bool IsConnected(); + std::string const & GetUrl() const; + 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: + void SendMessage(json_t * message); + static void Run(Private * self); + + IIvokationHandler & handler_; + std::string protocol_; + bool is_connected; + bool is_shutdown_requested; + lws * wsi_; + lws_context * ws_context; + lws_protocols ws_protocols[2]; + lws_context_creation_info info; + std::string url; + std::thread context; + std::mutex mutex; + std::queue writeQueue; +}; + +WsServer2::WsServer2( + IIvokationHandler& handler, + std::string const & protocol, + int port) +: d(new WsServer2::Private(handler, protocol, port)) +{ + +} + +WsServer2::~WsServer2() +{ + delete d; +} + +bool WsServer2::IsConnected() +{ + return d->IsConnected(); +} + +std::string const & WsServer2::GetUrl() const +{ + return d->GetUrl(); +} + +WsServer2::Private::Private( + IIvokationHandler & handler, + std::string const & protocol, + int port) +: handler_(handler) +, protocol_(protocol) +, is_connected(false) +, is_shutdown_requested(false) +, wsi_(nullptr) +{ + wf_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 = &wf_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); + + std::ostringstream stream; + struct lws_vhost * vhost = lws_create_vhost(ws_context, &info); + stream << "ws://localhost:" << lws_get_vhost_port(vhost) << "/"; + url = stream.str(); + + context = std::thread(&Run, this); +} + +WsServer2::Private::~Private() +{ + { + std::unique_lock lock(mutex); + is_shutdown_requested = true; + } + + lws_cancel_service(ws_context); + context.join(); + lws_context_destroy(ws_context); +} + +void WsServer2::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 WsServer2::Private::IsConnected() +{ + std::unique_lock lock(mutex); + return is_connected; +} + +void WsServer2::Private::OnConnected(lws * wsi) +{ + std::unique_lock lock(mutex); + is_connected = true; + wsi_ = wsi; +} + +void WsServer2::Private::OnConnectionClosed(lws * wsi) +{ + std::unique_lock lock(mutex); + if (wsi == wsi_) + { + is_connected = false; + wsi_ = nullptr; + } +} + +void WsServer2::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 WsServer2::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 WsServer2::Private::OnMessageReceived(struct lws * wsi, char const * data, size_t length) +{ + (void) wsi; + + json_t * request = json_loadb(data, length, JSON_DECODE_ANY, nullptr); + json_t * method = json_object_get(request, "method"); + json_t * params = json_object_get(request, "params"); + json_t * id = json_object_get(request, "id"); + + if (json_is_string(method) && json_is_array(params) && json_is_integer(id)) + { + json_t * response = json_object(); + + try + { + std::string result_text = handler_.Invoke(json_string_value(method), params); + json_t * result = json_loads(result_text.c_str(), JSON_DECODE_ANY, nullptr); + json_object_set_new(response, "result", result); + } + catch (...) + { + json_t * error = json_object(); + json_object_set_new(error, "code", json_integer(1)); + json_object_set_new(response, "error", error); + } + + json_object_set(response, "id", id); + SendMessage(response); + } + + json_decref(request); +} + +std::string const & WsServer2::Private::GetUrl() const +{ + return url; +} + + +} diff --git a/test/webfuse/utils/ws_server2.hpp b/test/webfuse/utils/ws_server2.hpp new file mode 100644 index 0000000..e19c47d --- /dev/null +++ b/test/webfuse/utils/ws_server2.hpp @@ -0,0 +1,36 @@ +#ifndef WF_TEST_UTILS_WS_SERVER2_HPP +#define WF_TEST_UTILS_WS_SERVER2_HPP + +#include +#include + +namespace webfuse_test +{ + +class IIvokationHandler +{ +public: + virtual ~IIvokationHandler() = default; + virtual std::string Invoke(char const * method, json_t * params) = 0; +}; + +class WsServer2 +{ + WsServer2(WsServer2 const &) = delete; + WsServer2 & operator=(WsServer2 const & ) = delete; +public: + WsServer2( + IIvokationHandler& handler, + std::string const & protocol, + int port = 0); + virtual ~WsServer2(); + bool IsConnected(); + std::string const & GetUrl() const; +private: + class Private; + Private * d; +}; + +} + +#endif From 4713ec3e934967f88da9c383a349af7636862aa7 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sat, 13 Jun 2020 17:08:05 +0200 Subject: [PATCH 21/37] fix: don't send messages if not connected --- lib/webfuse/adapter/impl/client_protocol.c | 13 +++++---- meson.build | 1 + test/webfuse/tests/adapter/test_client.cc | 27 +++++++++++++++++++ .../webfuse/utils/jansson_test_environment.cc | 16 +++++++++++ 4 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 test/webfuse/utils/jansson_test_environment.cc diff --git a/lib/webfuse/adapter/impl/client_protocol.c b/lib/webfuse/adapter/impl/client_protocol.c index 3bffaad..3eb1493 100644 --- a/lib/webfuse/adapter/impl/client_protocol.c +++ b/lib/webfuse/adapter/impl/client_protocol.c @@ -52,12 +52,15 @@ wf_impl_client_protocol_send( bool result = false; struct wf_client_protocol * protocol = user_data; - struct wf_message * message = wf_message_create(request); - if (NULL != message) + if (NULL != protocol->wsi) { - wf_slist_append(&protocol->messages, &message->item); - lws_callback_on_writable(protocol->wsi); - result = true; + struct wf_message * message = wf_message_create(request); + if (NULL != message) + { + wf_slist_append(&protocol->messages, &message->item); + lws_callback_on_writable(protocol->wsi); + result = true; + } } return result; diff --git a/meson.build b/meson.build index 3a7a1cd..873d55b 100644 --- a/meson.build +++ b/meson.build @@ -211,6 +211,7 @@ alltests = executable('alltests', 'test/webfuse/utils/static_filesystem.c', 'test/webfuse/utils/ws_server.cc', 'test/webfuse/utils/ws_server2.cc', + 'test/webfuse/utils/jansson_test_environment.cc', 'test/webfuse/mocks/fake_invokation_context.cc', 'test/webfuse/mocks/mock_authenticator.cc', 'test/webfuse/mocks/mock_request.cc', diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index e8ab17c..179df2b 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -208,6 +208,33 @@ TEST(AdapterClient, Authenticate) wf_client_dispose(client); } +TEST(AdapterClient, AuthenticateFailedWithoutConnect) +{ + TimeoutWatcher watcher(TIMEOUT); + + MockAdapterClientCallback callback; + EXPECT_CALL(callback, Invoke(_, _, _)).Times(AnyNumber()); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS, _)).Times(1) + .WillOnce(Invoke(GetCredentials)); + bool called = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_AUTHENTICATION_FAILED, nullptr)).Times(1) + .WillOnce(Invoke([&called] (wf_client *, int, void *) mutable { + called = true; + })); + + wf_client * client = wf_client_create( + callback.GetCallbackFn(), callback.GetUserData()); + + + wf_client_authenticate(client); + while (!called) { + watcher.check(); + wf_client_service(client); + } + + wf_client_dispose(client); +} + TEST(AdapterClient, AuthenticationFailed) { TimeoutWatcher watcher(TIMEOUT); diff --git a/test/webfuse/utils/jansson_test_environment.cc b/test/webfuse/utils/jansson_test_environment.cc new file mode 100644 index 0000000..24d091f --- /dev/null +++ b/test/webfuse/utils/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 From 51cb62841050f7c82ec7aeecf5c8f6ad8e380ee0 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sat, 13 Jun 2020 18:24:45 +0200 Subject: [PATCH 22/37] put wf_client into a separate thread for testing --- meson.build | 1 + test/webfuse/tests/adapter/test_client.cc | 131 +++------------------ test/webfuse/utils/adapter_client.cc | 136 ++++++++++++++++++++++ test/webfuse/utils/adapter_client.hpp | 31 +++++ 4 files changed, 185 insertions(+), 114 deletions(-) create mode 100644 test/webfuse/utils/adapter_client.cc create mode 100644 test/webfuse/utils/adapter_client.hpp diff --git a/meson.build b/meson.build index 873d55b..64bef56 100644 --- a/meson.build +++ b/meson.build @@ -211,6 +211,7 @@ alltests = executable('alltests', 'test/webfuse/utils/static_filesystem.c', 'test/webfuse/utils/ws_server.cc', 'test/webfuse/utils/ws_server2.cc', + 'test/webfuse/utils/adapter_client.cc', 'test/webfuse/utils/jansson_test_environment.cc', 'test/webfuse/mocks/fake_invokation_context.cc', 'test/webfuse/mocks/mock_authenticator.cc', diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index 179df2b..a8ad723 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -1,8 +1,8 @@ #include #include -#include "webfuse/adapter/client.h" -#include "webfuse/adapter/credentials.h" +#include "webfuse/utils/adapter_client.hpp" + #include "webfuse/adapter/credentials.h" #include "webfuse/core/protocol_names.h" #include "webfuse/utils/ws_server2.hpp" @@ -10,6 +10,7 @@ #include "webfuse/mocks/mock_invokation_handler.hpp" #include "webfuse/utils/timeout_watcher.hpp" +using webfuse_test::AdapterClient; using webfuse_test::WsServer2; using webfuse_test::MockInvokationHander; using webfuse_test::MockAdapterClientCallback; @@ -34,79 +35,6 @@ void GetCredentials(wf_client *, int, void * arg) wf_credentials_add(creds, "password", "secret"); } -enum class connection_state -{ - disconnected, - connected, - connecting -}; - -struct context -{ - connection_state state; -}; - -void callback( - wf_client * client, - int reason, - void * args) -{ - auto * ctx = reinterpret_cast(wf_client_get_userdata(client)); - - switch (reason) - { - case WF_CLIENT_CREATED: - ctx->state = connection_state::connecting; - wf_client_connect(client, "ws://dummy-server/"); - break; - case WF_CLIENT_CONNECTED: - ctx->state = connection_state::connected; - wf_client_authenticate(client); - break; - case WF_CLIENT_AUTHENTICATED: - wf_client_add_filesystem(client, ".", "test"); - break; - case WF_CLIENT_AUTHENTICATION_FAILED: - wf_client_disconnect(client); - break; - case WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS: - { - auto * credentials = reinterpret_cast(args); - wf_credentials_set_type(credentials, "username"); - wf_credentials_add(credentials, "user", "bob"); - wf_credentials_add(credentials, "password", "secret"); - } - break; - case WF_CLIENT_FILESYSTEM_ADDED: - // operational - break; - case WF_CLIENT_FILESYSTEM_ADD_FAILED: - wf_client_disconnect(client); - break; - case WF_CLIENT_DISCONNECTED: - ctx->state = connection_state::disconnected; - break; - default: - break; - } -} - -} - -TEST(AdapterClient, GeneralUsage) -{ - context ctx; - ctx.state = connection_state::connecting; - - wf_client * client = wf_client_create( - &callback, reinterpret_cast(&ctx)); - - while (ctx.state != connection_state::disconnected) - { - wf_client_service(client); - } - - wf_client_dispose(client); } TEST(AdapterClient, CreateAndDispose) @@ -141,25 +69,19 @@ TEST(AdapterClient, Connect) EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1); EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1); + AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), server.GetUrl()); - wf_client * client = wf_client_create( - callback.GetCallbackFn(), callback.GetUserData()); - - wf_client_connect(client, server.GetUrl().c_str()); + client.Connect(); while (!server.IsConnected()) { watcher.check(); - wf_client_service(client); } - wf_client_disconnect(client); + client.Disconnect(); while (server.IsConnected()) { watcher.check(); - wf_client_service(client); } - - wf_client_dispose(client); } TEST(AdapterClient, Authenticate) @@ -181,31 +103,24 @@ TEST(AdapterClient, Authenticate) called = true; })); - wf_client * client = wf_client_create( - callback.GetCallbackFn(), callback.GetUserData()); + AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), server.GetUrl()); - - wf_client_connect(client, server.GetUrl().c_str()); + client.Connect(); while (!server.IsConnected()) { watcher.check(); - wf_client_service(client); } - wf_client_authenticate(client); + client.Authenticate(); while (!called) { watcher.check(); - wf_client_service(client); } - wf_client_disconnect(client); + client.Disconnect(); while (server.IsConnected()) { watcher.check(); - wf_client_service(client); } - - wf_client_dispose(client); } TEST(AdapterClient, AuthenticateFailedWithoutConnect) @@ -222,17 +137,12 @@ TEST(AdapterClient, AuthenticateFailedWithoutConnect) called = true; })); - wf_client * client = wf_client_create( - callback.GetCallbackFn(), callback.GetUserData()); + AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), ""); - - wf_client_authenticate(client); + client.Authenticate(); while (!called) { watcher.check(); - wf_client_service(client); } - - wf_client_dispose(client); } TEST(AdapterClient, AuthenticationFailed) @@ -254,29 +164,22 @@ TEST(AdapterClient, AuthenticationFailed) called = true; })); - wf_client * client = wf_client_create( - callback.GetCallbackFn(), callback.GetUserData()); + AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), server.GetUrl()); - - wf_client_connect(client, server.GetUrl().c_str()); + client.Connect(); while (!server.IsConnected()) { watcher.check(); - wf_client_service(client); } - wf_client_authenticate(client); + client.Authenticate(); while (!called) { watcher.check(); - wf_client_service(client); } - wf_client_disconnect(client); + client.Disconnect(); while (server.IsConnected()) { watcher.check(); - wf_client_service(client); } - - wf_client_dispose(client); -} \ No newline at end of file +} diff --git a/test/webfuse/utils/adapter_client.cc b/test/webfuse/utils/adapter_client.cc new file mode 100644 index 0000000..ecc4360 --- /dev/null +++ b/test/webfuse/utils/adapter_client.cc @@ -0,0 +1,136 @@ +#include "webfuse/utils/adapter_client.hpp" +#include +#include +#include + +namespace +{ + +enum class Command +{ + run, + shutdown, + connect, + disconnect, + authenticate, + add_filesystem +}; + +} + +namespace webfuse_test +{ + +class AdapterClient::Private +{ +public: + Private( + wf_client_callback_fn * callback, + void * user_data, + std::string const & url) + : client(wf_client_create(callback, user_data)) + , url_(url) + , command(Command::run) + { + thread = std::thread(&Run, this); + } + + ~Private() + { + ApplyCommand(Command::shutdown); + thread.join(); + wf_client_dispose(client); + } + + void ApplyCommand(Command actual_command) + { + { + std::unique_lock lock(mutex); + command = actual_command; + } + + wf_client_interrupt(client); + } + +private: + static void Run(Private * self) + { + bool is_running = true; + while (is_running) + { + Command actual_command; + { + std::unique_lock lock(self->mutex); + actual_command = self->command; + self->command = Command::run; + } + + switch (actual_command) + { + case Command::run: + wf_client_service(self->client); + break; + case Command::connect: + wf_client_connect(self->client, self->url_.c_str()); + break; + case Command::disconnect: + wf_client_disconnect(self->client); + break; + case Command::authenticate: + wf_client_authenticate(self->client); + break; + case Command::add_filesystem: + wf_client_add_filesystem(self->client, "/tmp", "test"); + break; + case Command::shutdown: + // fall-through + default: + is_running = false; + break; + } + + } + } + + wf_client * client; + std::string url_; + Command command; + std::thread thread; + std::mutex mutex; +}; + +AdapterClient::AdapterClient( + wf_client_callback_fn * callback, + void * user_data, + std::string const & url) +: d(new Private(callback, user_data, url)) +{ + +} + +AdapterClient::~AdapterClient() +{ + delete d; +} + +void AdapterClient::Connect() +{ + d->ApplyCommand(Command::connect); +} + +void AdapterClient::Disconnect() +{ + d->ApplyCommand(Command::disconnect); +} + +void AdapterClient::Authenticate() +{ + d->ApplyCommand(Command::authenticate); +} + +void AdapterClient::AddFileSystem() +{ + d->ApplyCommand(Command::add_filesystem); +} + +} \ No newline at end of file diff --git a/test/webfuse/utils/adapter_client.hpp b/test/webfuse/utils/adapter_client.hpp new file mode 100644 index 0000000..3727aaa --- /dev/null +++ b/test/webfuse/utils/adapter_client.hpp @@ -0,0 +1,31 @@ +#ifndef WF_UTILS_ADAPTER_CLIENT_HPP +#define WF_UTILS_APAPTER_CLIENT_HPP + +#include "webfuse/adapter/client.h" +#include + +namespace webfuse_test +{ + +class AdapterClient +{ + AdapterClient(AdapterClient const &) = delete; + AdapterClient& operator=(AdapterClient const &) = delete; +public: + AdapterClient( + wf_client_callback_fn * callback, + void * user_data, + std::string const & url); + ~AdapterClient(); + void Connect(); + void Disconnect(); + void Authenticate(); + void AddFileSystem(); +private: + class Private; + Private * d; +}; + +} + +#endif From ed39db2d8d5133c5956245adb384b25cc65d0d8e Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sat, 13 Jun 2020 23:45:29 +0200 Subject: [PATCH 23/37] fix: minor fixes --- lib/webfuse/adapter/impl/client_protocol.c | 24 ++++++++++++---------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/webfuse/adapter/impl/client_protocol.c b/lib/webfuse/adapter/impl/client_protocol.c index 3eb1493..8f408d4 100644 --- a/lib/webfuse/adapter/impl/client_protocol.c +++ b/lib/webfuse/adapter/impl/client_protocol.c @@ -136,12 +136,13 @@ static int wf_impl_client_protocol_lws_callback( break; case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: protocol->is_connected = false; + protocol->wsi = NULL; protocol->callback(protocol->user_data, WF_CLIENT_DISCONNECTED, NULL); break; case LWS_CALLBACK_CLIENT_CLOSED: protocol->is_connected = false; - protocol->callback(protocol->user_data, WF_CLIENT_DISCONNECTED, NULL); protocol->wsi = NULL; + protocol->callback(protocol->user_data, WF_CLIENT_DISCONNECTED, NULL); break; case LWS_CALLBACK_CLIENT_RECEIVE: wf_impl_client_protocol_process(protocol, in, len); @@ -154,19 +155,20 @@ static int wf_impl_client_protocol_lws_callback( { result = 1; } - else if (!wf_slist_empty(&protocol->messages)) - { - struct wf_slist_item * item = wf_slist_remove_first(&protocol->messages); - struct wf_message * message = wf_container_of(item, struct wf_message, item); - lws_write(wsi, (unsigned char*) message->data, message->length, LWS_WRITE_TEXT); - wf_message_dispose(message); - - if (!wf_slist_empty(&protocol->messages)) + else if (!wf_slist_empty(&protocol->messages)) { - lws_callback_on_writable(wsi); + struct wf_slist_item * item = wf_slist_remove_first(&protocol->messages); + struct wf_message * message = wf_container_of(item, struct wf_message, item); + lws_write(wsi, (unsigned char*) message->data, message->length, LWS_WRITE_TEXT); + wf_message_dispose(message); + + if (!wf_slist_empty(&protocol->messages)) + { + lws_callback_on_writable(wsi); + } } } - } + break; default: break; } From edcad5dc4c35aa7393a9e76391270e6b5e7bce1b Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sat, 13 Jun 2020 23:47:52 +0200 Subject: [PATCH 24/37] fix: fixed deadlock in tests (wait for client to connect, instead on relying on the server) --- test/webfuse/tests/adapter/test_client.cc | 77 +++++++++++------------ test/webfuse/utils/timeout_watcher.cc | 14 +++++ test/webfuse/utils/timeout_watcher.hpp | 2 + 3 files changed, 54 insertions(+), 39 deletions(-) diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index a8ad723..5a224fd 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -66,22 +66,21 @@ TEST(AdapterClient, Connect) EXPECT_CALL(callback, Invoke(_, WF_CLIENT_GET_TLS_CONFIG, _)).Times(1); EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CLEANUP, nullptr)).Times(1); - EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1); - EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1); + bool connected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { connected = true; })); + + bool disconnected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { disconnected = true; })); AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), server.GetUrl()); client.Connect(); - while (!server.IsConnected()) - { - watcher.check(); - } + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return connected; })); client.Disconnect(); - while (server.IsConnected()) - { - watcher.check(); - } + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); } TEST(AdapterClient, Authenticate) @@ -95,32 +94,32 @@ TEST(AdapterClient, Authenticate) MockAdapterClientCallback callback; EXPECT_CALL(callback, Invoke(_, _, _)).Times(AnyNumber()); + + bool connected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { connected = true; })); + + bool disconnected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { disconnected = true; })); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS, _)).Times(1) .WillOnce(Invoke(GetCredentials)); - bool called = false; + + bool authenticated = false; EXPECT_CALL(callback, Invoke(_, WF_CLIENT_AUTHENTICATED, nullptr)).Times(1) - .WillOnce(Invoke([&called] (wf_client *, int, void *) mutable { - called = true; - })); + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { authenticated = true; })); AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), server.GetUrl()); client.Connect(); - while (!server.IsConnected()) - { - watcher.check(); - } + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return connected; })); client.Authenticate(); - while (!called) { - watcher.check(); - } + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return authenticated; })); client.Disconnect(); - while (server.IsConnected()) - { - watcher.check(); - } + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); } TEST(AdapterClient, AuthenticateFailedWithoutConnect) @@ -140,9 +139,7 @@ TEST(AdapterClient, AuthenticateFailedWithoutConnect) AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), ""); client.Authenticate(); - while (!called) { - watcher.check(); - } + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return called; })); } TEST(AdapterClient, AuthenticationFailed) @@ -156,8 +153,18 @@ TEST(AdapterClient, AuthenticationFailed) MockAdapterClientCallback callback; EXPECT_CALL(callback, Invoke(_, _, _)).Times(AnyNumber()); + + bool connected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { connected = true; })); + + bool disconnected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { disconnected = true; })); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS, _)).Times(1) .WillOnce(Invoke(GetCredentials)); + bool called = false; EXPECT_CALL(callback, Invoke(_, WF_CLIENT_AUTHENTICATION_FAILED, nullptr)).Times(1) .WillOnce(Invoke([&called] (wf_client *, int, void *) mutable { @@ -167,19 +174,11 @@ TEST(AdapterClient, AuthenticationFailed) AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), server.GetUrl()); client.Connect(); - while (!server.IsConnected()) - { - watcher.check(); - } + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return connected; })); client.Authenticate(); - while (!called) { - watcher.check(); - } + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return called; })); client.Disconnect(); - while (server.IsConnected()) - { - watcher.check(); - } + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); } diff --git a/test/webfuse/utils/timeout_watcher.cc b/test/webfuse/utils/timeout_watcher.cc index 8d9add7..705f1f3 100644 --- a/test/webfuse/utils/timeout_watcher.cc +++ b/test/webfuse/utils/timeout_watcher.cc @@ -1,5 +1,6 @@ #include "webfuse/utils/timeout_watcher.hpp" #include +#include using std::chrono::milliseconds; using std::chrono::duration_cast; @@ -41,4 +42,17 @@ void TimeoutWatcher::check() } } +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/utils/timeout_watcher.hpp b/test/webfuse/utils/timeout_watcher.hpp index 278d25f..3696dd1 100644 --- a/test/webfuse/utils/timeout_watcher.hpp +++ b/test/webfuse/utils/timeout_watcher.hpp @@ -2,6 +2,7 @@ #define WF_TEST_TIMEOUT_WATCHER_HPP #include +#include namespace webfuse_test { @@ -15,6 +16,7 @@ public: ~TimeoutWatcher(); bool isTimeout(); void check(); + bool waitUntil(std::function predicate); private: std::chrono::milliseconds startedAt; std::chrono::milliseconds timeout_; From 28f3f4ca477d5248700e995ce50932734c095cf0 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sun, 14 Jun 2020 10:39:33 +0200 Subject: [PATCH 25/37] added test to lookup file --- lib/webfuse/adapter/impl/client_protocol.c | 18 ++- test/webfuse/mocks/lookup_matcher.hpp | 50 ++++++++ test/webfuse/tests/adapter/test_client.cc | 138 +++++++++++++++++++++ test/webfuse/utils/adapter_client.cc | 17 ++- test/webfuse/utils/adapter_client.hpp | 1 + 5 files changed, 217 insertions(+), 7 deletions(-) create mode 100644 test/webfuse/mocks/lookup_matcher.hpp diff --git a/lib/webfuse/adapter/impl/client_protocol.c b/lib/webfuse/adapter/impl/client_protocol.c index 8f408d4..356cf35 100644 --- a/lib/webfuse/adapter/impl/client_protocol.c +++ b/lib/webfuse/adapter/impl/client_protocol.c @@ -146,6 +146,7 @@ static int wf_impl_client_protocol_lws_callback( break; case LWS_CALLBACK_CLIENT_RECEIVE: wf_impl_client_protocol_process(protocol, in, len); + break; case LWS_CALLBACK_SERVER_WRITEABLE: // fall-through case LWS_CALLBACK_CLIENT_WRITEABLE: @@ -169,6 +170,11 @@ static int wf_impl_client_protocol_lws_callback( } } break; + case LWS_CALLBACK_RAW_RX_FILE: + if ((NULL != protocol->filesystem) && (wsi == protocol->filesystem->wsi)) + { + wf_impl_filesystem_process_request(protocol->filesystem); + } default: break; } @@ -201,15 +207,17 @@ void wf_impl_client_protocol_cleanup( struct wf_client_protocol * protocol) { - if (NULL != protocol->filesystem) - { - wf_impl_filesystem_dispose(protocol->filesystem); - } - protocol->callback(protocol->user_data, WF_CLIENT_CLEANUP, NULL); + wf_jsonrpc_proxy_dispose(protocol->proxy); wf_timer_manager_dispose(protocol->timer_manager); wf_message_queue_cleanup(&protocol->messages); + + if (NULL != protocol->filesystem) + { + wf_impl_filesystem_dispose(protocol->filesystem); + protocol->filesystem = NULL; + } } void diff --git a/test/webfuse/mocks/lookup_matcher.hpp b/test/webfuse/mocks/lookup_matcher.hpp new file mode 100644 index 0000000..e9c5a55 --- /dev/null +++ b/test/webfuse/mocks/lookup_matcher.hpp @@ -0,0 +1,50 @@ +#ifndef WF_LOOKUP_MATCHER_HPP +#define WF_LOOKUP_MATCHER_HPP + +#include +#include +#include + +namespace webfuse_test +{ + +MATCHER_P2(Lookup, parent, name, "") +{ + if (!json_is_array(arg)) + { + *result_listener << "json array expected"; + return false; + } + + json_t * parent_ = json_array_get(arg, 1); + if (!json_is_integer(parent_)) + { + *result_listener << "parent is expected to be an integer"; + return false; + } + if (parent != json_integer_value(parent_)) + { + *result_listener << "parent mismatch: expected " << parent + << " but was " << json_integer_value(parent_); + return false; + } + + json_t * name_ = json_array_get(arg, 2); + if (!json_is_string(name_)) + { + *result_listener << "name is expected to be a string"; + return false; + } + if (0 != strcmp(name, json_string_value(name_))) + { + *result_listener << "name mismatch: expected \"" << name + << "\" but was \"" << json_string_value(name_) << "\""; + return false; + } + + return true; +} + +} + +#endif diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index 5a224fd..8d39f6c 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -9,12 +9,16 @@ #include "webfuse/mocks/mock_adapter_client_callback.hpp" #include "webfuse/mocks/mock_invokation_handler.hpp" #include "webfuse/utils/timeout_watcher.hpp" +#include "webfuse/tests/integration/file.hpp" +#include "webfuse/mocks/lookup_matcher.hpp" using webfuse_test::AdapterClient; using webfuse_test::WsServer2; using webfuse_test::MockInvokationHander; using webfuse_test::MockAdapterClientCallback; using webfuse_test::TimeoutWatcher; +using webfuse_test::File; +using webfuse_test::Lookup; using testing::_; using testing::Invoke; using testing::AnyNumber; @@ -182,3 +186,137 @@ TEST(AdapterClient, AuthenticationFailed) client.Disconnect(); ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); } + +TEST(AdapterClient, AddFileSystem) +{ + TimeoutWatcher watcher(TIMEOUT); + + MockInvokationHander handler; + WsServer2 server(handler, WF_PROTOCOL_NAME_PROVIDER_SERVER); + EXPECT_CALL(handler, Invoke(StrEq("add_filesystem"),_)).Times(1) + .WillOnce(Return("{\"id\": \"test\"}")); + EXPECT_CALL(handler, Invoke(StrEq("lookup"), _)).Times(AnyNumber()) + .WillRepeatedly(Throw(std::runtime_error("unknown"))); + + MockAdapterClientCallback callback; + EXPECT_CALL(callback, Invoke(_, _, _)).Times(AnyNumber()); + + bool connected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { connected = true; })); + + bool disconnected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { disconnected = true; })); + + bool called = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_FILESYSTEM_ADDED, nullptr)).Times(1) + .WillOnce(Invoke([&called] (wf_client *, int, void *) mutable { + called = true; + })); + + AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), server.GetUrl()); + + client.Connect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return connected; })); + + client.AddFileSystem(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return called; })); + + client.Disconnect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); +} + +TEST(AdapterClient, AddFileSystemFailed) +{ + TimeoutWatcher watcher(TIMEOUT); + + MockInvokationHander handler; + WsServer2 server(handler, WF_PROTOCOL_NAME_PROVIDER_SERVER); + EXPECT_CALL(handler, Invoke(StrEq("add_filesystem"),_)).Times(1) + .WillOnce(Throw(std::runtime_error("failed"))); + + MockAdapterClientCallback callback; + EXPECT_CALL(callback, Invoke(_, _, _)).Times(AnyNumber()); + + bool connected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { connected = true; })); + + bool disconnected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { disconnected = true; })); + + bool called = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_FILESYSTEM_ADD_FAILED, nullptr)).Times(1) + .WillOnce(Invoke([&called] (wf_client *, int, void *) mutable { + called = true; + })); + + AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), server.GetUrl()); + + client.Connect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return connected; })); + + client.AddFileSystem(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return called; })); + + client.Disconnect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); +} + +TEST(AdapterClient, LookupFile) +{ + TimeoutWatcher watcher(TIMEOUT); + + MockInvokationHander handler; + WsServer2 server(handler, WF_PROTOCOL_NAME_PROVIDER_SERVER); + EXPECT_CALL(handler, Invoke(StrEq("add_filesystem"),_)).Times(1) + .WillOnce(Return("{\"id\": \"test\"}")); + EXPECT_CALL(handler, Invoke(StrEq("lookup"), _)).Times(AnyNumber()) + .WillRepeatedly(Throw(std::runtime_error("unknown"))); + EXPECT_CALL(handler, Invoke(StrEq("lookup"), Lookup(1, "Hello.txt"))).Times(AnyNumber()) + .WillRepeatedly(Return( + "{" + "\"inode\": 2," + "\"mode\": 420," //0644 + "\"type\": \"file\"," + "\"size\": 42," + "\"atime\": 0," + "\"mtime\": 0," + "\"ctime\": 0" + "}" + )); + + MockAdapterClientCallback callback; + EXPECT_CALL(callback, Invoke(_, _, _)).Times(AnyNumber()); + + bool connected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { connected = true; })); + + bool disconnected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { disconnected = true; })); + + bool called = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_FILESYSTEM_ADDED, nullptr)).Times(1) + .WillOnce(Invoke([&called] (wf_client *, int, void *) mutable { + called = true; + })); + + AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), server.GetUrl()); + + client.Connect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return connected; })); + + client.AddFileSystem(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return called; })); + + std::string file_name = client.GetDir() + "/Hello.txt"; + File file(file_name); + ASSERT_TRUE(file.isFile()); + + client.Disconnect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); +} diff --git a/test/webfuse/utils/adapter_client.cc b/test/webfuse/utils/adapter_client.cc index ecc4360..d1dbb4e 100644 --- a/test/webfuse/utils/adapter_client.cc +++ b/test/webfuse/utils/adapter_client.cc @@ -1,7 +1,7 @@ #include "webfuse/utils/adapter_client.hpp" +#include "webfuse/utils/tempdir.hpp" #include #include -#include namespace { @@ -31,6 +31,7 @@ public: : client(wf_client_create(callback, user_data)) , url_(url) , command(Command::run) + , tempdir("webfuse_adpter_client") { thread = std::thread(&Run, this); } @@ -52,6 +53,11 @@ public: wf_client_interrupt(client); } + std::string GetDir() + { + return tempdir.path(); + } + private: static void Run(Private * self) { @@ -80,7 +86,7 @@ private: wf_client_authenticate(self->client); break; case Command::add_filesystem: - wf_client_add_filesystem(self->client, "/tmp", "test"); + wf_client_add_filesystem(self->client, self->tempdir.path(), "test"); break; case Command::shutdown: // fall-through @@ -95,6 +101,7 @@ private: wf_client * client; std::string url_; Command command; + TempDir tempdir; std::thread thread; std::mutex mutex; }; @@ -133,4 +140,10 @@ void AdapterClient::AddFileSystem() d->ApplyCommand(Command::add_filesystem); } +std::string AdapterClient::GetDir() const +{ + return d->GetDir(); +} + + } \ No newline at end of file diff --git a/test/webfuse/utils/adapter_client.hpp b/test/webfuse/utils/adapter_client.hpp index 3727aaa..ceaefcb 100644 --- a/test/webfuse/utils/adapter_client.hpp +++ b/test/webfuse/utils/adapter_client.hpp @@ -21,6 +21,7 @@ public: void Disconnect(); void Authenticate(); void AddFileSystem(); + std::string GetDir() const; private: class Private; Private * d; From b1013303e694a26fdebb97bb5751674a7354b0ae Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sun, 14 Jun 2020 13:16:28 +0200 Subject: [PATCH 26/37] clarified documentation about client to server requests --- doc/protocol.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/doc/protocol.md b/doc/protocol.md index 32eaa9d..7c686e4 100644 --- a/doc/protocol.md +++ b/doc/protocol.md @@ -221,14 +221,20 @@ Read from an open file. | "identiy" | Use data as is; note that JSON strings are UTF-8 encoded | | "base64" | data is base64 encoded | -## Requests (Provider -> Adapter) +## Requests (Client -> Server) + +_Note:_ The following requests are initiated by the client and +responded by the server. Depending on the use case, a provider +can be a client (e.g. a webbrowser which want's to inject a +filesystem to an IoT device) or a server (e.g. a cloud based +service, an adapter connects to and requests a filesystem). ### add_filesystem Adds a filesystem. - fs provider: {"method": "add_filesystem", "params": [], "id": } - webfuse daemon: {"result": {"id": }, "id": } + client: {"method": "add_filesystem", "params": [], "id": } + server: {"result": {"id": }, "id": } | Item | Data type | Description | | ----------- | ----------| ------------------------------- | @@ -237,10 +243,10 @@ Adds a filesystem. ### authtenticate Authenticate the provider. -If authentication is enabled, a provider must be authenticated by the adapter before filesystems can be added. +If authentication is enabled, a client must be authenticated by the server before filesystems can be added. - fs provider: {"method": "authenticate", "params": [, ], "id": } - webfuse daemon: {"result": {}, "id": } + client: {"method": "authenticate", "params": [, ], "id": } + server: {"result": {}, "id": } | Item | Data type | Description | | ----------- | ----------| ------------------------------- | From b2826e8325eaf3b3af283c73d311b22c55e20d09 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sun, 14 Jun 2020 13:41:10 +0200 Subject: [PATCH 27/37] enhanced API documentation --- doc/api.md | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 2 deletions(-) diff --git a/doc/api.md b/doc/api.md index 6cd8340..0c862d2 100644 --- a/doc/api.md +++ b/doc/api.md @@ -5,9 +5,10 @@ Please refer to the [build instructions](build.md) to generate API reference doc ## Contents -- [Authentication](#Authentication) +- [Authentication](#Authentication (Adapter Server)) +- [Adapter Client](#Adapter Client) -## Authentication +## Authentication (Adapter Server) By default, webfuse daemon will redirect each filesystem call to the first connected provider without any authentication. This might be good for testing purposes or when an external authentication mechanism is used. In some use cases, explicit authentication is needed. Therefore, authentication can be enabled within webfuse daemon. @@ -53,3 +54,107 @@ The authenticator type **username** is used to authenticate via username and pas - **password** refers to the password of the user **Note** that no further encryption is done, so this authenticator type should not be used over unencrypted websocket connections. + +## Adapter Client + +Webfuse also supports a client version of an adapter. This might be useful +to connect to a cloud based provider server and request a filesystem. + +The adapter client is driven by a callback function, which is triggered whenever +an event occurs, an adapter should take care of. + + static void client_callback( + struct wf_client * client, + int reason, + void * arg) + { + switch (reason) + { + // ... handle events + default: + break; + } + } + + // ... + void * user_data = ... + struct wf_client * client = wf_client_create(&client_callback, user_data); + +### Init and Cleanup + +There are two events definied to handle init and cleanup of clients: + +- `WF_CLIENT_INIT` +- `WF_CLIENT_CLEANUP` + +These two are the outer-most events and can be used for custom initialization +and cleanup. + +A thrid event, `WF_CLIENT_CREATED`, is triggered, when the client is fully created. +You might use this event to connect to a foreign provider. + +### Connection Status + +The connection status is relected by two events: + +- `WF_CLIENT_CONNECTED` +- `WF_CLIENT_DISCONNECTED` + +The disconnected event is also triggerd, when an attempt to connect fails. + +### Transport Layer Security + +During startup, the event `WF_CLIENT_GET_TLS_CONFIG` is triggered. +In this case, the `arg` parameter points to an instance of `struct wf_client_tlsconfig`. + +To enable TLS, set this struct. If the callback is ignorted or the struct is not +set, TLS is not active. + + static void client_callback( + struct wf_client * client, + int reason, + void * arg) + { + switch (reason) + { + // ... + case WF_CLIENT_GET_TLS_CONFIG: + { + struct wf_client_tlsconfig * tls = arg; + wf_client_tslconfig_set_keypath(tls, "/path/to/key.pem"); + wf_client_tslconfig_set_certpath(tls, "/path/to/cert.pem"); + wf_client_tslconfig_set_cafilepath(tls, "/path/to/ca_file.pem"); + } + break; + default: + break; + } + } + +### Authentication (Adapter Client) + +During `wf_client_authenticate` the event `WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS` +is triggered to query credentials for authentication. + +In this case, the `arg` paramter point to an instance of `struct wf_credentials`. + + static void client_callback( + struct wf_client * client, + int reason, + void * arg) + { + switch (reason) + { + // ... + case WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS: + { + struct wf_credentials * creds = arg; + wf_credentials_set_type(creds, "username"); + wf_credentials_add(creds, "username", "Bob"); + wf_credentials_add(creds, "password", "secret"); + } + break; + default: + break; + } + } From a92428cbe1cf4897d9dc9f91f798c8e5f1554114 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sun, 14 Jun 2020 14:17:36 +0200 Subject: [PATCH 28/37] added API docs --- include/webfuse/adapter/client.h | 96 ++++++++++++++++++++++ include/webfuse/adapter/client_callback.h | 37 ++++++--- include/webfuse/adapter/client_tlsconfig.h | 37 +++++++++ 3 files changed, 159 insertions(+), 11 deletions(-) diff --git a/include/webfuse/adapter/client.h b/include/webfuse/adapter/client.h index 1971106..29b4f17 100644 --- a/include/webfuse/adapter/client.h +++ b/include/webfuse/adapter/client.h @@ -14,42 +14,138 @@ extern "C" { #endif +//------------------------------------------------------------------------------ +/// \struct wf_client +/// \brief Adapter client +/// +/// An adapter client is used to connect to a remote provider server, e.g. a +/// cloud service, an request a filesystem to inject. +//------------------------------------------------------------------------------ struct wf_client; +//------------------------------------------------------------------------------ +/// \brief Created a new instance of an adapter client. +/// +/// During creation, the client is initialized in the following order: +/// - WF_CLIENT_INIT is triggered to initialize custom data +/// - WF_CLIENT_GET_TLS_CONFIG is triggered to query TLS configuration +/// - internal initialization is performed +/// - WF_CLIENT_CREATED is triggered +/// +/// Therefore, the client should not be used, unless WF_CLIENT_CREATED is +/// triggered. +/// +/// When TLS configuration is queried, a pointer to an instance if +/// \see wf_client_tlsconfig is provided to be set by the user. +/// +/// \param callback Pointer to the callback function. +/// \param user_data Pointer to user data. +/// +/// \return Newly created instance of an adapter client. +//------------------------------------------------------------------------------ extern WF_API struct wf_client * wf_client_create( wf_client_callback_fn * callback, void * user_data); +//------------------------------------------------------------------------------ +/// \brief Disposes an adapter client. +/// +/// \param client Pointer to the client. +//------------------------------------------------------------------------------ extern WF_API void wf_client_dispose( struct wf_client * client); +//------------------------------------------------------------------------------ +/// \brief Get user data. +/// +/// \param client Pointer to the client. +//------------------------------------------------------------------------------ extern WF_API void * wf_client_get_userdata( struct wf_client * client); +//------------------------------------------------------------------------------ +/// \brief Triggers the client. +/// +/// \param client Pointer to the client. +//------------------------------------------------------------------------------ extern WF_API void wf_client_service( struct wf_client * client); +//------------------------------------------------------------------------------ +/// \brief Interrupts a service call. +/// +/// Wakes up the client. +/// +/// \note This is the only function that can be used safely from another +// thread. +/// +/// \param client Pointer to the client. +//------------------------------------------------------------------------------ extern WF_API void wf_client_interrupt( struct wf_client * client); +//------------------------------------------------------------------------------ +/// \brief Connects to a foreign server. +/// +/// Starts to connect. The callback is triggered, when connect is finised: +/// - WF_CLIENT_CONNECTED on success +/// - WF_CLIENT_DISCONNECTED on connect error +/// +/// \param client Pointer to the client. +/// \param url URL of the remote ppovider. +//------------------------------------------------------------------------------ extern WF_API void wf_client_connect( struct wf_client * client, char const * url); +//------------------------------------------------------------------------------ +/// \brief Disconnects from the foreign server. +/// +/// The event WF_CLIENT_DISCONNECTED is triggered, when disconnected. +/// +/// \param client Pointer to the client. +//------------------------------------------------------------------------------ extern WF_API void wf_client_disconnect( struct wf_client * client); +//------------------------------------------------------------------------------ +/// \brief Authenticated the client. +/// +/// During authentication, credentials are queries via +/// WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS event. In that case a pointer to an +/// instance of wf_client_tlsconfig is provided to set credentials. +/// +/// When authentications finishes, an event is triggered: +/// - WF_CLIENT_AUTHENTICATED on success +/// - WF_CLIENT_AUTHENTICATION_FAILED on failure +/// +/// \param client Pointer to the client. +//------------------------------------------------------------------------------ extern WF_API void wf_client_authenticate( struct wf_client * client); +//------------------------------------------------------------------------------ +/// \brief Add a filesystem. +/// +/// After add filesystem finished, an event is triggered: +/// - WF_CLIENT_FILESYSTEM_ADDED on success +/// - WF_CLIENT_FILESYSTEM_ADD_FAILED on failure +/// +/// \note Currently, only one file system is supported by the client. +/// +/// \param client Pointer to the client. +/// \param local_path Local path where the filesystem should be places +/// (must be an exististing and empty directory). +/// \param name Name of the filesystem (identifier sent to the provider). +//------------------------------------------------------------------------------ extern WF_API void wf_client_add_filesystem( struct wf_client * client, diff --git a/include/webfuse/adapter/client_callback.h b/include/webfuse/adapter/client_callback.h index 90aabab..ad8bd86 100644 --- a/include/webfuse/adapter/client_callback.h +++ b/include/webfuse/adapter/client_callback.h @@ -1,3 +1,8 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file adapter/client_callbak.h +/// \brief Callback of adapter clients. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WF_ADAPTER_CLIENT_CALLBACK_H #define WF_ADAPTER_CLIENT_CALLBACK_H @@ -7,24 +12,34 @@ extern "C" #endif -#define WF_CLIENT_INIT 0x0001 -#define WF_CLIENT_CLEANUP 0x0002 -#define WF_CLIENT_CREATED 0x0003 +#define WF_CLIENT_INIT 0x0001 ///< Out-most initialization of a client +#define WF_CLIENT_CLEANUP 0x0002 ///< Out-most cleanup of a client +#define WF_CLIENT_CREATED 0x0003 ///< Client is fully initialized an can be used -#define WF_CLIENT_CONNECTED 0x0011 -#define WF_CLIENT_DISCONNECTED 0x0012 +#define WF_CLIENT_CONNECTED 0x0011 ///< Connection to a foreign provider established +#define WF_CLIENT_DISCONNECTED 0x0012 ///< Connection closed or connect failed -#define WF_CLIENT_AUTHENTICATED 0x0021 -#define WF_CLIENT_AUTHENTICATION_FAILED 0x0022 -#define WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS 0x0023 +#define WF_CLIENT_AUTHENTICATED 0x0021 ///< Authentication succeeded +#define WF_CLIENT_AUTHENTICATION_FAILED 0x0022 ///< Authentication failed +#define WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS 0x0023 ///< Query credentials (\see wf_client_authenticate) -#define WF_CLIENT_FILESYSTEM_ADDED 0x0031 -#define WF_CLIENT_FILESYSTEM_ADD_FAILED 0x0032 +#define WF_CLIENT_FILESYSTEM_ADDED 0x0031 ///< File system added successfully +#define WF_CLIENT_FILESYSTEM_ADD_FAILED 0x0032 ///< Failed to add file system -#define WF_CLIENT_GET_TLS_CONFIG 0x0041 +#define WF_CLIENT_GET_TLS_CONFIG 0x0041 ///< Query TLS config (\see wf_client_create) struct wf_client; +//------------------------------------------------------------------------------ +/// \brief Client callback function +/// +/// This function is triggered whenever an event occurs the client should +/// be aware of. +/// +/// \param client Pointer to the client +/// \param reason Event, that triggered the callback +/// \param args Event-specific arguments +//------------------------------------------------------------------------------ typedef void wf_client_callback_fn( struct wf_client * client, int reason, diff --git a/include/webfuse/adapter/client_tlsconfig.h b/include/webfuse/adapter/client_tlsconfig.h index c2b5298..550b664 100644 --- a/include/webfuse/adapter/client_tlsconfig.h +++ b/include/webfuse/adapter/client_tlsconfig.h @@ -1,3 +1,8 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file adapter/client_tslconfig.h +/// \brief Configuration of TLS (Transport Layer Security) for adapter clients. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WF_ADAPTER_CLIENT_TLSCONFIG_H #define WF_ADAPTER_CLIENT_TLSCONFIG_H @@ -8,18 +13,50 @@ extern "C" { #endif +//------------------------------------------------------------------------------ +/// \struct wf_client_tlsconfig +/// \brief TLS configuration of the client. +/// +/// TLS configuration is queried during initialization of a client via +/// WF_CLIENT_GET_TLS_CONFIG event. +/// +/// \see WF_CLIENT_GET_TLS_CONFIG +/// \see wf_client_create +//------------------------------------------------------------------------------ struct wf_client_tlsconfig; +//------------------------------------------------------------------------------ +/// \brief Sets the path to the private key. +/// +/// \note To enable TLS both, key_path and cert_path, must be specified. +/// +/// \param config Pointer to config. +/// \param key_path Path to private key file (in PEM format). +//------------------------------------------------------------------------------ extern WF_API void wf_client_tlsconfig_set_keypath( struct wf_client_tlsconfig * config, char const * key_path); +//------------------------------------------------------------------------------ +/// \brief Sets the path to the clients certificate. +/// +/// \note To enable TLS both, key_path and cert_path, must be specified. +/// +/// \param config Pointer to the config. +/// \param cert_path Path the the clients certificate (in PEM format). +//------------------------------------------------------------------------------ extern WF_API void wf_client_tlsconfig_set_certpath( struct wf_client_tlsconfig * config, char const * cert_path); +//------------------------------------------------------------------------------ +/// \brief Sets the path the CA file. +/// +/// \param config Pointer to the config. +/// \param cafile_path Path to CA file (in PEM format). +//------------------------------------------------------------------------------ extern WF_API void wf_client_tlsconfig_set_cafilepath( struct wf_client_tlsconfig * config, From f768418548231e7d120cadafd6efc0b42c198dbd Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sun, 14 Jun 2020 14:18:52 +0200 Subject: [PATCH 29/37] removed dead code --- lib/webfuse/provider/impl/client.c | 6 ------ lib/webfuse/provider/impl/client.h | 3 --- 2 files changed, 9 deletions(-) diff --git a/lib/webfuse/provider/impl/client.c b/lib/webfuse/provider/impl/client.c index e7c6e81..2c584e9 100644 --- a/lib/webfuse/provider/impl/client.c +++ b/lib/webfuse/provider/impl/client.c @@ -82,12 +82,6 @@ void wfp_impl_client_disconnect( wfp_impl_client_protocol_disconnect(&client->protocol); } -bool wfp_impl_client_is_connected( - struct wfp_client * client) -{ - return client->protocol.is_connected; -} - void wfp_impl_client_service( struct wfp_client * client) { diff --git a/lib/webfuse/provider/impl/client.h b/lib/webfuse/provider/impl/client.h index 46e41f9..0dfc64e 100644 --- a/lib/webfuse/provider/impl/client.h +++ b/lib/webfuse/provider/impl/client.h @@ -34,9 +34,6 @@ extern void wfp_impl_client_disconnect( extern void wfp_impl_client_dispose( struct wfp_client * client); -extern bool wfp_impl_client_is_connected( - struct wfp_client * client); - extern void wfp_impl_client_service( struct wfp_client * client); From 7311253dfd5a2c42244df2c4889edfa594d16a03 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sun, 14 Jun 2020 14:20:20 +0200 Subject: [PATCH 30/37] add adapter client implementation --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index e62c3f8..a15b4a4 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,7 @@ ### New Features +* Add adapter client implementation * Allow system to choose port of webfuse server (by setting port in `wf_server_config` to 0) ### Fixes From 71956c45745bce9499149d93f2b083f19e3fb33b Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sun, 14 Jun 2020 19:28:05 +0200 Subject: [PATCH 31/37] removed arm32v7/minsize config (build time too long) --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f021b2e..994f20a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,6 @@ env: - DISTRIB_ID=ubuntu VARIANT=coverage MARCH=x86_64 CHECK_TARGET=check - DISTRIB_ID=ubuntu VARIANT=release MARCH=x86_64 CHECK_TARGET=memcheck - DISTRIB_ID=ubuntu VARIANT=debug MARCH=arm32v7 CHECK_TARGET=check - - DISTRIB_ID=ubuntu VARIANT=minsize MARCH=arm32v7 CHECK_TARGET=check - DISTRIB_ID=alpine VARIANT=debug MARCH=x86_64 CHECK_TARGET=check before_script: - make -j4 DISTRIB_ID=$DISTRIB_ID VARIANT=$VARIANT MARCH=$MARCH From 8de8ec0003773750b58157b63966dc8105748ffe Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sun, 14 Jun 2020 19:35:50 +0200 Subject: [PATCH 32/37] added test to connect adapter client via TLS --- test/webfuse/tests/adapter/test_client.cc | 40 ++++++++++++++++++++++- test/webfuse/utils/ws_server2.cc | 20 +++++++++--- test/webfuse/utils/ws_server2.hpp | 3 +- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index 8d39f6c..1002187 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -2,7 +2,7 @@ #include #include "webfuse/utils/adapter_client.hpp" - +#include "webfuse/adapter/client_tlsconfig.h" #include "webfuse/adapter/credentials.h" #include "webfuse/core/protocol_names.h" #include "webfuse/utils/ws_server2.hpp" @@ -87,6 +87,44 @@ TEST(AdapterClient, Connect) ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); } +TEST(AdapterClient, ConnectWithTls) +{ + TimeoutWatcher watcher(TIMEOUT); + + MockInvokationHander handler; + WsServer2 server(handler, WF_PROTOCOL_NAME_PROVIDER_SERVER, 0, true); + EXPECT_CALL(handler, Invoke(_,_)).Times(0); + + MockAdapterClientCallback callback; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_INIT, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CREATED, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_GET_TLS_CONFIG, _)).Times(1) + .WillOnce(Invoke([](wf_client *, int, void * arg) { + auto * tls = reinterpret_cast(arg); + wf_client_tlsconfig_set_keypath (tls, "client-key.pem"); + wf_client_tlsconfig_set_certpath(tls, "client-cert.pem"); + wf_client_tlsconfig_set_cafilepath(tls, "server-cert.pem"); + })); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CLEANUP, nullptr)).Times(1); + + bool connected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { connected = true; })); + + bool disconnected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { disconnected = true; })); + + AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), server.GetUrl()); + + client.Connect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return connected; })); + + client.Disconnect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); +} + + TEST(AdapterClient, Authenticate) { TimeoutWatcher watcher(TIMEOUT); diff --git a/test/webfuse/utils/ws_server2.cc b/test/webfuse/utils/ws_server2.cc index df1b772..7cb28f1 100644 --- a/test/webfuse/utils/ws_server2.cc +++ b/test/webfuse/utils/ws_server2.cc @@ -75,7 +75,7 @@ class WsServer2::Private : IServer Private(Private const &) = delete; Private & operator=(Private const &) = delete; public: - Private(IIvokationHandler & handler, std::string const & protocol, int port); + Private(IIvokationHandler & handler, std::string const & protocol, int port, bool enable_tls); ~Private(); bool IsConnected(); std::string const & GetUrl() const; @@ -105,8 +105,9 @@ private: WsServer2::WsServer2( IIvokationHandler& handler, std::string const & protocol, - int port) -: d(new WsServer2::Private(handler, protocol, port)) + int port, + bool enable_tls) +: d(new WsServer2::Private(handler, protocol, port, enable_tls)) { } @@ -129,7 +130,8 @@ std::string const & WsServer2::GetUrl() const WsServer2::Private::Private( IIvokationHandler & handler, std::string const & protocol, - int port) + int port, + bool enable_tls) : handler_(handler) , protocol_(protocol) , is_connected(false) @@ -154,11 +156,19 @@ WsServer2::Private::Private( info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; info.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS; + if (enable_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"; + } + ws_context = lws_create_context(&info); std::ostringstream stream; struct lws_vhost * vhost = lws_create_vhost(ws_context, &info); - stream << "ws://localhost:" << lws_get_vhost_port(vhost) << "/"; + stream << (enable_tls ? "wss://" : "ws://") + << "localhost:" << lws_get_vhost_port(vhost) << "/"; url = stream.str(); context = std::thread(&Run, this); diff --git a/test/webfuse/utils/ws_server2.hpp b/test/webfuse/utils/ws_server2.hpp index e19c47d..b09c573 100644 --- a/test/webfuse/utils/ws_server2.hpp +++ b/test/webfuse/utils/ws_server2.hpp @@ -22,7 +22,8 @@ public: WsServer2( IIvokationHandler& handler, std::string const & protocol, - int port = 0); + int port = 0, + bool enable_tls = false); virtual ~WsServer2(); bool IsConnected(); std::string const & GetUrl() const; From 01a9488f6ea7074695bd3dd1dc32e7369dbd7a76 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sun, 14 Jun 2020 20:20:14 +0200 Subject: [PATCH 33/37] add test for connect failure --- test/webfuse/tests/adapter/test_client.cc | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index 1002187..aa61867 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -124,6 +124,27 @@ TEST(AdapterClient, ConnectWithTls) ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); } +TEST(AdapterClient, FailedToConnectInvalidPort) +{ + TimeoutWatcher watcher(TIMEOUT); + + + MockAdapterClientCallback callback; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_INIT, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CREATED, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_GET_TLS_CONFIG, _)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CLEANUP, nullptr)).Times(1); + + bool disconnected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { disconnected = true; })); + + AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), "ws://localhost:4/"); + + client.Connect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); +} + TEST(AdapterClient, Authenticate) { From 60141c4d8a5aab71c376e5b3879351460ff348b3 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sun, 14 Jun 2020 20:28:39 +0200 Subject: [PATCH 34/37] chore: replaced wf_string_create by asprintf --- lib/webfuse/adapter/impl/filesystem.c | 2 -- lib/webfuse/core/string.c | 32 -------------------------- lib/webfuse/core/string.h | 21 ----------------- meson.build | 2 -- test/webfuse/tests/core/test_string.cc | 18 --------------- test/webfuse/utils/tempdir.cc | 4 ++-- 6 files changed, 2 insertions(+), 77 deletions(-) delete mode 100644 lib/webfuse/core/string.c delete mode 100644 lib/webfuse/core/string.h delete mode 100644 test/webfuse/tests/core/test_string.cc diff --git a/lib/webfuse/adapter/impl/filesystem.c b/lib/webfuse/adapter/impl/filesystem.c index 4fa1d38..453f67f 100644 --- a/lib/webfuse/adapter/impl/filesystem.c +++ b/lib/webfuse/adapter/impl/filesystem.c @@ -9,8 +9,6 @@ #include "webfuse/adapter/impl/session.h" #include "webfuse/adapter/impl/mountpoint.h" -#include "webfuse/core/string.h" - #include #include diff --git a/lib/webfuse/core/string.c b/lib/webfuse/core/string.c deleted file mode 100644 index 84608cb..0000000 --- a/lib/webfuse/core/string.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "webfuse/core/string.h" - -#include -#include - -char * wf_create_string(char const * format, ...) -{ - char * result = NULL; - - va_list measure_args; - va_start(measure_args, format); - char buffer; - int needed = vsnprintf(&buffer, 1, format, measure_args); - va_end(measure_args); - - if (0 <= needed) - { - result = malloc(needed + 1); - va_list args; - va_start(args, format); - int count = vsnprintf(result, needed + 1, format, args); - va_end(args); - - if ((count < 0) || (needed < count)) - { - free(result); - result = NULL; - } - } - - return result; -} diff --git a/lib/webfuse/core/string.h b/lib/webfuse/core/string.h deleted file mode 100644 index 00a686b..0000000 --- a/lib/webfuse/core/string.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef WF_CORE_STRING_H -#define WF_CORE_STRING_H - -#ifndef __cplusplus -#include -#else -#include -#endif - -#ifdef __cplusplus -extern "C" -{ -#endif - -extern char * wf_create_string(char const * format, ...); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/meson.build b/meson.build index 64bef56..86cef0e 100644 --- a/meson.build +++ b/meson.build @@ -25,7 +25,6 @@ webfuse_core = static_library('webfuse_core', 'lib/webfuse/core/message.c', 'lib/webfuse/core/message_queue.c', 'lib/webfuse/core/status.c', - 'lib/webfuse/core/string.c', 'lib/webfuse/core/base64.c', 'lib/webfuse/core/lws_log.c', 'lib/webfuse/core/json_util.c', @@ -224,7 +223,6 @@ alltests = executable('alltests', 'test/webfuse/mocks/mock_adapter_client_callback.cc', 'test/webfuse//tests/core/test_util.cc', 'test/webfuse/tests/core/test_container_of.cc', - 'test/webfuse/tests/core/test_string.cc', 'test/webfuse/tests/core/test_slist.cc', 'test/webfuse/tests/core/test_base64.cc', 'test/webfuse/tests/core/test_status.cc', diff --git a/test/webfuse/tests/core/test_string.cc b/test/webfuse/tests/core/test_string.cc deleted file mode 100644 index 17e501a..0000000 --- a/test/webfuse/tests/core/test_string.cc +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include - -#include "webfuse/core/string.h" - -TEST(wf_string_create, Default) -{ - char * value = wf_create_string("test %s/%d", "hello", 42); - ASSERT_STREQ("test hello/42", value); - free(value); -} - -TEST(wf_string_create, EmptyString) -{ - char * value = wf_create_string(""); - ASSERT_STREQ("", value); - free(value); -} diff --git a/test/webfuse/utils/tempdir.cc b/test/webfuse/utils/tempdir.cc index 31dce25..46b78a7 100644 --- a/test/webfuse/utils/tempdir.cc +++ b/test/webfuse/utils/tempdir.cc @@ -1,7 +1,7 @@ -#include "webfuse/core/string.h" #include "webfuse/utils/tempdir.hpp" #include +#include #include #include @@ -9,8 +9,8 @@ namespace webfuse_test { TempDir::TempDir(char const * prefix) -: path_(wf_create_string("/tmp/%s_XXXXXX", prefix)) { + asprintf(&path_, "/tmp/%s_XXXXXX", prefix); char * result = mkdtemp(path_); if (NULL == result) { From 0502580850639ec890eecb5f2f46479945320663 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sun, 14 Jun 2020 21:15:39 +0200 Subject: [PATCH 35/37] added test for dirbuffer --- meson.build | 1 + test/webfuse/tests/provider/test_dirbuffer.cc | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 test/webfuse/tests/provider/test_dirbuffer.cc diff --git a/meson.build b/meson.build index 86cef0e..4842fea 100644 --- a/meson.build +++ b/meson.build @@ -244,6 +244,7 @@ alltests = executable('alltests', 'test/webfuse/tests/adapter/operation/test_getattr.cc', 'test/webfuse/tests/adapter/operation/test_lookup.cc', 'test/webfuse/tests/provider/test_client_protocol.cc', + 'test/webfuse/tests/provider/test_dirbuffer.cc', 'test/webfuse/tests/provider/operation/test_close.cc', 'test/webfuse/tests/provider/operation/test_getattr.cc', 'test/webfuse/tests/provider/operation/test_lookup.cc', diff --git a/test/webfuse/tests/provider/test_dirbuffer.cc b/test/webfuse/tests/provider/test_dirbuffer.cc new file mode 100644 index 0000000..b4dfe58 --- /dev/null +++ b/test/webfuse/tests/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); +} From 4e42c856c70dab380ede4f8aaa56794b7175071d Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Mon, 15 Jun 2020 16:24:36 +0200 Subject: [PATCH 36/37] added unit tests for add_filesystem request --- test/webfuse/tests/adapter/test_client.cc | 130 ++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index aa61867..abafa52 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -286,6 +286,136 @@ TEST(AdapterClient, AddFileSystem) ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); } +TEST(AdapterClient, FailToAddFileSystemTwice) +{ + TimeoutWatcher watcher(TIMEOUT); + + MockInvokationHander handler; + WsServer2 server(handler, WF_PROTOCOL_NAME_PROVIDER_SERVER); + EXPECT_CALL(handler, Invoke(StrEq("add_filesystem"),_)).Times(1) + .WillOnce(Return("{\"id\": \"test\"}")); + EXPECT_CALL(handler, Invoke(StrEq("lookup"), _)).Times(AnyNumber()) + .WillRepeatedly(Throw(std::runtime_error("unknown"))); + + MockAdapterClientCallback callback; + EXPECT_CALL(callback, Invoke(_, _, _)).Times(AnyNumber()); + + bool connected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { connected = true; })); + + bool disconnected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { disconnected = true; })); + + bool filesystem_added = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_FILESYSTEM_ADDED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { + filesystem_added = true; + })); + + bool filesystem_add_failed = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_FILESYSTEM_ADD_FAILED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { + filesystem_add_failed = true; + })); + + AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), server.GetUrl()); + + client.Connect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return connected; })); + + client.AddFileSystem(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return filesystem_added; })); + + client.AddFileSystem(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return filesystem_add_failed; })); + + client.Disconnect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); +} + +TEST(AdapterClient, FailToAddFileSystemMissingId) +{ + TimeoutWatcher watcher(TIMEOUT); + + MockInvokationHander handler; + WsServer2 server(handler, WF_PROTOCOL_NAME_PROVIDER_SERVER); + EXPECT_CALL(handler, Invoke(StrEq("add_filesystem"),_)).Times(1) + .WillOnce(Return("{}")); + EXPECT_CALL(handler, Invoke(StrEq("lookup"), _)).Times(AnyNumber()) + .WillRepeatedly(Throw(std::runtime_error("unknown"))); + + MockAdapterClientCallback callback; + EXPECT_CALL(callback, Invoke(_, _, _)).Times(AnyNumber()); + + bool connected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { connected = true; })); + + bool disconnected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { disconnected = true; })); + + bool filesystem_add_failed = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_FILESYSTEM_ADD_FAILED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { + filesystem_add_failed = true; + })); + + AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), server.GetUrl()); + + client.Connect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return connected; })); + + client.AddFileSystem(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return filesystem_add_failed; })); + + client.Disconnect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); +} + +TEST(AdapterClient, FailToAddFileSystemIdNotString) +{ + TimeoutWatcher watcher(TIMEOUT); + + MockInvokationHander handler; + WsServer2 server(handler, WF_PROTOCOL_NAME_PROVIDER_SERVER); + EXPECT_CALL(handler, Invoke(StrEq("add_filesystem"),_)).Times(1) + .WillOnce(Return("{\"id\": 42}")); + EXPECT_CALL(handler, Invoke(StrEq("lookup"), _)).Times(AnyNumber()) + .WillRepeatedly(Throw(std::runtime_error("unknown"))); + + MockAdapterClientCallback callback; + EXPECT_CALL(callback, Invoke(_, _, _)).Times(AnyNumber()); + + bool connected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { connected = true; })); + + bool disconnected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { disconnected = true; })); + + bool filesystem_add_failed = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_FILESYSTEM_ADD_FAILED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { + filesystem_add_failed = true; + })); + + AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), server.GetUrl()); + + client.Connect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return connected; })); + + client.AddFileSystem(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return filesystem_add_failed; })); + + client.Disconnect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); +} + + TEST(AdapterClient, AddFileSystemFailed) { TimeoutWatcher watcher(TIMEOUT); From 6bafbdd43aa61f00f0b6bb9c3100964abaccf4bb Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Mon, 15 Jun 2020 16:58:04 +0200 Subject: [PATCH 37/37] added unit tests --- test/webfuse/tests/adapter/test_client.cc | 68 +++++++++++++++++++++++ test/webfuse/utils/ws_server2.cc | 32 ++++++++--- test/webfuse/utils/ws_server2.hpp | 2 + 3 files changed, 94 insertions(+), 8 deletions(-) diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index abafa52..78cf7b2 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -87,6 +87,74 @@ TEST(AdapterClient, Connect) ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); } +TEST(AdapterClient, IgnoreNonJsonMessage) +{ + TimeoutWatcher watcher(TIMEOUT); + + MockInvokationHander handler; + WsServer2 server(handler, WF_PROTOCOL_NAME_PROVIDER_SERVER); + EXPECT_CALL(handler, Invoke(_,_)).Times(0); + + MockAdapterClientCallback callback; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_INIT, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CREATED, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_GET_TLS_CONFIG, _)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CLEANUP, nullptr)).Times(1); + + bool connected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { connected = true; })); + + bool disconnected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { disconnected = true; })); + + AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), server.GetUrl()); + + client.Connect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return connected; })); + + server.SendMessage("brummni"); + + client.Disconnect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); +} + + +TEST(AdapterClient, IgnoreInvalidJsonMessage) +{ + TimeoutWatcher watcher(TIMEOUT); + + MockInvokationHander handler; + WsServer2 server(handler, WF_PROTOCOL_NAME_PROVIDER_SERVER); + EXPECT_CALL(handler, Invoke(_,_)).Times(0); + + MockAdapterClientCallback callback; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_INIT, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CREATED, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_GET_TLS_CONFIG, _)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CLEANUP, nullptr)).Times(1); + + bool connected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { connected = true; })); + + bool disconnected = false; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1) + .WillOnce(Invoke([&] (wf_client *, int, void *) mutable { disconnected = true; })); + + AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), server.GetUrl()); + + client.Connect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return connected; })); + + json_t * invalid_request = json_object(); + server.SendMessage(invalid_request); + + client.Disconnect(); + ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; })); +} + TEST(AdapterClient, ConnectWithTls) { TimeoutWatcher watcher(TIMEOUT); diff --git a/test/webfuse/utils/ws_server2.cc b/test/webfuse/utils/ws_server2.cc index 7cb28f1..21d4a6e 100644 --- a/test/webfuse/utils/ws_server2.cc +++ b/test/webfuse/utils/ws_server2.cc @@ -70,7 +70,7 @@ static int wf_test_utils_ws_server_callback( namespace webfuse_test { -class WsServer2::Private : IServer +class WsServer2::Private : public IServer { Private(Private const &) = delete; Private & operator=(Private const &) = delete; @@ -84,8 +84,9 @@ public: void OnMessageReceived(struct lws * wsi, char const * data, size_t length) override; void OnWritable(struct lws * wsi) override; -private: + void SendMessage(char const * message); void SendMessage(json_t * message); +private: static void Run(Private * self); IIvokationHandler & handler_; @@ -127,6 +128,17 @@ std::string const & WsServer2::GetUrl() const return d->GetUrl(); } +void WsServer2::SendMessage(char const * message) +{ + d->SendMessage(message); +} + +void WsServer2::SendMessage(json_t * message) +{ + d->SendMessage(message); +} + + WsServer2::Private::Private( IIvokationHandler & handler, std::string const & protocol, @@ -249,8 +261,7 @@ void WsServer2::Private::OnWritable(struct lws * wsi) } } - -void WsServer2::Private::SendMessage(json_t * message) +void WsServer2::Private::SendMessage(char const * message) { lws * wsi = nullptr; @@ -259,10 +270,7 @@ void WsServer2::Private::SendMessage(json_t * message) if (nullptr != wsi_) { - char* message_text = json_dumps(message, JSON_COMPACT); - writeQueue.push(message_text); - json_decref(message); - free(message_text); + writeQueue.push(message); wsi = wsi_; } } @@ -273,6 +281,14 @@ void WsServer2::Private::SendMessage(json_t * message) } } +void WsServer2::Private::SendMessage(json_t * message) +{ + char* message_text = json_dumps(message, JSON_COMPACT); + SendMessage(message_text); + json_decref(message); + free(message_text); +} + void WsServer2::Private::OnMessageReceived(struct lws * wsi, char const * data, size_t length) { (void) wsi; diff --git a/test/webfuse/utils/ws_server2.hpp b/test/webfuse/utils/ws_server2.hpp index b09c573..b29ffb1 100644 --- a/test/webfuse/utils/ws_server2.hpp +++ b/test/webfuse/utils/ws_server2.hpp @@ -27,6 +27,8 @@ public: virtual ~WsServer2(); bool IsConnected(); std::string const & GetUrl() const; + void SendMessage(char const * message); + void SendMessage(json_t * message); private: class Private; Private * d;