Merge pull request #77 from falk-werner/adapter_client

Provides client mode for webfuse_adapter
pull/78/head
Falk Werner 4 years ago committed by GitHub
commit 56749c113b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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

@ -5,6 +5,12 @@
### Breaking Changes
* Remove CMake support (change build system to meson)
* Make argument credentials const in `wf_authenticate_fn`
### New Features
* Add adapter client implementation
* Allow system to choose port of webfuse server (by setting port in `wf_server_config` to 0)
### Fixes

@ -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;
}
}

@ -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": [<name>], "id": <id>}
webfuse daemon: {"result": {"id": <name>}, "id": <id>}
client: {"method": "add_filesystem", "params": [<name>], "id": <id>}
server: {"result": {"id": <name>}, "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": [<type>, <credentials>], "id": <id>}
webfuse daemon: {"result": {}, "id": <id>}
client: {"method": "authenticate", "params": [<type>, <credentials>], "id": <id>}
server: {"result": {}, "id": <id>}
| Item | Data type | Description |
| ----------- | ----------| ------------------------------- |

@ -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

@ -0,0 +1,159 @@
////////////////////////////////////////////////////////////////////////////////
/// \file adapter/client.h
/// \brief Adapter client.
////////////////////////////////////////////////////////////////////////////////
#ifndef WF_ADAPTER_CLIENT_H
#define WF_ADAPTER_CLIENT_H
#include <webfuse/adapter/api.h>
#include <webfuse/adapter/client_callback.h>
#ifdef __cplusplus
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,
char const * local_path,
char const * name);
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,53 @@
////////////////////////////////////////////////////////////////////////////////
/// \file adapter/client_callbak.h
/// \brief Callback of adapter clients.
////////////////////////////////////////////////////////////////////////////////
#ifndef WF_ADAPTER_CLIENT_CALLBACK_H
#define WF_ADAPTER_CLIENT_CALLBACK_H
#ifdef __cplusplus
extern "C"
{
#endif
#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 ///< Connection to a foreign provider established
#define WF_CLIENT_DISCONNECTED 0x0012 ///< Connection closed or connect failed
#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 ///< File system added successfully
#define WF_CLIENT_FILESYSTEM_ADD_FAILED 0x0032 ///< Failed to add file system
#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,
void * args);
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,69 @@
////////////////////////////////////////////////////////////////////////////////
/// \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
#include <webfuse/adapter/api.h>
#ifdef __cplusplus
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,
char const * cafile_path);
#ifdef __cplusplus
}
#endif
#endif

@ -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

@ -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

@ -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
//------------------------------------------------------------------------------

@ -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

@ -17,4 +17,9 @@
#include <webfuse/adapter/credentials.h>
#include <webfuse/adapter/mountpoint.h>
#include <webfuse/adapter/client.h>
#include <webfuse/adapter/client_callback.h>
#include <webfuse/adapter/client_tlsconfig.h>
#endif

@ -8,6 +8,9 @@
#include "webfuse/core/util.h"
#include "webfuse/adapter/impl/client.h"
#include "webfuse/adapter/impl/client_tlsconfig.h"
// server
struct wf_server * wf_server_create(
@ -34,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
@ -147,6 +155,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 *
@ -178,3 +201,98 @@ 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)
{
return wf_impl_client_create(callback, user_data);
}
void
wf_client_dispose(
struct wf_client * client)
{
wf_impl_client_dispose(client);
}
void *
wf_client_get_userdata(
struct wf_client * client)
{
return wf_impl_client_get_userdata(client);
}
void
wf_client_service(
struct wf_client * client)
{
wf_impl_client_service(client);
}
void
wf_client_interrupt(
struct wf_client * client)
{
wf_impl_client_interrupt(client);
}
void
wf_client_connect(
struct wf_client * client,
char const * url)
{
wf_impl_client_connect(client, url);
}
void
wf_client_disconnect(
struct wf_client * client)
{
wf_impl_client_disconnect(client);
}
void
wf_client_authenticate(
struct wf_client * client)
{
wf_impl_client_authenticate(client);
}
void
wf_client_add_filesystem(
struct wf_client * client,
char const * local_path,
char const * name)
{
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);
}

