diff --git a/lib/webfuse/adapter/impl/client.c b/lib/webfuse/adapter/impl/client.c index ed3863d..c7a72e6 100644 --- a/lib/webfuse/adapter/impl/client.c +++ b/lib/webfuse/adapter/impl/client.c @@ -114,7 +114,7 @@ void wf_impl_client_authenticate( struct wf_client * client) { - wf_impl_client_protocol_callback(&client->protocol, WF_CLIENT_AUTHENTICATION_FAILED, NULL); + wf_impl_client_protocol_authenticate(&client->protocol); } void diff --git a/lib/webfuse/adapter/impl/client_protocol.c b/lib/webfuse/adapter/impl/client_protocol.c index 894ec0d..fb392c8 100644 --- a/lib/webfuse/adapter/impl/client_protocol.c +++ b/lib/webfuse/adapter/impl/client_protocol.c @@ -1,12 +1,73 @@ #include "webfuse/adapter/impl/client_protocol.h" #include "webfuse/adapter/client_callback.h" +#include "webfuse/adapter/impl/credentials.h" #include "webfuse/core/protocol_names.h" #include "webfuse/core/url.h" #include "webfuse/core/util.h" +#include "webfuse/core/timer/manager.h" +#include "webfuse/core/jsonrpc/response.h" +#include "webfuse/core/jsonrpc/proxy.h" + +#include "webfuse/core/message.h" +#include "webfuse/core/message_queue.h" +#include "webfuse/core/container_of.h" + #include #include +#define WF_DEFAULT_TIMEOUT (10 * 1000) + +static void +wf_impl_client_protocol_process( + struct wf_client_protocol * protocol, + char const * data, + size_t length) +{ + json_t * message = json_loadb(data, length, 0, NULL); + if (NULL != message) + { + if (wf_jsonrpc_is_response(message)) + { + wf_jsonrpc_proxy_onresult(protocol->proxy, message); + } + + json_decref(message); + } +} + +static bool +wf_impl_client_protocol_send( + json_t * request, + void * user_data) +{ + bool result = false; + struct wf_client_protocol * protocol = user_data; + + struct wf_message * message = wf_message_create(request); + if (NULL != message) + { + wf_slist_append(&protocol->messages, &message->item); + lws_callback_on_writable(protocol->wsi); + result = true; + } + + return result; +} + + +static void +wf_impl_client_protocol_on_authenticate_finished( + void * user_data, + json_t const * result, + json_t const * WF_UNUSED_PARAM(error)) +{ + struct wf_client_protocol * protocol = user_data; + int const reason = (NULL != result) ? WF_CLIENT_AUTHENTICATED : WF_CLIENT_AUTHENTICATION_FAILED; + + protocol->callback(protocol->user_data, reason, NULL); +} + static int wf_impl_client_protocol_lws_callback( struct lws * wsi, enum lws_callback_reasons reason, @@ -20,6 +81,8 @@ static int wf_impl_client_protocol_lws_callback( if (NULL != protocol) { + wf_timer_manager_check(protocol->timer_manager); + switch (reason) { case LWS_CALLBACK_CLIENT_ESTABLISHED: @@ -35,6 +98,8 @@ static int wf_impl_client_protocol_lws_callback( protocol->callback(protocol->user_data, WF_CLIENT_DISCONNECTED, NULL); protocol->wsi = NULL; break; + case LWS_CALLBACK_CLIENT_RECEIVE: + wf_impl_client_protocol_process(protocol, in, len); case LWS_CALLBACK_SERVER_WRITEABLE: // fall-through case LWS_CALLBACK_CLIENT_WRITEABLE: @@ -44,6 +109,18 @@ static int wf_impl_client_protocol_lws_callback( { result = 1; } + else if (!wf_slist_empty(&protocol->messages)) + { + struct wf_slist_item * item = wf_slist_remove_first(&protocol->messages); + struct wf_message * message = wf_container_of(item, struct wf_message, item); + lws_write(wsi, (unsigned char*) message->data, message->length, LWS_WRITE_TEXT); + wf_message_dispose(message); + + if (!wf_slist_empty(&protocol->messages)) + { + lws_callback_on_writable(wsi); + } + } } default: break; @@ -65,6 +142,11 @@ wf_impl_client_protocol_init( protocol->callback = callback; protocol->user_data = user_data; protocol->callback(protocol->user_data, WF_CLIENT_INIT, NULL); + + wf_slist_init(&protocol->messages); + protocol->timer_manager = wf_timer_manager_create(); + protocol->proxy = wf_jsonrpc_proxy_create(protocol->timer_manager, WF_DEFAULT_TIMEOUT, &wf_impl_client_protocol_send, protocol); + } void @@ -72,6 +154,9 @@ wf_impl_client_protocol_cleanup( struct wf_client_protocol * protocol) { protocol->callback(protocol->user_data, WF_CLIENT_CLEANUP, NULL); + wf_jsonrpc_proxy_dispose(protocol->proxy); + wf_timer_manager_dispose(protocol->timer_manager); + wf_message_queue_cleanup(&protocol->messages); } void @@ -141,3 +226,23 @@ wf_impl_client_protocol_disconnect( } } + +void +wf_impl_client_protocol_authenticate( + struct wf_client_protocol * protocol) +{ + struct wf_credentials creds; + wf_impl_credentials_init_default(&creds); + protocol->callback(protocol->user_data, WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS, &creds); + + json_incref(creds.data); + wf_jsonrpc_proxy_invoke( + protocol->proxy, + &wf_impl_client_protocol_on_authenticate_finished, + protocol, + "authenticate", + "sj", + creds.type, creds.data); + + wf_impl_credentials_cleanup(&creds); +} diff --git a/lib/webfuse/adapter/impl/client_protocol.h b/lib/webfuse/adapter/impl/client_protocol.h index 29606f4..02e8b5b 100644 --- a/lib/webfuse/adapter/impl/client_protocol.h +++ b/lib/webfuse/adapter/impl/client_protocol.h @@ -2,6 +2,7 @@ #define WF_ADAPTER_IMPL_CLIENT_PROTOCOL_H #include "webfuse/adapter/client_callback.h" +#include "webfuse/core/slist.h" #ifndef __cplusplus #include @@ -15,6 +16,9 @@ extern "C" struct lws_protocols; struct lws_context; +struct wf_jsonrpc_proxy; +struct wf_timer_manager; + typedef void wf_client_protocol_callback_fn( void * user_data, @@ -28,6 +32,9 @@ struct wf_client_protocol struct lws * wsi; wf_client_callback_fn * callback; void * user_data; + struct wf_timer_manager * timer_manager; + struct wf_jsonrpc_proxy * proxy; + struct wf_slist messages; }; extern void @@ -61,6 +68,10 @@ extern void wf_impl_client_protocol_disconnect( struct wf_client_protocol * protocol); +extern void +wf_impl_client_protocol_authenticate( + struct wf_client_protocol * protocol); + #ifdef __cplusplus } #endif diff --git a/test/webfuse/tests/adapter/test_client.cc b/test/webfuse/tests/adapter/test_client.cc index 16d650f..835cdf4 100644 --- a/test/webfuse/tests/adapter/test_client.cc +++ b/test/webfuse/tests/adapter/test_client.cc @@ -3,6 +3,7 @@ #include "webfuse/adapter/client.h" #include "webfuse/adapter/credentials.h" +#include "webfuse/adapter/credentials.h" #include "webfuse/core/protocol_names.h" #include "webfuse/utils/ws_server.h" #include "webfuse/mocks/mock_adapter_client_callback.hpp" @@ -12,12 +13,21 @@ using webfuse_test::WsServer; using webfuse_test::MockAdapterClientCallback; using webfuse_test::TimeoutWatcher; using testing::_; +using testing::Invoke; #define TIMEOUT (std::chrono::milliseconds(10 * 1000)) namespace { +void GetCredentials(wf_client *, int, void * arg) +{ + auto * creds = reinterpret_cast(arg); + wf_credentials_set_type(creds, "username"); + wf_credentials_add(creds, "username", "Bob"); + wf_credentials_add(creds, "password", "secret"); +} + enum class connection_state { disconnected, @@ -141,5 +151,86 @@ TEST(AdapterClient, Connect) wf_client_service(client); } + wf_client_dispose(client); +} + +TEST(AdapterClient, Authenticate) +{ + TimeoutWatcher watcher(TIMEOUT); + + WsServer server(WF_PROTOCOL_NAME_PROVIDER_SERVER); + MockAdapterClientCallback callback; + + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_INIT, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CREATED, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_GET_TLS_CONFIG, _)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CLEANUP, nullptr)).Times(1); + + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_CONNECTED, nullptr)).Times(1); + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_DISCONNECTED, nullptr)).Times(1); + + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_AUTHENTICATE_GET_CREDENTIALS, _)).Times(1) + .WillOnce(Invoke(GetCredentials)); + bool called = false; + bool * p_called = &called; + EXPECT_CALL(callback, Invoke(_, WF_CLIENT_AUTHENTICATED, nullptr)).Times(1) + .WillOnce(Invoke([p_called] (wf_client *, int, void *) { + *p_called = true; + })); + + wf_client * client = wf_client_create( + callback.GetCallbackFn(), callback.GetUserData()); + + wf_client_connect(client, server.GetUrl().c_str()); + while (!server.IsConnected()) + { + watcher.check(); + wf_client_service(client); + } + + wf_client_authenticate(client); + json_t * request = server.ReceiveMessage(); + while (nullptr == request) + { + watcher.check(); + wf_client_service(client); + request = server.ReceiveMessage(); + } + json_t * id = json_object_get(request, "id"); + ASSERT_TRUE(json_is_integer(id)); + json_t * method = json_object_get(request, "method"); + ASSERT_TRUE(json_is_string(method)); + ASSERT_STREQ("authenticate", json_string_value(method)); + json_t * params = json_object_get(request, "params"); + ASSERT_TRUE(json_is_array(params)); + ASSERT_EQ(2, json_array_size(params)); + json_t * type = json_array_get(params, 0); + ASSERT_TRUE(json_is_string(type)); + json_t * creds = json_array_get(params, 1); + ASSERT_TRUE(json_is_object(creds)); + json_t * username = json_object_get(creds, "username"); + ASSERT_TRUE(json_is_string(username)); + ASSERT_STREQ("Bob", json_string_value(username)); + json_t * password = json_object_get(creds, "password"); + ASSERT_TRUE(json_is_string(password)); + ASSERT_STREQ("secret", json_string_value(password)); + + json_t * response = json_object(); + json_object_set(response, "id", id); + json_object_set_new(response, "result", json_object()); + server.SendMessage(response); + json_decref(request); + while (!called) { + watcher.check(); + wf_client_service(client); + } + + wf_client_disconnect(client); + while (server.IsConnected()) + { + watcher.check(); + wf_client_service(client); + } + wf_client_dispose(client); } \ No newline at end of file