diff --git a/CMakeLists.txt b/CMakeLists.txt index 9da0dde..97c4c4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,7 @@ set(WSFS_PROVIDER_SOURCES lib/wsfsp/client_protocol.c lib/wsfsp/provider.c lib/wsfsp/provider_default.c + lib/wsfsp/url.c ) add_library(wsfs-provider SHARED ${WSFS_PROVIDER_SOURCES}) @@ -169,12 +170,15 @@ if(NOT WITHOUT_TESTS) pkg_check_modules(GTEST gtest_main) add_library(wsfs-static STATIC ${WSFS_SOURCES}) - set_target_properties(wsfs-static PROPERTIES OUTPUT_NAME wsfs) - target_include_directories(wsfs-static PUBLIC lib ${EXTRA_INCLUDE_DIRS}) target_compile_options(wsfs-static PUBLIC ${EXTRA_CFLAGS}) +add_library(wsfs-provider-static STATIC ${WSFS_PROVIDER_SOURCES}) +set_target_properties(wsfs-provider-static PROPERTIES OUTPUT_NAME wsfs-provider) +target_include_directories(wsfs-provider-static PUBLIC lib ${EXTRA_INCLUDE_DIRS}) +target_compile_options(wsfs-provider-static PUBLIC ${EXTRA_CFLAGS}) + add_executable(alltests test/msleep.cc @@ -182,9 +186,10 @@ add_executable(alltests test/test_server.cc test/test_timepoint.cc test/test_timer.cc + test/test_url.cc ) -target_link_libraries(alltests PUBLIC wsfs-static ${EXTRA_LIBS} ${GTEST_LIBRARIES}) +target_link_libraries(alltests PUBLIC wsfs-static wsfs-provider-static ${EXTRA_LIBS} ${GTEST_LIBRARIES}) target_include_directories(alltests PUBLIC lib ${EXTRA_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS}) target_compile_options(alltests PUBLIC ${EXTRA_CFLAGS} ${GTEST_CFLAGS}) diff --git a/lib/wsfsp/url.c b/lib/wsfsp/url.c new file mode 100644 index 0000000..54877fd --- /dev/null +++ b/lib/wsfsp/url.c @@ -0,0 +1,131 @@ +#include "wsfsp/url.h" + +#include +#include + +struct wsfsp_url_protocol +{ + char const * name; + size_t name_length; + int default_port; +}; + +static bool wsfsp_url_readprotocol( + struct wsfsp_url * url, + char const * * data) +{ + static struct wsfsp_url_protocol const known_protocols[] = + { + {"ws://", 5, 80}, + {"wss://", 6, 443} + }; + static size_t const count = (sizeof(known_protocols) / sizeof(known_protocols[0])); + + bool found = false; + for(size_t i = 0; (!found) && (i < count); i++) + { + struct wsfsp_url_protocol const * protocol = &known_protocols[i]; + if (0 == strncmp(*data, protocol->name, protocol->name_length)) + { + url->port = protocol->default_port; + *data = *data + protocol->name_length; + found = true; + } + } + + return found; +} + +static bool wsfsp_url_readhost( + struct wsfsp_url * url, + char const * * data) +{ + char * end = strpbrk(*data, ":/"); + bool const result = (NULL != end); + + if (result) + { + size_t length = end - *data; + url->host = strndup(*data, length); + *data = end; + } + + return result; +} + +static bool wsfsp_url_readport( + struct wsfsp_url * url, + char const * * data) +{ + bool result; + + if (':' == **data) + { + *data = *data + 1; + char * end = strchr(*data, '/'); + result = (NULL != end); + + if (result) + { + url->port = atoi(*data); + *data = end; + } + } + else + { + result = ('/' == **data); + } + + return result; +} + +static bool wsfsp_url_readpath( + struct wsfsp_url * url, + char const * * data) +{ + bool const result = ('/' == **data); + url->path = strdup(*data); + *data = NULL; + + return result; +} + + +bool wsfsp_url_init( + struct wsfsp_url * url, + char const * value) +{ + memset(url, 0, sizeof(struct wsfsp_url)); + char const * data = value; + + bool const result = + wsfsp_url_readprotocol(url, &data) && + wsfsp_url_readhost(url, &data) && + wsfsp_url_readport(url, &data) && + wsfsp_url_readpath(url, &data) + ; + + if (!result) + { + wsfsp_url_cleanup(url); + } + + return result; +} + +void wsfsp_url_cleanup( + struct wsfsp_url * url) +{ + free(url->host); + free(url->path); + memset(url, 0, sizeof(struct wsfsp_url)); +} + +char const * wsfsp_url_gethost( + struct wsfsp_url const * url); + +int wsfsp_url_getport( + struct wsfsp_url const * url); + +char const * wsfsp_url_getpath( + struct wsfsp_url const * url); diff --git a/lib/wsfsp/url.h b/lib/wsfsp/url.h new file mode 100644 index 0000000..bf9c7b4 --- /dev/null +++ b/lib/wsfsp/url.h @@ -0,0 +1,32 @@ +#ifndef WSFSP_URL_H +#define WSFSP_URL_H + +#ifndef __cplusplus +#include +#endif + +struct wsfsp_url +{ + char * host; + int port; + char * path; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern bool wsfsp_url_init( + struct wsfsp_url * url, + char const * value); + +extern void wsfsp_url_cleanup( + struct wsfsp_url * url); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/test_url.cc b/test/test_url.cc new file mode 100644 index 0000000..7476da4 --- /dev/null +++ b/test/test_url.cc @@ -0,0 +1,90 @@ +#include + +#include "wsfsp/url.h" + +TEST(url, ParseWs) +{ + struct wsfsp_url url; + bool result = wsfsp_url_init(&url, "ws://localhost/"); + ASSERT_TRUE(result); + ASSERT_EQ(80, url.port); + ASSERT_STREQ("localhost", url.host); + ASSERT_STREQ("/", url.path); + + wsfsp_url_cleanup(&url); +} + +TEST(url, ParswWss) +{ + struct wsfsp_url url; + bool result = wsfsp_url_init(&url, "wss://localhost/"); + ASSERT_TRUE(result); + ASSERT_EQ(443, url.port); + ASSERT_STREQ("localhost", url.host); + ASSERT_STREQ("/", url.path); + + wsfsp_url_cleanup(&url); +} + +TEST(url, ParseIPAdress) +{ + struct wsfsp_url url; + bool result = wsfsp_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); + + wsfsp_url_cleanup(&url); +} + +TEST(url, ParsePort) +{ + struct wsfsp_url url; + bool result = wsfsp_url_init(&url, "ws://localhost:54321/"); + ASSERT_TRUE(result); + ASSERT_EQ(54321, url.port); + + wsfsp_url_cleanup(&url); +} + +TEST(url, ParseNonEmptyPath) +{ + struct wsfsp_url url; + bool result = wsfsp_url_init(&url, "ws://localhost/some_path?query"); + ASSERT_TRUE(result); + ASSERT_STREQ("/some_path?query", url.path); + + wsfsp_url_cleanup(&url); +} + +TEST(url, FailToParseUnknownProtocol) +{ + struct wsfsp_url url; + bool result = wsfsp_url_init(&url, "unknown://localhost/"); + ASSERT_FALSE(result); + ASSERT_EQ(0, url.port); + ASSERT_EQ(nullptr, url.path); + ASSERT_EQ(nullptr, url.host); +} + +TEST(url, FailToParseMissingProtocol) +{ + struct wsfsp_url url; + bool result = wsfsp_url_init(&url, "unknown"); + ASSERT_FALSE(result); + ASSERT_EQ(0, url.port); + ASSERT_EQ(nullptr, url.path); + ASSERT_EQ(nullptr, url.host); +} + +TEST(url, FailToParseMissingPath) +{ + struct wsfsp_url url; + bool result = wsfsp_url_init(&url, "wss://localhost"); + ASSERT_FALSE(result); + ASSERT_EQ(0, url.port); + ASSERT_EQ(nullptr, url.path); + ASSERT_EQ(nullptr, url.host); +} +