@ -0,0 +1,127 @@
#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 <libwebsockets.h>
#include <stdlib.h>
#include <string.h>
#define WF_CLIENT_PROTOCOL_COUNT 2
struct wf_client
{
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;
};
struct wf_client *
wf_impl_client_create(
wf_client_callback_fn * callback,
void * user_data)
{
wf_lwslog_disable();
struct wf_client * client = malloc(sizeof(struct wf_client));
wf_impl_client_tlsconfig_init(&client->tls);
client->user_data = user_data;
wf_impl_client_protocol_init(&client->protocol,
(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]);
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;
}
void
wf_impl_client_dispose(
struct wf_client * client)
{
lws_context_destroy(client->context);
wf_impl_client_protocol_cleanup(&client->protocol);
wf_impl_client_tlsconfig_cleanup(&client->tls);
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)
{
lws_service(client->context, 0);
}
void
wf_impl_client_interrupt(
struct wf_client * client)
{
lws_cancel_service(client->context);
}
void
wf_impl_client_connect(
struct wf_client * client,
char const * url)
{
wf_impl_client_protocol_connect(&client->protocol, client->context, url);
}
void
wf_impl_client_disconnect(
struct wf_client * client)
{
wf_impl_client_protocol_disconnect(&client->protocol);
}
void
wf_impl_client_authenticate(
struct wf_client * client)
{
wf_impl_client_protocol_authenticate(&client->protocol);
}
void
wf_impl_client_add_filesystem(
struct wf_client * client,
char const * local_path,
char const * name)
{
wf_impl_client_protocol_add_filesystem(&client->protocol, local_path, name);
}

@ -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

@ -0,0 +1,336 @@
#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"
#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 <stddef.h>
#include <libwebsockets.h>
#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,
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;
if (NULL != protocol->wsi)
{
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 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,
void * WF_UNUSED_PARAM(user),
void * in,
size_t WF_UNUSED_PARAM(len))
{
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)
{
wf_timer_manager_check(protocol->timer_manager);
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->wsi = NULL;
protocol->callback(protocol->user_data, WF_CLIENT_DISCONNECTED, NULL);
break;
case LWS_CALLBACK_CLIENT_CLOSED:
protocol->is_connected = false;
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);
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
// fall-through
case LWS_CALLBACK_CLIENT_WRITEABLE:
if (wsi == protocol->wsi)
{
if (protocol->is_shutdown_requested)
{
result = 1;
}
else if (!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);
}
}
}
break;
case LWS_CALLBACK_RAW_RX_FILE:
if ((NULL != protocol->filesystem) && (wsi == protocol->filesystem->wsi))
{
wf_impl_filesystem_process_request(protocol->filesystem);
}
default:
break;
}
}
return result;
}
void
wf_impl_client_protocol_init(
struct wf_client_protocol * protocol,
wf_client_protocol_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->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)
{
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
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;
}
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);
}
}
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);
}
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);
}
}

@ -0,0 +1,88 @@
#ifndef WF_ADAPTER_IMPL_CLIENT_PROTOCOL_H
#define WF_ADAPTER_IMPL_CLIENT_PROTOCOL_H
#include "webfuse/adapter/client_callback.h"
#include "webfuse/core/slist.h"
#ifndef __cplusplus
#include <stdbool.h>
#endif
#ifdef __cplusplus
extern "C"
{
#endif
struct lws_protocols;
struct lws_context;
struct wf_impl_filesystem;
struct wf_jsonrpc_proxy;
struct wf_timer_manager;
typedef void
wf_client_protocol_callback_fn(
void * user_data,
int reason,
void * arg);
struct wf_client_protocol
{
bool is_connected;
bool is_shutdown_requested;
struct lws * wsi;
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;
struct wf_slist messages;
};
extern void
wf_impl_client_protocol_init(
struct wf_client_protocol * protocol,
wf_client_protocol_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);
extern void
wf_impl_client_protocol_connect(
struct wf_client_protocol * protocol,
struct lws_context * conext,
char const * url);
extern void
wf_impl_client_protocol_disconnect(
struct wf_client_protocol * protocol);
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
#endif

@ -0,0 +1,56 @@
#include "webfuse/adapter/impl/client_tlsconfig.h"
#include <stdlib.h>
#include <string.h>
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);
}

@ -0,0 +1,52 @@
#ifndef WF_ADAPTER_IMPL_CLIENT_TLSCONFIG_H
#define WF_ADAPTER_IMPL_CLIENT_TLSCONFIG_H
#ifndef __cplusplus
#include <stdbool.h>
#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

@ -1,6 +1,13 @@
#include "webfuse/adapter/impl/credentials.h"
#include <string.h>
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));
}

@ -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

@ -9,8 +9,6 @@
#include "webfuse/adapter/impl/session.h"
#include "webfuse/adapter/impl/mountpoint.h"
#include "webfuse/core/string.h"
#include <libwebsockets.h>
#include <sys/stat.h>
@ -52,7 +50,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 +62,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 +84,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 +99,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);

@ -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);

@ -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;
}

@ -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;
};

@ -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;
}

@ -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

@ -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)
{

@ -1,32 +0,0 @@
#include "webfuse/core/string.h"
#include <stdio.h>
#include <stdlib.h>
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;
}

@ -1,21 +0,0 @@
#ifndef WF_CORE_STRING_H
#define WF_CORE_STRING_H
#ifndef __cplusplus
#include <stdarg.h>
#else
#include <cstdarg>
#endif
#ifdef __cplusplus
extern "C"
{
#endif
extern char * wf_create_string(char const * format, ...);
#ifdef __cplusplus
}
#endif
#endif

@ -1,9 +1,9 @@
#include "webfuse/provider/impl/url.h"
#include "webfuse/core/url.h"
#include <stdlib.h>
#include <string.h>
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));
}

@ -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 <stdbool.h>
@ -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

@ -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)
{

@ -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);

@ -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
{

@ -25,10 +25,10 @@ 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',
'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 +56,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',
@ -132,6 +131,9 @@ 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',
'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])
@ -207,6 +209,9 @@ 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/utils/adapter_client.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',
@ -215,14 +220,15 @@ 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',
'test/webfuse/tests/core/test_slist.cc',
'test/webfuse/tests/core/test_base64.cc',
'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',
@ -237,8 +243,8 @@ 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/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',
@ -250,6 +256,8 @@ 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',
'test/webfuse/tests/adapter/test_client_tlsconfig.cc',
link_args: [
'-Wl,--wrap=wf_timer_manager_create',
'-Wl,--wrap=wf_timer_manager_dispose',

@ -0,0 +1,50 @@
#ifndef WF_LOOKUP_MATCHER_HPP
#define WF_LOOKUP_MATCHER_HPP
#include <gmock/gmock.h>
#include <jansson.h>
#include <cstring>
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

@ -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<webfuse_test::MockAdapterClientCallback*>(user_data);
callback->Invoke(client, reason, args);
}
}
namespace webfuse_test
{
MockAdapterClientCallback::MockAdapterClientCallback()
{
}
MockAdapterClientCallback::~MockAdapterClientCallback()
{
}
void * MockAdapterClientCallback::GetUserData()
{
return reinterpret_cast<void*>(this);
}
wf_client_callback_fn * MockAdapterClientCallback::GetCallbackFn()
{
return &webfuse_test_MockAdapterClientCallback_callback;
}
}

@ -0,0 +1,22 @@
#ifndef WF_MOCK_ADAPTER_CLIENT_CALLBACK_HPP
#define WF_MOCK_ADAPTER_CLIENT_CALLBACK_HPP
#include <gmock/gmock.h>
#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

@ -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);
}

@ -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);
}

@ -0,0 +1,18 @@
#ifndef WF_MOCK_INVOKATION_HANDLER_HPP
#define WF_MOCK_INVOKATION_HANDLER_HPP
#include "webfuse/utils/ws_server2.hpp"
#include <gtest/gtest.h>
namespace webfuse_test
{
class MockInvokationHander: public IIvokationHandler
{
public:
MOCK_METHOD2(Invoke, std::string(char const * method, json_t * params));
};
}
#endif

@ -5,10 +5,8 @@
TEST(wf_impl_operation_context, get_proxy)
{
wf_jsonrpc_proxy * proxy = reinterpret_cast<wf_jsonrpc_proxy*>(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));

@ -0,0 +1,579 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#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"
#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;
using testing::Return;
using testing::Throw;
using testing::StrEq;
#define TIMEOUT (std::chrono::milliseconds(30 * 1000))
namespace
{
void GetCredentials(wf_client *, int, void * arg)
{
auto * creds = reinterpret_cast<wf_credentials*>(arg);
wf_credentials_set_type(creds, "username");
wf_credentials_add(creds, "username", "Bob");
wf_credentials_add(creds, "password", "secret");
}
}
TEST(AdapterClient, CreateAndDispose)
{
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);
wf_client * client = wf_client_create(
callback.GetCallbackFn(), callback.GetUserData());
wf_client_dispose(client);
}
TEST(AdapterClient, Connect)
{
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; }));
client.Disconnect();
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);
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<wf_client_tlsconfig*>(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, 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)
{
TimeoutWatcher watcher(TIMEOUT);
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());
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 authenticated = false;
EXPECT_CALL(callback, Invoke(_, WF_CLIENT_AUTHENTICATED, nullptr)).Times(1)
.WillOnce(Invoke([&] (wf_client *, int, void *) mutable { authenticated = true; }));
AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), server.GetUrl());
client.Connect();
ASSERT_TRUE(watcher.waitUntil([&]() mutable { return connected; }));
client.Authenticate();
ASSERT_TRUE(watcher.waitUntil([&]() mutable { return authenticated; }));
client.Disconnect();
ASSERT_TRUE(watcher.waitUntil([&]() mutable { return disconnected; }));
}
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;
}));
AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), "");
client.Authenticate();
ASSERT_TRUE(watcher.waitUntil([&]() mutable { return called; }));
}
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());
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 {
called = true;
}));
AdapterClient client(callback.GetCallbackFn(), callback.GetUserData(), server.GetUrl());
client.Connect();
ASSERT_TRUE(watcher.waitUntil([&]() mutable { return connected; }));
client.Authenticate();
ASSERT_TRUE(watcher.waitUntil([&]() mutable { return called; }));
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, 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);
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; }));
}

@ -0,0 +1,69 @@
#include <gtest/gtest.h>
#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);
}

@ -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);
}

@ -20,7 +20,7 @@ wf_mountpoint * create_mountpoint(
}
bool authenticate(
wf_credentials * credentials,
wf_credentials const * credentials,
void * user_data)
{
(void) credentials;

@ -1,18 +0,0 @@
#include <gtest/gtest.h>
#include <stdlib.h>
#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);
}

@ -1,69 +1,69 @@
#include <gtest/gtest.h>
#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);

@ -1,6 +1,7 @@
#include "webfuse/tests/integration/server.hpp"
#include <thread>
#include <mutex>
#include <sstream>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
@ -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<void*>(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();
}
}

@ -1,6 +1,8 @@
#ifndef WF_TEST_INTEGRATION_SERVER_HPP
#define WF_TEST_INTEGRATION_SERVER_HPP
#include <string>
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;

@ -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()

@ -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");

@ -3,20 +3,27 @@
#include <webfuse/provider/client_protocol.h>
#include <webfuse/provider/client_config.h>
#include "webfuse/utils/ws_server.hpp"
#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"
#include <libwebsockets.h>
#include <cstring>
#include <thread>
#include <atomic>
using webfuse_test::WebsocketServer;
using webfuse_test::WsServer;
using webfuse_test::MockProviderClient;
using webfuse_test::IProviderClient;
using webfuse_test::TimeoutWatcher;
using testing::_;
using testing::AtMost;
using testing::Invoke;
#define DEFAULT_TIMEOUT (std::chrono::milliseconds(5 * 1000))
namespace
{
@ -27,29 +34,42 @@ class ClientProtocolFixture
public:
explicit ClientProtocolFixture(IProviderClient& client, bool enableAuthentication = false)
{
server = new WsServer(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 +79,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 +132,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 +164,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;
WsServer * server;
wfp_client_config * config;
wfp_client_protocol * protocol;
struct lws_context_creation_info info;
struct lws_protocols protocols[2];
struct lws_context * context;
};
void GetCredentials(wfp_credentials * credentials)

@ -0,0 +1,32 @@
#include "webfuse/provider/impl/dirbuffer.h"
#include <gtest/gtest.h>
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);
}

@ -0,0 +1,149 @@
#include "webfuse/utils/adapter_client.hpp"
#include "webfuse/utils/tempdir.hpp"
#include <thread>
#include <mutex>
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)
, tempdir("webfuse_adpter_client")
{
thread = std::thread(&Run, this);
}
~Private()
{
ApplyCommand(Command::shutdown);
thread.join();
wf_client_dispose(client);
}
void ApplyCommand(Command actual_command)
{
{
std::unique_lock<std::mutex> lock(mutex);
command = actual_command;
}
wf_client_interrupt(client);
}
std::string GetDir()
{
return tempdir.path();
}
private:
static void Run(Private * self)
{
bool is_running = true;
while (is_running)
{
Command actual_command;
{
std::unique_lock<std::mutex> 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, self->tempdir.path(), "test");
break;
case Command::shutdown:
// fall-through
default:
is_running = false;
break;
}
}
}
wf_client * client;
std::string url_;
Command command;
TempDir tempdir;
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);
}
std::string AdapterClient::GetDir() const
{
return d->GetDir();
}
}

@ -0,0 +1,32 @@
#ifndef WF_UTILS_ADAPTER_CLIENT_HPP
#define WF_UTILS_APAPTER_CLIENT_HPP
#include "webfuse/adapter/client.h"
#include <string>
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();
std::string GetDir() const;
private:
class Private;
Private * d;
};
}
#endif

@ -0,0 +1,16 @@
#include <gtest/gtest.h>
#include <jansson.h>
namespace webfuse_test
{
class JanssonTestEnvironment: public ::testing::Environment
{
public:
void SetUp()
{
json_object_seed(0);
}
};
#
}

@ -1,7 +1,7 @@
#include "webfuse/core/string.h"
#include "webfuse/utils/tempdir.hpp"
#include <unistd.h>
#include <cstdio>
#include <cstdlib>
#include <stdexcept>
@ -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)
{

@ -1,5 +1,6 @@
#include "webfuse/utils/timeout_watcher.hpp"
#include <stdexcept>
#include <thread>
using std::chrono::milliseconds;
using std::chrono::duration_cast;
@ -41,4 +42,17 @@ void TimeoutWatcher::check()
}
}
bool TimeoutWatcher::waitUntil(std::function<bool()> predicate)
{
bool result = predicate();
while ((!result) && (!isTimeout()))
{
std::this_thread::yield();
result = predicate();
}
return result;
}
}

@ -2,6 +2,7 @@
#define WF_TEST_TIMEOUT_WATCHER_HPP
#include <chrono>
#include <functional>
namespace webfuse_test
{
@ -15,6 +16,7 @@ public:
~TimeoutWatcher();
bool isTimeout();
void check();
bool waitUntil(std::function<bool()> predicate);
private:
std::chrono::milliseconds startedAt;
std::chrono::milliseconds timeout_;

@ -1,17 +1,12 @@
#include "webfuse/utils/ws_server.hpp"
#include "webfuse/utils/timeout_watcher.hpp"
#include "webfuse/utils/ws_server.h"
#include "webfuse/core/lws_log.h"
#include "webfuse/core/util.h"
#include "webfuse/core/protocol_names.h"
#include <libwebsockets.h>
#include <cstring>
#include <vector>
#include <thread>
#include <mutex>
#include <chrono>
#include <sstream>
#include <queue>
#include <string>
using webfuse_test::TimeoutWatcher;
#define DEFAULT_TIMEOUT (std::chrono::milliseconds(5 * 1000))
namespace
{
@ -20,10 +15,47 @@ 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;
virtual void OnConnected(lws * wsi) = 0;
virtual void OnConnectionClosed(lws * wsi) = 0;
virtual void OnMessageReceived(struct lws * wsi, char const * data, size_t length) = 0;
virtual void OnWritable(struct lws * wsi) = 0;
};
}
namespace webfuse_test
{
class WsServer::Private : IServer
{
Private(Private const &) = delete;
Private & operator=(Private const &) = delete;
public:
Private(std::string const & protocol, int port);
~Private();
bool IsConnected();
std::string GetUrl() const;
void SendMessage(json_t * message);
json_t * ReceiveMessage();
void OnConnected(lws * wsi) override;
void OnConnectionClosed(lws * wsi) override;
void OnMessageReceived(struct lws * wsi, char const * data, size_t length) override;
void OnWritable(struct lws * wsi) override;
private:
static void run(Private * self);
std::string protocol_;
int port_;
bool is_connected;
bool is_shutdown_requested;
lws * wsi_;
lws_context * ws_context;
lws_protocols ws_protocols[2];
lws_context_creation_info info;
std::thread context;
std::mutex mutex;
std::queue<std::string> writeQueue;
std::queue<std::string> recvQueue;
};
}
@ -32,161 +64,169 @@ 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 * 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);
if (NULL == ws_protocol)
{
return 0;
}
auto * server = reinterpret_cast<IServer*>(nullptr != ws_protocol ? ws_protocol->user : nullptr);
auto * server = reinterpret_cast<IServer*>(ws_protocol->user);
switch(reason)
if (nullptr != server)
{
case LWS_CALLBACK_ESTABLISHED:
server->onConnectionEstablished(wsi);
break;
case LWS_CALLBACK_CLOSED:
server->onConnectionClosed(wsi);
break;
case LWS_CALLBACK_RECEIVE:
{
auto * data = reinterpret_cast<char const *>(in);
server->onMessageReceived(wsi, data, len);
}
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
server->onWritable(wsi);
break;
default:
break;
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<char const *>(in);
server->OnMessageReceived(wsi, data, len);
}
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
server->OnWritable(wsi);
break;
default:
break;
}
}
return 0;
return result;
}
}
namespace webfuse_test
{
class WebsocketServer::Private: public IServer
WsServer::WsServer(std::string const & protocol, int port)
: d(new Private(protocol, port))
{
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<void*>(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;
WsServer::~WsServer()
{
delete d;
}
context = lws_create_context(&info);
bool WsServer::IsConnected()
{
return d->IsConnected();
}
}
void WsServer::SendMessage(json_t * message)
{
d->SendMessage(message);
}
virtual ~Private()
{
lws_context_destroy(context);
delete[] ws_protocols;
}
json_t * WsServer::ReceiveMessage()
{
return d->ReceiveMessage();
}
struct lws_context * getContext()
{
return context;
}
std::string WsServer::GetUrl() const
{
return d->GetUrl();
}
void waitForConnection()
{
TimeoutWatcher watcher(DEFAULT_TIMEOUT);
while (nullptr == client_wsi)
{
watcher.check();
lws_service(context, 100);
}
}
WsServer::Private::Private(std::string const & protocol, int port)
: protocol_(protocol)
, 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 = 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<void*>(server);
memset(&info, 0, sizeof(struct lws_context_creation_info));
info.port = port;
info.mounts = NULL;
info.protocols =ws_protocols;
info.vhost_name = "localhost";
info.ws_ping_pong_interval = 10;
info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
info.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
ws_context = lws_create_context(&info);
struct lws_vhost * vhost = lws_create_vhost(ws_context, &info);
port_ = lws_get_vhost_port(vhost);
context = std::thread(&run, this);
}
void sendMessage(json_t * message)
WsServer::Private::~Private()
{
{
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);
}
}
std::unique_lock<std::mutex> lock(mutex);
is_shutdown_requested = true;
}
json_t * receiveMessage()
{
TimeoutWatcher watcher(DEFAULT_TIMEOUT);
lws_cancel_service(ws_context);
context.join();
lws_context_destroy(ws_context);
}
while (recvQueue.empty())
void WsServer::Private::run(Private * self)
{
bool is_running = true;
while (is_running)
{
lws_service(self->ws_context, 0);
{
watcher.check();
lws_service(context, 100);
std::unique_lock<std::mutex> lock(self->mutex);
is_running = !self->is_shutdown_requested;
}
}
}
std::string const & message_text = recvQueue.front();
json_t * message = json_loads(message_text.c_str(), JSON_DECODE_ANY, nullptr);
recvQueue.pop();
bool WsServer::Private::IsConnected()
{
std::unique_lock<std::mutex> lock(mutex);
return is_connected;
}
return message;
}
void WsServer::Private::OnConnected(lws * wsi)
{
std::unique_lock<std::mutex> lock(mutex);
is_connected = true;
wsi_ = wsi;
}
void onConnectionEstablished(struct lws * wsi) override
void WsServer::Private::OnConnectionClosed(lws * wsi)
{
std::unique_lock<std::mutex> lock(mutex);
if (wsi == wsi_)
{
client_wsi = wsi;
is_connected = false;
wsi_ = nullptr;
}
}
void onConnectionClosed(struct lws * wsi) override
{
if (wsi == client_wsi)
{
client_wsi = nullptr;
}
}
void WsServer::Private::OnWritable(struct lws * wsi)
{
bool notify = false;
void onMessageReceived(struct lws * wsi, char const * data, size_t length) override
{
if (wsi == client_wsi)
{
recvQueue.push(std::string(data, length));
}
}
std::unique_lock<std::mutex> lock(mutex);
void onWritable(struct lws * wsi) override
{
if (!writeQueue.empty())
{
std::string const & message = writeQueue.front();
@ -197,69 +237,69 @@ public:
delete[] data;
writeQueue.pop();
if (!writeQueue.empty())
{
lws_callback_on_writable(wsi);
}
notify = !writeQueue.empty();
}
}
private:
void send(std::string const & message)
if (notify)
{
if (nullptr != client_wsi)
{
writeQueue.push(message);
lws_callback_on_writable(client_wsi);
}
lws_callback_on_writable(wsi);
}
}
struct lws * client_wsi;
struct lws_protocols * ws_protocols;
struct lws_context_creation_info info;
struct lws_context * context;
std::queue<std::string> writeQueue;
std::queue<std::string> recvQueue;
};
WebsocketServer::WebsocketServer(int port)
: d(new Private(port, nullptr, 0))
void WsServer::Private::SendMessage(json_t * message)
{
lws * wsi = nullptr;
}
WebsocketServer::WebsocketServer(int port, struct lws_protocols * additionalProtocols, std::size_t additionalProtocolsCount)
: d(new Private(port, additionalProtocols, additionalProtocolsCount))
{
{
std::unique_lock<std::mutex> 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_;
}
}
WebsocketServer::~WebsocketServer()
{
delete d;
if (nullptr != wsi)
{
lws_callback_on_writable(wsi_);
}
}
struct lws_context * WebsocketServer::getContext()
void WsServer::Private::OnMessageReceived(struct lws * wsi, char const * data, size_t length)
{
return d->getContext();
std::unique_lock<std::mutex> lock(mutex);
if (wsi == wsi_)
{
recvQueue.push(std::string(data, length));
}
}
void WebsocketServer::waitForConnection()
json_t * WsServer::Private::ReceiveMessage()
{
d->waitForConnection();
}
std::unique_lock<std::mutex> 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();
}
void WebsocketServer::sendMessage(json_t * message)
{
d->sendMessage(message);
return result;
}
json_t * WebsocketServer::receiveMessage()
std::string WsServer::Private::GetUrl() const
{
return d->receiveMessage();
std::ostringstream stream;
stream << "ws://localhost:" << port_ << "/";
return stream.str();
}

@ -0,0 +1,29 @@
#ifndef WF_TEST_UTILS_WS_SERVER_HPP
#define WF_TEST_UTILS_WS_SERVER_HPP
#include <jansson.h>
#include <string>
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

@ -1,29 +0,0 @@
#ifndef WF_TEST_UTILS_WS_SERVER_HPP
#define WF_TEST_UTILS_WS_SERVER_HPP
#include <libwebsockets.h>
#include <jansson.h>
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

@ -0,0 +1,331 @@
#include "webfuse/utils/ws_server2.hpp"
#include "webfuse/core/lws_log.h"
#include <libwebsockets.h>
#include <thread>
#include <mutex>
#include <chrono>
#include <sstream>
#include <queue>
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<IServer*>(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<char const *>(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 : public IServer
{
Private(Private const &) = delete;
Private & operator=(Private const &) = delete;
public:
Private(IIvokationHandler & handler, std::string const & protocol, int port, bool enable_tls);
~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;
void SendMessage(char const * message);
void SendMessage(json_t * message);
private:
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<std::string> writeQueue;
};
WsServer2::WsServer2(
IIvokationHandler& handler,
std::string const & protocol,
int port,
bool enable_tls)
: d(new WsServer2::Private(handler, protocol, port, enable_tls))
{
}
WsServer2::~WsServer2()
{
delete d;
}
bool WsServer2::IsConnected()
{
return d->IsConnected();
}
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,
int port,
bool enable_tls)
: 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<void*>(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;
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 << (enable_tls ? "wss://" : "ws://")
<< "localhost:" << lws_get_vhost_port(vhost) << "/";
url = stream.str();
context = std::thread(&Run, this);
}
WsServer2::Private::~Private()
{
{
std::unique_lock<std::mutex> 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<std::mutex> lock(self->mutex);
is_running = !self->is_shutdown_requested;
}
}
}
bool WsServer2::Private::IsConnected()
{
std::unique_lock<std::mutex> lock(mutex);
return is_connected;
}
void WsServer2::Private::OnConnected(lws * wsi)
{
std::unique_lock<std::mutex> lock(mutex);
is_connected = true;
wsi_ = wsi;
}
void WsServer2::Private::OnConnectionClosed(lws * wsi)
{
std::unique_lock<std::mutex> lock(mutex);
if (wsi == wsi_)
{
is_connected = false;
wsi_ = nullptr;
}
}
void WsServer2::Private::OnWritable(struct lws * wsi)
{
bool notify = false;
{
std::unique_lock<std::mutex> 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(char const * message)
{
lws * wsi = nullptr;
{
std::unique_lock<std::mutex> lock(mutex);
if (nullptr != wsi_)
{
writeQueue.push(message);
wsi = wsi_;
}
}
if (nullptr != wsi)
{
lws_callback_on_writable(wsi_);
}
}
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;
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;
}
}

@ -0,0 +1,39 @@
#ifndef WF_TEST_UTILS_WS_SERVER2_HPP
#define WF_TEST_UTILS_WS_SERVER2_HPP
#include <jansson.h>
#include <string>
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,
bool enable_tls = false);
virtual ~WsServer2();
bool IsConnected();
std::string const & GetUrl() const;
void SendMessage(char const * message);
void SendMessage(json_t * message);
private:
class Private;
Private * d;
};
}
#endif
Loading…
Cancel
Save