diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index c6f8c69..b8f9cff 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,15 +2,15 @@ name: Bug report about: Create a report to help us improve title: '' -labels: '' +labels: 'bug' assignees: '' --- -**Describe the bug** +**Description** A clear and concise description of what the bug is. -**To Reproduce** +**Steps To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' @@ -20,19 +20,5 @@ Steps to reproduce the behavior: **Expected behavior** A clear and concise description of what you expected to happen. -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information):** -- OS: \[e.g. iOS\] -- Browser \[e.g. chrome, safari\] -- Version \[e.g. 22\] - -**Smartphone (please complete the following information):** -- Device: \[e.g. iPhone6\] -- OS: \[e.g. iOS8.1\] -- Browser \[e.g. stock browser, safari\] -- Version \[e.g. 22\] - **Additional context** Add any other context about the problem here. diff --git a/.gitignore b/.gitignore index 95be7aa..08038c6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ /.build/ /.deps/ /.settings/language.settings.xml +/doc/api +/subprojects/* +!/subprojects/*.wrap \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index c4da690..ae18a7a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,17 +12,18 @@ addons: - qemu-user-static env: global: - - DISTRO=ubuntu - PARALLELMFLAGS="-j4" - PORTABLE_WORSPACE=1 matrix: - - BUILDTYPE=Debug MARCH=amd64 CHECK_TARGET=memcheck - - BUILDTYPE=Release MARCH=amd64 CHECK_TARGET=memcheck - - BUILDTYPE=Debug MARCH=arm32v7 CHECK_TARGET=check - - BUILDTYPE=MinSizeRel MARCH=arm32v7 CHECK_TARGET=check + - DISTRO=ubuntu BUILDTYPE=Debug MARCH=amd64 CHECK_TARGET=memcheck + - DISTRO=ubuntu BUILDTYPE=Coverage MARCH=amd64 CHECK_TARGET=check + - DISTRO=ubuntu BUILDTYPE=Release MARCH=amd64 CHECK_TARGET=memcheck + - DISTRO=ubuntu BUILDTYPE=Debug MARCH=arm32v7 CHECK_TARGET=check + - DISTRO=ubuntu BUILDTYPE=MinSizeRel MARCH=arm32v7 CHECK_TARGET=check + - DISTRO=alpine BUILDTYPE=Debug MARCH=amd64 CHECK_TARGET=check before_script: - make BUILDTYPE=$BUILDTYPE MARCH=$MARCH script: -- make BUILDTYPE=$BUILDTYPE MARCH=$MARCH $CHECK_TARGET +- make DISTRO=$DISTRO BUILDTYPE=$BUILDTYPE MARCH=$MARCH $CHECK_TARGET after_success: - bash <(curl -s https://codecov.io/bash) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ce597b..5e3f18c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,348 +1,63 @@ cmake_minimum_required (VERSION 3.10) -project(webfuse VERSION 0.2.0 DESCRIPTION "Websocket filesystem based on libfuse") +project(webfuse VERSION 0.3.0 DESCRIPTION "Websocket filesystem based on libfuse") -option(WITHOUT_TESTS "disable unit tests" OFF) -option(WITHOUT_EXAMPLE "disable example" OFF) +option(WITHOUT_TESTS "disable unit tests" OFF) +option(WITHOUT_ADAPTER "disable adapter library" OFF) +option(WITHOUT_PROVIDER "disable provider library" OFF) + +set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/cmake") +include(coverage) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) find_package(PkgConfig REQUIRED) -pkg_check_modules(FUSE3 REQUIRED fuse3) pkg_check_modules(LWS REQUIRED libwebsockets) pkg_check_modules(JANSSON REQUIRED jansson) -pkg_check_modules(UUID REQUIRED uuid) add_definitions(-D_FILE_OFFSET_BITS=64) set(CMAKE_C_STANDARD 99) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(C_WARNINGS -Wall -Wextra) +set(C_WARNINGS -Wall -Wextra -Werror) set(CMAKE_POSITION_INDEPENDENT_CODE ON) -if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - -add_definitions( - -pg - --coverage - -fprofile-arcs - -ftest-coverage -) - -if(CMAKE_C_COMPILER_ID STREQUAL "GNU") - link_libraries(gcov) -else() - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") -endif() - -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs") - -endif() - include_directories( "include" - ${FUSE3_INCLUDE_DIRS} ${LWS_INCLUDE_DIRS} ${JANSSON_INCLUDE_DIRS} - ${UUID_INCLUDE_DIRS} -) - -set(EXTRA_LIBS - ${EXTRA_LIBS} - ${FUSE3_LIBRARIES} - ${LWS_LIBRARIES} - ${JANSSON_LIBRARIES} - ${UUID_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} ) add_compile_options( ${C_WARNINGS} - ${FUSE3_CFLAGS_OTHER} ${LWS_CFLAGS_OTHER} ${JANSSON_CFLAGS_OTHER} - ${UUID_CFLAGS_OTHER} "-pthread" ) -# libwebfuse-core +include(webfuse_core) +include(webfuse_adapter) +include(webfuse_provider) +include(unit_tests) -add_library(webfuse-core STATIC - lib/webfuse/core/slist.c - lib/webfuse/core/message.c - lib/webfuse/core/message_queue.c - lib/webfuse/core/status.c - lib/webfuse/core/string.c - lib/webfuse/core/path.c - lib/webfuse/core/lws_log.c -) +set(CPACK_SOURCE_GENERATOR "TGZ") +set(CPACK_GENERATOR "DEB") +set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}") +set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}") +set(CPACK_DESCRIPTION "Websocket filesystem based on libfuse") +#set(CPACK_COMPONENTS_ALL libraries) +set(CPACK_DEB_COMPONENT_INSTALL ON) +set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Falk Werner") +set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) -set_target_properties(webfuse-core PROPERTIES OUTPUT_NAME webfuse-core) -target_include_directories(webfuse-core PUBLIC lib) -set_target_properties(webfuse-core PROPERTIES C_VISIBILITY_PRESET hidden) +set(CPACK_DEBIAN_LIBRARIES_FILE_NAME "webfuse_${PROJECT_VERSION}_${CMAKE_SYSTEM_NAME}.deb") +set(CPACK_DEBIAN_LIBRARIES_PACKAGE_NAME "webfuse") -install(DIRECTORY include/webfuse/core DESTINATION include/webfuse) +set(CPACK_DEBIAN_HEADERS_FILE_NAME "webfuse-dev_${PROJECT_VERSION}_${CMAKE_SYSTEM_NAME}.deb") +set(CPACK_DEBIAN_HEADERS_PACKAGE_NAME "webfuse-dev") +set(CPACK_DEBIAN_HEADERS_PACKAGE_DEPENDS "webfuse") - -# libwebfuse-adapter - -add_library(webfuse-adapter-static STATIC - lib/webfuse/adapter/api.c - lib/webfuse/adapter/impl/filesystem.c - lib/webfuse/adapter/impl/server.c - lib/webfuse/adapter/impl/server_config.c - lib/webfuse/adapter/impl/server_protocol.c - lib/webfuse/adapter/impl/session.c - lib/webfuse/adapter/impl/session_manager.c - lib/webfuse/adapter/impl/authenticator.c - lib/webfuse/adapter/impl/authenticators.c - lib/webfuse/adapter/impl/credentials.c - lib/webfuse/adapter/impl/operations.c - lib/webfuse/adapter/impl/time/timepoint.c - lib/webfuse/adapter/impl/time/timer.c - lib/webfuse/adapter/impl/time/timeout_manager.c - lib/webfuse/adapter/impl/operation/lookup.c - lib/webfuse/adapter/impl/operation/getattr.c - lib/webfuse/adapter/impl/operation/readdir.c - 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/jsonrpc/proxy.c - lib/webfuse/adapter/impl/jsonrpc/server.c - lib/webfuse/adapter/impl/jsonrpc/method.c - lib/webfuse/adapter/impl/jsonrpc/request.c - lib/webfuse/adapter/impl/jsonrpc/response.c - lib/webfuse/adapter/impl/jsonrpc/util.c -) - -set_target_properties(webfuse-adapter-static PROPERTIES OUTPUT_NAME webfuse-adapter) -set_target_properties(webfuse-adapter-static PROPERTIES C_VISIBILITY_PRESET hidden) -target_include_directories(webfuse-adapter-static PUBLIC lib) - -add_library(webfuse-adapter SHARED - lib/webfuse/adapter/api.c -) - -set_target_properties(webfuse-adapter PROPERTIES VERSION ${PROJECT_VERSION}) -set_target_properties(webfuse-adapter PROPERTIES SOVERSION 0) -set_target_properties(webfuse-adapter PROPERTIES C_VISIBILITY_PRESET hidden) -set_target_properties(webfuse-adapter PROPERTIES COMPILE_DEFINITIONS "WF_API=WF_EXPORT") - -target_link_libraries(webfuse-adapter PRIVATE webfuse-adapter-static webfuse-core) - -file(WRITE "${PROJECT_BINARY_DIR}/libwebfuse-adapter.pc" -"prefix=\"${CMAKE_INSTALL_PREFIX}\" - -exec_prefix=\${prefix} -libdir=\${exec_prefix}/lib${LIB_SUFFIX} -includedir=\${prefix}/include -Name: libwebfuse -Description: Websockets filesystem server library -Version: ${PROJECT_VERSION} - -Libs: -L\${libdir} -lwebfuse-adapter -l${FUSE3_LIBRARIES} -l${LWS_LIBRARIES} -l${JANSSON_LIBRARIES} -Cflags: -I\${includedir}" -) - -install(TARGETS webfuse-adapter DESTINATION lib${LIB_SUFFIX}) -install(FILES include/webfuse_adapter.h DESTINATION include) -install(DIRECTORY include/webfuse/adapter DESTINATION include/webfuse) -install(FILES "${PROJECT_BINARY_DIR}/libwebfuse-adapter.pc" DESTINATION lib${LIB_SUFFIX}/pkgconfig) - -#libwebfuse-provider - -add_library(webfuse-provider-static STATIC - 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 - lib/webfuse/provider/impl/provider.c - lib/webfuse/provider/impl/request.c - lib/webfuse/provider/impl/dirbuffer.c - lib/webfuse/provider/impl/operation/lookup.c - lib/webfuse/provider/impl/operation/getattr.c - lib/webfuse/provider/impl/operation/readdir.c - lib/webfuse/provider/impl/operation/open.c - lib/webfuse/provider/impl/operation/close.c - lib/webfuse/provider/impl/operation/read.c - lib/webfuse/provider/impl/static_filesystem.c -) - -set_target_properties(webfuse-provider-static PROPERTIES OUTPUT_NAME webfuse-provider) -set_target_properties(webfuse-provider-static PROPERTIES C_VISIBILITY_PRESET hidden) -target_include_directories(webfuse-provider-static PUBLIC lib) - -add_library(webfuse-provider SHARED - lib/webfuse/provider/api.c -) - -set_target_properties(webfuse-provider PROPERTIES VERSION ${PROJECT_VERSION}) -set_target_properties(webfuse-provider PROPERTIES SOVERSION 0) -set_target_properties(webfuse-provider PROPERTIES C_VISIBILITY_PRESET hidden) -set_target_properties(webfuse-provider PROPERTIES COMPILE_DEFINITIONS "WFP_API=WFP_EXPORT") - -target_include_directories(webfuse-provider PUBLIC lib) -target_link_libraries(webfuse-provider PRIVATE webfuse-provider-static webfuse-core) - -file(WRITE "${PROJECT_BINARY_DIR}/libwebfuse-provider.pc" -"prefix=\"${CMAKE_INSTALL_PREFIX}\" -exec_prefix=\${prefix} -libdir=\${exec_prefix}/lib${LIB_SUFFIX} -includedir=\${prefix}/include -Name: libwebfuse-provider -Description: Provider library for websockets filesystem -Version: ${PROJECT_VERSION} - -Libs: -L\${libdir} -lwebfuse-provider -l${LWS_LIBRARIES} -l${JANSSON_LIBRARIES} -Cflags: -I\${includedir}" -) - -install(TARGETS webfuse-provider DESTINATION lib${LIB_SUFFIX}) -install(FILES include/webfuse_provider.h DESTINATION include) -install(DIRECTORY include/webfuse/provider DESTINATION include/webfuse) -install(FILES "${PROJECT_BINARY_DIR}/libwebfuse-provider.pc" DESTINATION lib${LIB_SUFFIX}/pkgconfig) - - -# examples - -pkg_check_modules(OPENSSL REQUIRED openssl) - -if(NOT WITHOUT_EXAMPLE) - -# libuserdb - -add_library(userdb STATIC - example/lib/userdb/src/userdb.c -) - -target_include_directories(userdb PUBLIC - example/lib/userdb/include - ${OPENSSL_INCLUDE_DIRS} - ${JANSSON_INCLUDE_DIRS} -) - -target_compile_options(userdb PUBLIC ${OPENSSL_CFLAGS_OTHER}) - -# daemon - -add_executable(webfused - example/daemon/main.c -) - -target_link_libraries(webfused PUBLIC webfuse-adapter userdb ${OPENSSL_LIBRARIES} ${EXTRA_LIBS}) -target_compile_options(webfused PUBLIC ${OPENSSL_CFLAGS_OTHER}) - -# provider - -add_executable(webfuse-provider-app - example/provider/main.c -) - -set_target_properties(webfuse-provider-app PROPERTIES OUTPUT_NAME webfuse-provider) - -target_link_libraries(webfuse-provider-app PUBLIC webfuse-provider ${EXTRA_LIBS}) -target_include_directories(webfuse-provider-app PUBLIC ${EXTRA_INCLUDE_DIRS}) - -# static-filesystem-provider - -add_executable(static-filesystem-provider - example/provider/static_filesystem.c -) - -target_link_libraries(static-filesystem-provider PUBLIC webfuse-provider ${EXTRA_LIBS}) -target_include_directories(static-filesystem-provider PUBLIC ${EXTRA_INCLUDE_DIRS}) -target_compile_options(static-filesystem-provider PUBLIC ${EXTRA_CFLAGS}) - -# webfuse-passwd - -add_executable(webfuse-passwd - example/passwd/main.c -) - -target_link_libraries(webfuse-passwd PUBLIC - userdb - ${OPENSSL_LIBRARIES} - ${JANSSON_LIBRARIES} -) - -target_include_directories(webfuse-passwd PUBLIC - example/passwd - example/lib/userdb/include - ${OPENSSL_INCLUDE_DIRS} - ${JANSSON_INCLUDE_DIRS} -) - -target_compile_options(webfuse-passwd PUBLIC ${OPENSSL_CFLAGS_OTHER}) - - -endif(NOT WITHOUT_EXAMPLE) - -# tests - -if(NOT WITHOUT_TESTS) - -include (CTest) - -pkg_check_modules(GTEST gtest_main) -include(GoogleTest) -pkg_check_modules(GMOCK gmock) - -add_executable(alltests - test/msleep.cc - test/die_if.cc - test/mock_authenticator.cc - test/mock_request.cc - test/core/test_container_of.cc - test/core/test_string.cc - test/core/test_slist.cc - test/core/test_path.cc - test/core/test_status.cc - test/core/test_message.cc - test/core/test_message_queue.cc - test/adapter/test_response_parser.cc - test/adapter/test_server.cc - test/adapter/test_timepoint.cc - test/adapter/test_timer.cc - test/adapter/test_credentials.cc - test/adapter/test_authenticator.cc - test/adapter/test_authenticators.cc - test/adapter/test_fuse_req.cc - test/adapter/jsonrpc/test_util.cc - test/adapter/jsonrpc/test_is_request.cc - test/adapter/jsonrpc/test_request.cc - test/adapter/jsonrpc/test_is_response.cc - test/adapter/jsonrpc/test_response.cc - test/adapter/jsonrpc/test_server.cc - test/adapter/jsonrpc/test_proxy.cc - test/provider/test_url.cc - test/provider/test_static_filesystem.cc - test/integration/test_integration.cc - test/integration/server.cc - test/integration/provider.cc -) - -target_link_libraries(alltests PUBLIC webfuse-adapter-static webfuse-provider-static webfuse-core ${EXTRA_LIBS} ${GMOCK_LIBRARIES} ${GTEST_LIBRARIES}) -target_include_directories(alltests PUBLIC test lib ${GMOCK_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS}) -target_compile_options(alltests PUBLIC ${GMOCK_CFLAGS} ${GTEST_CFLAGS}) - -enable_testing() -gtest_discover_tests(alltests TEST_PREFIX alltests:) - -add_custom_target(coverage - ./alltests - COMMAND mkdir -p coverage - COMMAND lcov --capture --directory . --output-file coverage/lcov.info - COMMAND lcov --remove coverage/lcov.info '/usr/*' --output-file coverage/lcov.info - COMMAND lcov --remove coverage/lcov.info '*/test/*' --output-file coverage/lcov.info -) -add_dependencies(coverage alltests) - -add_custom_target(coverage-report - COMMAND genhtml coverage/lcov.info --output-directory coverage/report -) -add_dependencies(coverage-report coverage) - -endif(NOT WITHOUT_TESTS) +include(CPack) \ No newline at end of file diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..4f4ccdf --- /dev/null +++ b/Doxyfile @@ -0,0 +1,333 @@ +# Doxyfile 1.8.13 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "webfuse" +PROJECT_NUMBER = 0.3.0 +PROJECT_BRIEF = "Websocket filesystem based on libfuse" +PROJECT_LOGO = +OUTPUT_DIRECTORY = "doc/api" +CREATE_SUBDIRS = NO +ALLOW_UNICODE_NAMES = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 4 +ALIASES = +TCL_SUBST = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +TOC_INCLUDE_HEADINGS = 0 +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +GROUP_NESTED_COMPOUNDS = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +LOOKUP_CACHE_SIZE = 0 +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +HIDE_COMPOUND_REFERENCE= NO +SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_AS_ERROR = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = README.md \ + doc/build.md doc/protocol.md doc/api.md \ + include +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.h +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = README.md +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = NO +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +CLANG_ASSISTED_PARSING = NO +CLANG_OPTIONS = +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = wf_ wfp_ +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = NO +HTML_DYNAMIC_SECTIONS = NO +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +USE_MATHJAX = NO +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +LATEX_EXTRA_STYLESHEET = +LATEX_EXTRA_FILES = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain +LATEX_TIMESTAMP = NO +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +RTF_SOURCE_CODE = NO +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_SUBDIR = +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- +GENERATE_DOCBOOK = NO +DOCBOOK_OUTPUT = docbook +DOCBOOK_PROGRAMLISTING = NO +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +DIA_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +MSCFILE_DIRS = +DIAFILE_DIRS = +PLANTUML_JAR_PATH = +PLANTUML_CFG_FILE = +PLANTUML_INCLUDE_PATH = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/Makefile b/Makefile index b0982c5..a606d7c 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ CONTAINER_USER ?= user CONTAINER_GROUP ?= user UBUNTU_CODENAME ?= bionic -DEBIAN_CODENAME ?= testing-slim +ALPINE_CODENAME ?= 3.9 SKIP_MD5SUM ?= $(call filter_out_command,md5sum) SKIP_MD5SUM := $(SKIP_MD5SUM) @@ -50,23 +50,23 @@ FETCH_TARGETS += $(FETCHDIR)/dumb-init-$(DUMB_INIT_VERSION).tar.gz $(FETCHDIR)/dumb-init-$(DUMB_INIT_VERSION).tar.gz: URL := https://github.com/Yelp/dumb-init/archive/v${DUMB_INIT_VERSION}.tar.gz $(SKIP_MD5SUM)$(FETCHDIR)/dumb-init-$(DUMB_INIT_VERSION).tar.gz: MD5 := 6166084b05772cdcf615a762c6f3b32e -GTEST_VERSION ?= 1.8.1 +GTEST_VERSION ?= 1.10.0 DOCKER_BUILDARGS += GTEST_VERSION=$(GTEST_VERSION) FETCH_TARGETS += $(FETCHDIR)/googletest-release-$(GTEST_VERSION).tar.gz $(FETCHDIR)/googletest-release-$(GTEST_VERSION).tar.gz: URL := https://github.com/google/googletest/archive/release-$(GTEST_VERSION).tar.gz -$(SKIP_MD5SUM)$(FETCHDIR)/googletest-release-$(GTEST_VERSION).tar.gz: MD5 := 2e6fbeb6a91310a16efe181886c59596 +$(SKIP_MD5SUM)$(FETCHDIR)/googletest-release-$(GTEST_VERSION).tar.gz: MD5 := ecd1fa65e7de707cd5c00bdac56022cd -FUSE_VERSION ?= 3.1.1 +FUSE_VERSION ?= 3.9.1 DOCKER_BUILDARGS += FUSE_VERSION=$(FUSE_VERSION) FETCH_TARGETS += $(FETCHDIR)/libfuse-fuse-$(FUSE_VERSION).tar.gz $(FETCHDIR)/libfuse-fuse-$(FUSE_VERSION).tar.gz: URL := https://github.com/libfuse/libfuse/archive/fuse-$(FUSE_VERSION).tar.gz -$(SKIP_MD5SUM)$(FETCHDIR)/libfuse-fuse-$(FUSE_VERSION).tar.gz: MD5 := 097f194856938afdd98bea1a5c046edd +$(SKIP_MD5SUM)$(FETCHDIR)/libfuse-fuse-$(FUSE_VERSION).tar.gz: MD5 := 5f7c1062def710d8b60343524a18cc82 -WEBSOCKETS_VERSION ?= 3.1.0 +WEBSOCKETS_VERSION ?= 4.0.10 DOCKER_BUILDARGS += WEBSOCKETS_VERSION=$(WEBSOCKETS_VERSION) FETCH_TARGETS += $(FETCHDIR)/libwebsockets-$(WEBSOCKETS_VERSION).tar.gz $(FETCHDIR)/libwebsockets-$(WEBSOCKETS_VERSION).tar.gz: URL := https://github.com/warmcat/libwebsockets/archive/v$(WEBSOCKETS_VERSION).tar.gz -$(SKIP_MD5SUM)$(FETCHDIR)/libwebsockets-$(WEBSOCKETS_VERSION).tar.gz: MD5 := 325359a25d5f6d22725ff5d086db1c76 +$(SKIP_MD5SUM)$(FETCHDIR)/libwebsockets-$(WEBSOCKETS_VERSION).tar.gz: MD5 := a1ce5a279fd06b2ce132c02c292df7aa JANSSON_VERSION ?= 2.12 DOCKER_BUILDARGS += JANSSON_VERSION=$(JANSSON_VERSION) @@ -74,27 +74,27 @@ FETCH_TARGETS += $(FETCHDIR)/jansson-$(JANSSON_VERSION).tar.gz $(FETCHDIR)/jansson-$(JANSSON_VERSION).tar.gz: URL := https://github.com/akheron/jansson/archive/v$(JANSSON_VERSION).tar.gz $(SKIP_MD5SUM)$(FETCHDIR)/jansson-$(JANSSON_VERSION).tar.gz: MD5 := c4b106528d5ffb521178565de1ba950d -QEMU_VERSION ?= v3.1.0-2 +QEMU_VERSION ?= v4.1.0-1 DOCKER_BUILDARGS += QEMU_VERSION_=$(QEMU_VERSION) FETCH_TARGETS += $(FETCHDIR)/qemu-arm-static-$(QEMU_VERSION) $(FETCHDIR)/qemu-arm-static-$(QEMU_VERSION): URL := https://github.com/multiarch/qemu-user-static/releases/download/$(QEMU_VERSION)/qemu-arm-static -$(SKIP_MD5SUM)$(FETCHDIR)/qemu-arm-static-$(QEMU_VERSION): MD5 := 8ebd24e63fdfa07c557d45373bd831b1 +$(SKIP_MD5SUM)$(FETCHDIR)/qemu-arm-static-$(QEMU_VERSION): MD5 := e508e6e4dd7f3a851207aac245a4653f ####################################################################################################################### # Architecture-specific rule target configuration CMAKE_TARGETS += amd64-ubuntu-builder -CMAKE_TARGETS += amd64-debian-builder +CMAKE_TARGETS += amd64-alpine-builder CMAKE_TARGETS += arm32v7-ubuntu-builder -CMAKE_TARGETS += arm32v7-debian-builder +CMAKE_TARGETS += arm32v7-alpine-builder MEMCHECK_FILTER = $(call regex_march_distro,'$(HOST_MARCH)','.*') UBUNTU_FILTER = $(call regex_march_distro,'.*','ubuntu') UBUNTU_TARGETS = $(addprefix $(OUTDIR)/docker/,$(call filter_targets,$(UBUNTU_FILTER),$(TARGETS))) -DEBIAN_FILTER = $(call regex_march_distro,'.*','debian') -DEBIAN_TARGETS = $(addprefix $(OUTDIR)/docker/,$(call filter_targets,$(DEBIAN_FILTER),$(TARGETS))) +ALPINE_FILTER = $(call regex_march_distro,'.*','alpine') +ALPINE_TARGETS = $(addprefix $(OUTDIR)/docker/,$(call filter_targets,$(ALPINE_FILTER),$(TARGETS))) ####################################################################################################################### # Common rule target configuration @@ -138,7 +138,7 @@ $(CHECK_TARGETS): GOALS := test $(UBUNTU_TARGETS): CODENAME := $(UBUNTU_CODENAME) -$(DEBIAN_TARGETS): CODENAME := $(DEBIAN_CODENAME) +$(ALPINE_TARGETS): CODENAME := $(ALPINE_CODENAME) $(FETCH_TARGETS): | $(FETCHDIR) $(SILENT)$(call curl,$@,$(URL),$(MD5)) diff --git a/README.md b/README.md index f656bde..1c25a55 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,10 @@ webfuse combines libwebsockets and libfuse. It allows ot attach a remote filesys ## Contents - [Motivation](#Motivation) +- [Fellow Repositories](#Fellow-Repositories) - [Concept](#Concept) - [Similar Projects](#Similar-Projects) -- [API](#API) -- [Build and run](#Build-and-run) -- [Dependencies](#Dependencies) +- [Further Documentation](#Further-Documentation) ## Motivation @@ -32,6 +31,12 @@ To avoid Steps 1 and 2, it would be great to keep the update file entirely in we webfuse solves this problem by using the [WebSocket](https://en.wikipedia.org/wiki/WebSocket) protocol. The emdedded device runs a service, known as webfuse adapter, awaiting incoming connections, e.g. from a web browser. The browser acts as a file system provider, providing the update file to the device. +## Fellow Repositories + +- **[webfuse-example](https://github.com/falk-werner/webfuse-example)**: Example of webfuse +- **[webfused](https://github.com/falk-werner/webfused)**: Reference implementation of webfuse daemon +- **[webfuse-provider](https://github.com/falk-werner/webfuse-provider)**: Reference implementation of webfuse provider + ## Concept ![concept](doc/concept.png) @@ -46,27 +51,14 @@ A reference implementation of such a daemon is provided within the examples. The - Whenever the user makes filesystem requests, such as *ls*, the request is redirected via webfuse daemon to the connected filesystem provider -Currently all requests are initiated by webfuse daemon and responded by filesystem provider. This may change in future, e.g. when authentication is supported. +### Adapters and Providers -### Filesystem represenation +In webfuse, an adapter is a component that adapts the libfuse API to a websocket interface. +Currently, libwebfuse implements only a server based adapter - a websocket server, that allows clients to connect a remote file system which +is represented via libfuse on the server side. -![filesystem](doc/filesystem.png) - -To handle multiple filesystems, that are registered by one or more providers, webfuse daemon maintains a directory structure as shown above. - -- **mount_point** is the entry point of the drectory structure - -- **fwupdate** is a name defined by the provider when filesystem was registered - *Note: the picture above shows two providers, where both registered a filesystem named "fwupdate"* - -- **<uuid>** is the filesystem id choosen by webfuse daemon to distinguish different filesystems - -- **default** is a symbolic link maintained by webfuse daemon to identify the default filesystem - -This directoy structure allows to handle multiple filesystems registered by multiple providers. -It can be used as a kind of service registry, where each filesystem represents a service. -The named subdirectores distinguish differend service types. The symbolic link *default* can be used to identify the -default service and the listing of a named subdirectory can be used to list available services of a particular type. +In webfuse, a provider is a component that provides a filesystem via websocket interface. +Currently, libwebfuse implements only a client based provider - a websocket client that provides a local filesystem to a remote server. ## Similar Projects @@ -76,378 +68,8 @@ default service and the listing of a named subdirectory can be used to list avai Unlike webfuse, davfs2 mounts a remote filesystem locally, that is provided by a WebDAV server. In contrast, webfuse starts a server awaiting client connections to attach the remote file system. -## API +## Further Documentation -### Requests, responses and notifications - -There are three types of messages, used for communication between webfuse daemon and filesystem provider. All message types are encoded in [JSON](https://www.json.org/) and strongly inspired by [JSON-RPC](https://www.jsonrpc.org/). - -#### Request - -A request is used by a sender to invoke a method on the receiver. The sender awaits a response from the receiver. Since requests and responses can be sendet or answered in any order, an id is provided in each request to identify it. - - { - "method": , - "params": , - "id" : - } - -| Item | Data type | Description | -| ----------- |:---------:| --------------------------------- | -| method_name | string | name of the method to invoke | -| params | array | method specific parameters | -| id | integer | id, which is repeated in response | - -#### Response - -A response is used to answer a prior request. There are two kinds of responses: - -##### Successful Results - - { - "result": , - "id": - } - -| Item | Data type | Description | -| ----------- |:---------:| ----------------------- | -| result | any | request specific result | -| id | integer | id, same as request | - -##### Error notifications - - { - "error": { - "code": - }, - "id": - } - -| Item | Data type | Description | -| ----------- |:---------:| ------------------- | -| code | integer | error code | -| id | integer | id, same as request | - -##### Error codes - -| Symbolic name | Code | Description | -| ------------------ | ---------:| ---------------------- | -| GOOD | 0 | no error | -| BAD | 1 | generic error | -| BAD_NOTIMPLEMENTED | 2 | method not implemented | -| BAD_TIMEOUT | 3 | timeout occured | -| BAD_BUSY | 4 | resource busy | -| BAD_FORMAT | 5 | invalid formt | -| BAD_NOENTRY | 101 | invalid entry | -| BAD_ACCESS_DENIED | 102 | access not allowed | - -#### Notification - -Notfications are used to inform a receiver about something. Unlike requests, notifications are not answered. Therefore, an id is not supplied. - - { - "method": , - "params": - } - -| Item | Data type | Description | -| ----------- |:---------:| --------------------------------- | -| method_name | string | name of the method to invoke | -| params | array | method specific parameters | - -### Requests (Adapter -> Provider) - -#### lookup - -Retrieve information about a filesystem entry by name. - - webfuse daemon: {"method": "lookup", "params": [, , ], "id": } - fs provider: {"result": { - "inode": , - "mode" : , - "type" : , - "size" : , - "atime": , - "mtime": , - "ctime": - }, "id": } - -| Item | Data type | Description | -| ----------- | --------------- | ------------------------------------------- | -| filesystem | string | name of the filesystem | -| parent | integer | inode of parent directory (1 = root) | -| name | string | name of the filesystem object to look up | -| inode | integer | inode of the filesystem object | -| mode | integer | unix file mode | -| type | "file" or "dir" | type of filesystem object | -| size | integer | required for files; file size in bytes | -| atime | integer | optional; unix time of last access | -| mtime | integer | optional; unix time of last modification | -| ctime | intefer | optional; unix time of last metadata change | - -#### getattr - -Get file attributes. - - webfuse daemon: {"method": "getattr", "params": [, ], "id": } - fs provider: {"result": { - "mode" : , - "type" : , - "size" : , - "atime": , - "mtime": , - "ctime": - }, "id": } - -| Item | Data type | Description | -| ----------- | --------------- | ------------------------------------------- | -| filesystem | string | name of the filesystem | -| inode | integer | inode of the filesystem object | -| mode | integer | unix file mode | -| type | "file" or "dir" | type of filesystem object | -| size | integer | required for files; file size in bytes | -| atime | integer | optional; unix time of last access | -| mtime | integer | optional; unix time of last modification | -| ctime | intefer | optional; unix time of last metadata change | - -#### readdir - -Read directory contents. -Result is an array of name-inode pairs for each entry. The generic entries -"." and ".." should also be provided. - - webfuse daemon: {"method": "readdir", "params": [, ], "id": } - fs provider: {"result": [ - {"name": , "inode": }, - ... - ], "id": } - -| Item | Data type | Description | -| ----------- | --------------- | ------------------------------ | -| filesystem | string | name of the filesystem | -| dir_inode | integer | inode of the directory to read | -| name | integer | name of the entry | -| inode | integer | inode of the entry | - -#### open - -Open a file. - - webfuse daemon: {"method": "readdir", "params": [, , ], "id": } - fs provider: {"result": {"handle": }, "id": } - -| Item | Data type | Description | -| ----------- | ----------| ----------------------------- | -| filesystem | string | name of the filesystem | -| inode | integer | inode of the file | -| flags | integer | access mode flags (see below) | -| handle | integer | handle of the file | - -##### Flags - -| Symbolic name | Code | Description | -| --------------| ---------:| --------------------------- | -| O_ACCMODE | 0x003 | access mode mask | -| O_RDONLY | 0x000 | open for reading only | -| O_WRONLY | 0x001 | open for writing only | -| O_RDWR | 0x002 | open for reading an writing | -| O_CREAT | 0x040 | create (a new) file | -| O_EXCL | 0x080 | open file exclusivly | -| O_TRUNC | 0x200 | open file to truncate | -| O_APPEND | 0x400 | open file to append | - -#### close - -Informs filesystem provider, that a file is closed. -Since `close` is a notification, it cannot fail. - - webfuse daemon: {"method": "close", "params": [, , , ], "id": } - -| Item | Data type | Description | -| ----------- | ----------| ---------------------------- | -| filesystem | string | name of the filesystem | -| inode | integer | inode of the file | -| handle | integer | handle of the file | -| flags | integer | access mode flags (see open) | - -#### read - -Read from an open file. - - webfuse daemon: {"method": "close", "params": [, , , , ], "id": } - fs provider: {"result": { - "data": , - "format": , - "count": - }, "id": } - -| Item | Data type | Description | -| ----------- | ----------| ----------------------------- | -| filesystem | string | name of the filesystem | -| inode | integer | inode of the file | -| handle | integer | handle of the file | -| offset | integer | Offet to start read operation | -| length | integer | Max. number of bytes to read | -| data | integer | handle of the file | -| format | string | Encoding of data (see below) | -| count | integer | Actual number of bytes read | - -##### Format - -| Format | Description | -| ---------- | -------------------------------------------------------- | -| "identiy" | Use data as is; note that JSON strings are UTF-8 encoded | -| "base64" | data is base64 encoded | - -### Requests (Provider -> Adapter) - -#### add_filesystem - -Adds a filesystem. - - fs provider: {"method": "add_filesytem", "params": [], "id": } - webfuse daemon: {"result": {"id": }, "id": } - -| Item | Data type | Description | -| ----------- | ----------| ------------------------------- | -| name | string | name and id of filesystem | - -#### authtenticate - -Authenticate the provider. -If authentication is enabled, a provider must be authenticated by the adapter before filesystems can be added. - - fs provider: {"method": "authenticate", "params": [, ], "id": } - webfuse daemon: {"result": {}, "id": } - -| Item | Data type | Description | -| ----------- | ----------| ------------------------------- | -| type | string | authentication type (see below) | -| credentials | object | credentials to authenticate | - -##### authentication types - -- **username**: authenticate via username and password - `{"username": , "password": }` - -## Authentication - -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. - -When authentication is enabled, filesystem calls are only redirected to a connected provider, after `authenticate` -has succeeded. - -![authenticate](doc/authenticate.png) - -### Enable authentication - -Authentication is enabled, if one or more authenticators are registered via `wf_server_config`. - - static bool authenticate(struct wf_credentials * creds, void * user_data) - { - char const * username = wf_credentials_get(creds, "username"); - char const * password = wf_credentials_get(creds, "password"); - - return ((NULL != username) && (0 == strcmp(username, "bob")) && - (NULL != password) && (0 == strcmp(password, "???"))); - } - - wf_server_config * config = wf_server_config_create(); - wf_server_config_add_authenticator(config, "username", &authenticate, NULL); - - wf_server * server = wf_server_create(config); - //... - -### Authenticator types and credentidals - -Each authenticator is identified by a user defined string, called `type`. The type is provided by the `authenticate` request, so you can define different authenticators for different authentication types, e.g. username, certificate, token. - -Actually, only one type is used: **username** -**It is strongly recommended to prefix custom authenticator types with an underscore (`_`) to avoid name clashes.** - -The `wf_credentials`struct represents a map to access credentials as key-value pairs, where both, key and value, are of type string. - -#### username - -The authenticator type **username** is used to authenticate via username and password. Valid credentials should contain two keys. - -- **username** refers to the name of the user -- **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. - -## Build and run - -To install dependencies, see below. - - cd webfuse - mkdir .build - cd .build - cmake .. - mkdir test - ./webfused -m test --document_root=../exmaple/daemon/www --port=4711 - -### Build options - -By default, unit tests and example application are enabled. You can disable them using the following cmake options: - -- **WITHOUT_TESTS**: disable tests - `cmake -DWITHOUT_TESTS=ON ..` - -- **WITHOUT_EXAMPLE**: disable example - `cmake -DWITHOUD_EXAMPLE=ON ..` - -## Dependencies - -- [libfuse3](https://github.com/libfuse/libfuse/) -- [libwebsockets](https://libwebsockets.org/) -- [Jansson](https://jansson.readthedocs.io) -- [GoogleTest](https://github.com/google/googletest) *(optional)* - -### Installation from source - -#### libfuse - - wget -O fuse-3.1.1.tar.gz https://github.com/libfuse/libfuse/archive/fuse-3.1.1.tar.gz - tar -xf fuse-3.1.1.tar.gz - cd libfuse-fuse-3.1.1 - ./makeconf.sh - ./configure - make - sudo make install - -#### libwebsockets - - wget -O libwebsockets-3.1.0.tar.gz https://github.com/warmcat/libwebsockets/archive/v3.1.0.tar.gz - tar -xf libwebsockets-3.1.0.tar.gz - cd libwebsockets-3.1.0 - mkdir .build - cd .build - cmake .. - make - sudo make install - -#### Jansson - - wget -O libjansson-2.12.tar.gz https://github.com/akheron/jansson/archive/v2.12.tar.gz - tar -xf libjansson-2.12.tar.gz - cd jansson-2.12 - mkdir .build - cd .build - cmake .. - make - sudo make install - -#### GoogleTest - -Installation of GoogleTest is optional webfuse library, but required to compile tests. - - wget -O gtest-1.8.1.tar.gz https://github.com/google/googletest/archive/release-1.8.1.tar.gz - tar -xf gtest-1.8.1.tar.gz - cd googletest-release-1.8.1 - mkdir .build - cd .build - cmake .. - make - sudo make install +- [Build instructions](doc/build.md) +- [Webfuse Protocol](doc/protocol.md) +- [API](doc/api.md) diff --git a/VERSION b/VERSION index 0ea3a94..0d91a54 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.0 +0.3.0 diff --git a/build/amd64-debian-builder.dockerfile b/build/amd64-alpine-builder.dockerfile similarity index 60% rename from build/amd64-debian-builder.dockerfile rename to build/amd64-alpine-builder.dockerfile index 5231e2d..4503181 100644 --- a/build/amd64-debian-builder.dockerfile +++ b/build/amd64-alpine-builder.dockerfile @@ -1,20 +1,23 @@ ARG REGISTRY_PREFIX='' -ARG CODENAME=testing-slim +ARG CODENAME=3.9 -FROM ${REGISTRY_PREFIX}debian:${CODENAME} as builder +FROM ${REGISTRY_PREFIX}alpine:${CODENAME} as builder RUN set -x \ - && apt update \ - && apt upgrade -y \ - && apt install --yes --no-install-recommends \ - build-essential \ + && apk add --no-cache \ + bash \ + coreutils \ + gcc \ + g++ \ + make \ cmake \ - ninja-build \ - pkg-config \ + ninja \ + pkgconf \ rsync \ gdb \ - gdbserver \ - valgrind + valgrind +# util-linux \ +# util-linux-dev COPY src /usr/local/src @@ -23,8 +26,8 @@ ARG PARALLELMFLAGS=-j2 ARG DUMB_INIT_VERSION=1.2.2 RUN set -x \ - && builddeps="xxd" \ - && apt install --yes --no-install-recommends $builddeps \ + && builddeps="vim" \ + && apk add --no-cache --virtual .build-deps $builddeps \ && builddir="/tmp/out" \ && mkdir -p "$builddir" \ && cd "$builddir" \ @@ -35,9 +38,9 @@ RUN set -x \ && mv dumb-init /usr/local/bin/dumb-init \ && dumb-init --version \ && rm -rf "$builddir" \ - && apt purge -y $builddeps + && apk del .build-deps -ARG GTEST_VERSION=1.8.1 +ARG GTEST_VERSION=1.10.0 RUN set -x \ && builddir="/tmp/out" \ @@ -47,34 +50,39 @@ RUN set -x \ && make "$PARALLELMFLAGS" install \ && rm -rf "$builddir" -ARG FUSE_VERSION=3.1.1 +ARG FUSE_VERSION=3.9.1 RUN set -x \ - && builddeps="libtool automake gettext" \ - && apt install --yes --no-install-recommends $builddeps \ - && cd "/usr/local/src/libfuse-fuse-$FUSE_VERSION" \ - && ./makeconf.sh \ + && builddeps="linux-headers eudev-dev python3 py3-pip py3-setuptools py3-cryptography" \ + && apk add --no-cache --virtual .build-deps $builddeps \ + && pip3 install meson \ && builddir="/tmp/out" \ && mkdir -p "$builddir" \ && cd "$builddir" \ - && "/usr/local/src/libfuse-fuse-$FUSE_VERSION/configure" \ - && make "$PARALLELMFLAGS" install \ + && meson "/usr/local/src/libfuse-fuse-$FUSE_VERSION" \ + && meson configure -Dexamples=false \ + && ninja \ + && ninja install \ + && pip3 uninstall -y meson \ && rm -rf "$builddir" \ - && apt purge -y $builddeps + && apk del .build-deps -ARG WEBSOCKETS_VERSION=3.1.0 +ARG WEBSOCKETS_VERSION=4.0.10 RUN set -x \ - && apt install --yes --no-install-recommends \ + && builddeps="linux-headers" \ + && apk add --no-cache --virtual .build-deps $builddeps \ + && apk add --no-cache \ ca-certificates \ openssl \ - libssl-dev \ + openssl-dev \ && builddir="/tmp/out" \ && mkdir -p "$builddir" \ && cd "$builddir" \ && cmake "/usr/local/src/libwebsockets-$WEBSOCKETS_VERSION" \ && make "$PARALLELMFLAGS" install \ - && rm -rf "$builddir" + && rm -rf "$builddir" \ + && apk del .build-deps ARG JANSSON_VERSION=2.12 @@ -87,6 +95,7 @@ RUN set -x \ && rm -rf "$builddir" ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib" +ENV PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig ARG USERID=1000 @@ -95,14 +104,10 @@ ARG OUTDIR=/workspace/out ARG SCRIPTDIR=/workspace/bin RUN set -x \ - && useradd -u "$USERID" -ms /bin/bash user \ + && adduser -u "$USERID" -s /bin/bash -D user \ && mkdir -p "$PROJECTDIR" "$OUTDIR" "$SCRIPTDIR" \ && chown user:user "$PROJECTDIR" "$OUTDIR" "$SCRIPTDIR" WORKDIR "$OUTDIR" ENTRYPOINT ["dumb-init", "--"] - -# unused -ARG QEMU_VERSION_=v3.1.0-2 - diff --git a/build/amd64-ubuntu-builder.dockerfile b/build/amd64-ubuntu-builder.dockerfile index ddfe61a..f242163 100644 --- a/build/amd64-ubuntu-builder.dockerfile +++ b/build/amd64-ubuntu-builder.dockerfile @@ -15,8 +15,7 @@ RUN set -x \ gdb \ gdbserver \ valgrind \ - lcov \ - uuid-dev + lcov COPY src /usr/local/src @@ -39,7 +38,7 @@ RUN set -x \ && rm -rf "$builddir" \ && apt purge -y $builddeps -ARG GTEST_VERSION=1.8.1 +ARG GTEST_VERSION=1.10.0 RUN set -x \ && builddir="/tmp/out" \ @@ -49,22 +48,24 @@ RUN set -x \ && make "$PARALLELMFLAGS" install \ && rm -rf "$builddir" -ARG FUSE_VERSION=3.1.1 +ARG FUSE_VERSION=3.9.1 RUN set -x \ - && builddeps="libtool automake gettext" \ + && builddeps="udev gettext python3 python3-pip python3-setuptools python3-wheel" \ && apt install --yes --no-install-recommends $builddeps \ - && cd "/usr/local/src/libfuse-fuse-$FUSE_VERSION" \ - && ./makeconf.sh \ + && pip3 install --system meson \ && builddir="/tmp/out" \ && mkdir -p "$builddir" \ && cd "$builddir" \ - && "/usr/local/src/libfuse-fuse-$FUSE_VERSION/configure" \ - && make "$PARALLELMFLAGS" install \ + && meson "/usr/local/src/libfuse-fuse-$FUSE_VERSION" \ + && meson configure -Dexamples=false \ + && ninja \ + && ninja install \ + && pip3 uninstall -y meson \ && rm -rf "$builddir" \ && apt purge -y $builddeps -ARG WEBSOCKETS_VERSION=3.1.0 +ARG WEBSOCKETS_VERSION=4.0.10 RUN set -x \ && apt install --yes --no-install-recommends \ @@ -104,7 +105,3 @@ RUN set -x \ WORKDIR "$OUTDIR" ENTRYPOINT ["dumb-init", "--"] - -# unused -ARG QEMU_VERSION_=v3.1.0-2 - diff --git a/build/arm32v7-debian-builder.dockerfile b/build/arm32v7-alpine-builder.dockerfile similarity index 60% rename from build/arm32v7-debian-builder.dockerfile rename to build/arm32v7-alpine-builder.dockerfile index b34fdfa..3544867 100644 --- a/build/arm32v7-debian-builder.dockerfile +++ b/build/arm32v7-alpine-builder.dockerfile @@ -1,23 +1,27 @@ ARG REGISTRY_PREFIX='' -ARG CODENAME=testing-slim +ARG CODENAME=3.9 -FROM ${REGISTRY_PREFIX}arm32v7/debian:${CODENAME} as builder +FROM ${REGISTRY_PREFIX}arm32v7/alpine:${CODENAME} as builder -ARG QEMU_VERSION_=v3.1.0-2 +ARG QEMU_VERSION_=v4.1.0-1 COPY docker/qemu-arm-static-$QEMU_VERSION_ /usr/bin/qemu-arm-static RUN set -x \ - && apt update \ - && apt upgrade -y \ - && apt install --yes --no-install-recommends \ - build-essential \ + && apk add --no-cache \ + bash \ + coreutils \ + gcc \ + g++ \ + make \ cmake \ - ninja-build \ - pkg-config \ + ninja \ + pkgconf \ rsync \ gdb \ - gdbserver + valgrind +# util-linux \ +# util-linux-dev COPY src /usr/local/src @@ -26,8 +30,8 @@ ARG PARALLELMFLAGS=-j2 ARG DUMB_INIT_VERSION=1.2.2 RUN set -x \ - && builddeps="xxd" \ - && apt install --yes --no-install-recommends $builddeps \ + && builddeps="vim" \ + && apk add --no-cache --virtual .build-deps $builddeps \ && builddir="/tmp/out" \ && mkdir -p "$builddir" \ && cd "$builddir" \ @@ -38,9 +42,9 @@ RUN set -x \ && mv dumb-init /usr/local/bin/dumb-init \ && dumb-init --version \ && rm -rf "$builddir" \ - && apt purge -y $builddeps + && apk del .build-deps -ARG GTEST_VERSION=1.8.1 +ARG GTEST_VERSION=1.10.0 RUN set -x \ && builddir="/tmp/out" \ @@ -50,34 +54,39 @@ RUN set -x \ && make "$PARALLELMFLAGS" install \ && rm -rf "$builddir" -ARG FUSE_VERSION=3.1.1 +ARG FUSE_VERSION=3.9.1 RUN set -x \ - && builddeps="libtool automake gettext" \ - && apt install --yes --no-install-recommends $builddeps \ - && cd "/usr/local/src/libfuse-fuse-$FUSE_VERSION" \ - && ./makeconf.sh \ + && builddeps="linux-headers eudev-dev python3 py3-pip py3-setuptools py3-cryptography" \ + && apk add --no-cache --virtual .build-deps $builddeps \ + && pip3 install meson \ && builddir="/tmp/out" \ && mkdir -p "$builddir" \ && cd "$builddir" \ - && "/usr/local/src/libfuse-fuse-$FUSE_VERSION/configure" \ - && make "$PARALLELMFLAGS" install \ + && meson "/usr/local/src/libfuse-fuse-$FUSE_VERSION" \ + && meson configure -Dexamples=false \ + && ninja \ + && ninja install \ + && pip3 uninstall -y meson \ && rm -rf "$builddir" \ - && apt purge -y $builddeps + && apk del .build-deps -ARG WEBSOCKETS_VERSION=3.1.0 +ARG WEBSOCKETS_VERSION=4.0.10 RUN set -x \ - && apt install --yes --no-install-recommends \ + && builddeps="linux-headers" \ + && apk add --no-cache --virtual .build-deps $builddeps \ + && apk add --no-cache \ ca-certificates \ openssl \ - libssl-dev \ + openssl-dev \ && builddir="/tmp/out" \ && mkdir -p "$builddir" \ && cd "$builddir" \ && cmake "/usr/local/src/libwebsockets-$WEBSOCKETS_VERSION" \ && make "$PARALLELMFLAGS" install \ - && rm -rf "$builddir" + && rm -rf "$builddir" \ + && apk del .build-deps ARG JANSSON_VERSION=2.12 @@ -90,6 +99,7 @@ RUN set -x \ && rm -rf "$builddir" ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib" +ENV PKG_CONFIG_PATH=/usr/local/lib32/pkgconfig ARG USERID=1000 @@ -98,7 +108,7 @@ ARG OUTDIR=/workspace/out ARG SCRIPTDIR=/workspace/bin RUN set -x \ - && useradd -u "$USERID" -ms /bin/bash user \ + && adduser -u "$USERID" -s /bin/bash -D user \ && mkdir -p "$PROJECTDIR" "$OUTDIR" "$SCRIPTDIR" \ && chown user:user "$PROJECTDIR" "$OUTDIR" "$SCRIPTDIR" diff --git a/build/arm32v7-ubuntu-builder.dockerfile b/build/arm32v7-ubuntu-builder.dockerfile index 77ce038..7204795 100644 --- a/build/arm32v7-ubuntu-builder.dockerfile +++ b/build/arm32v7-ubuntu-builder.dockerfile @@ -3,7 +3,7 @@ ARG CODENAME=bionic FROM ${REGISTRY_PREFIX}arm32v7/ubuntu:${CODENAME} as builder -ARG QEMU_VERSION_=v3.1.0-2 +ARG QEMU_VERSION_=v4.1.0-1 COPY docker/qemu-arm-static-$QEMU_VERSION_ /usr/bin/qemu-arm-static @@ -18,8 +18,7 @@ RUN set -x \ rsync \ gdb \ gdbserver \ - lcov \ - uuid-dev + lcov COPY src /usr/local/src @@ -42,7 +41,7 @@ RUN set -x \ && rm -rf "$builddir" \ && apt purge -y $builddeps -ARG GTEST_VERSION=1.8.1 +ARG GTEST_VERSION=1.10.0 RUN set -x \ && builddir="/tmp/out" \ @@ -52,22 +51,24 @@ RUN set -x \ && make "$PARALLELMFLAGS" install \ && rm -rf "$builddir" -ARG FUSE_VERSION=3.1.1 +ARG FUSE_VERSION=3.9.1 RUN set -x \ - && builddeps="libtool automake gettext" \ + && builddeps="udev gettext python3 python3-pip python3-setuptools python3-wheel" \ && apt install --yes --no-install-recommends $builddeps \ - && cd "/usr/local/src/libfuse-fuse-$FUSE_VERSION" \ - && ./makeconf.sh \ + && pip3 install --system meson \ && builddir="/tmp/out" \ && mkdir -p "$builddir" \ && cd "$builddir" \ - && "/usr/local/src/libfuse-fuse-$FUSE_VERSION/configure" \ - && make "$PARALLELMFLAGS" install \ + && meson "/usr/local/src/libfuse-fuse-$FUSE_VERSION" \ + && meson configure -Dexamples=false \ + && ninja \ + && ninja install \ + && pip3 uninstall -y meson \ && rm -rf "$builddir" \ && apt purge -y $builddeps -ARG WEBSOCKETS_VERSION=3.1.0 +ARG WEBSOCKETS_VERSION=4.0.10 RUN set -x \ && apt install --yes --no-install-recommends \ diff --git a/cmake/coverage.cmake b/cmake/coverage.cmake new file mode 100644 index 0000000..4660d6a --- /dev/null +++ b/cmake/coverage.cmake @@ -0,0 +1,30 @@ +set(CMAKE_C_FLAGS_COVERAGE + "${CMAKE_C_FLAGS_DEBUG} -pg --coverage -fprofile-arcs -ftest-coverage" + CACHE STRING "Flags used by the C compiler during coverage builds" + FORCE +) + +set(CMAKE_CXX_FLAGS_COVERAGE + "${CMAKE_CXX_FLAGS_DEBUG} -pg --coverage -fprofile-arcs -ftest-coverage" + CACHE STRING "Flags used by the C++ compiler during coverage builds." + FORCE +) + +set(CMAKE_EXE_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used for linking binaries during coverage builds." + FORCE +) + +set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used by the shared libraries linker during coverage builds." + FORCE +) + +mark_as_advanced( + CMAKE_C_FLAGS_COVERAGE + CMAKE_CXX_FLAGS_COVERAGE + CMAKE_EXE_LINKER_FLAGS_COVERAGE + CMAKE_SHARED_LINKER_FLAGS_COVERAGE +) diff --git a/cmake/unit_tests.cmake b/cmake/unit_tests.cmake new file mode 100644 index 0000000..60023ee --- /dev/null +++ b/cmake/unit_tests.cmake @@ -0,0 +1,136 @@ +if(NOT WITHOUT_TESTS AND NOT WITHOUT_ADAPTER AND NOT WITHOUT_PROVIDER) + +set(MEMORYCHECK_COMMAND_OPTIONS "--leak-check=full --error-exitcode=1") +include (CTest) + +add_executable(fs_check + test/webfuse/tests/integration/fs_check.c +) + +pkg_check_modules(GTEST gtest_main) +include(GoogleTest) +pkg_check_modules(GMOCK gmock) + +add_executable(alltests + test/webfuse/tests/core/jsonrpc/mock_timer_callback.cc + test/webfuse/tests/core/jsonrpc/mock_timer.cc + test/webfuse/tests/core/jsonrpc/test_is_request.cc + test/webfuse/tests/core/jsonrpc/test_request.cc + test/webfuse/tests/core/jsonrpc/test_is_response.cc + test/webfuse/tests/core/jsonrpc/test_response.cc + test/webfuse/tests/core/jsonrpc/test_server.cc + test/webfuse/tests/core/jsonrpc/test_proxy.cc + test/webfuse/tests/core/jsonrpc/test_response_parser.cc + test/webfuse/tests/core/timer/test_timepoint.cc + test/webfuse/tests/core/timer/test_timer.cc + test/webfuse/utils/tempdir.cc + test/webfuse/utils/file_utils.cc + test/webfuse/utils/timeout_watcher.cc + test/webfuse/utils/path.c + test/webfuse/utils/static_filesystem.c + test/webfuse/utils/ws_server.cc + test/webfuse/mocks/fake_invokation_context.cc + test/webfuse/mocks/mock_authenticator.cc + test/webfuse/mocks/mock_request.cc + test/webfuse/mocks/mock_provider_client.cc + test/webfuse/mocks/mock_provider.cc + test/webfuse/mocks/mock_fuse.cc + test/webfuse/mocks/mock_operation_context.cc + test/webfuse/mocks/mock_jsonrpc_proxy.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/adapter/test_server.cc + test/webfuse/tests/adapter/test_server_config.cc + test/webfuse/tests/adapter/test_credentials.cc + test/webfuse/tests/adapter/test_authenticator.cc + test/webfuse/tests/adapter/test_authenticators.cc + test/webfuse/tests/adapter/test_mountpoint.cc + test/webfuse/tests/adapter/test_fuse_req.cc + test/webfuse/tests/adapter/operation/test_context.cc + test/webfuse/tests/adapter/operation/test_open.cc + test/webfuse/tests/adapter/operation/test_close.cc + test/webfuse/tests/adapter/operation/test_read.cc + test/webfuse/tests/adapter/operation/test_readdir.cc + test/webfuse/tests/adapter/operation/test_getattr.cc + test/webfuse/tests/adapter/operation/test_lookup.cc + test/webfuse/tests/provider/test_url.cc + test/webfuse/tests/provider/test_client_protocol.cc + test/webfuse/tests/provider/operation/test_close.cc + test/webfuse/tests/provider/operation/test_getattr.cc + test/webfuse/tests/provider/operation/test_lookup.cc + test/webfuse/tests/provider/operation/test_open.cc + test/webfuse/tests/provider/operation/test_read.cc + test/webfuse/tests/provider/operation/test_readdir.cc + test/webfuse/tests/integration/test_lowlevel.cc + test/webfuse/tests/integration/test_integration.cc + test/webfuse/tests/integration/file.cc + test/webfuse/tests/integration/server.cc + test/webfuse/tests/integration/provider.cc +) + +target_link_libraries(alltests PUBLIC + -Wl,--wrap=wf_timer_manager_create + -Wl,--wrap=wf_timer_manager_dispose + -Wl,--wrap=wf_timer_manager_check + -Wl,--wrap=wf_timer_create + -Wl,--wrap=wf_timer_dispose + -Wl,--wrap=wf_timer_start + -Wl,--wrap=wf_timer_cancel + -Wl,--wrap=wf_impl_operation_context_get_proxy + -Wl,--wrap=wf_jsonrpc_proxy_vinvoke + -Wl,--wrap=wf_jsonrpc_proxy_vnotify + -Wl,--wrap=fuse_req_userdata + -Wl,--wrap=fuse_reply_open + -Wl,--wrap=fuse_reply_err + -Wl,--wrap=fuse_reply_buf + -Wl,--wrap=fuse_reply_attr + -Wl,--wrap=fuse_reply_entry + -Wl,--wrap=fuse_req_ctx + + webfuse-adapter-static + webfuse-provider-static + webfuse-core + ${FUSE3_LIBRARIES} + ${LWS_LIBRARIES} + ${JANSSON_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ${GMOCK_LIBRARIES} + ${GTEST_LIBRARIES} +) + +target_include_directories(alltests PUBLIC test lib ${FUSE3_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS}) +target_compile_options(alltests PUBLIC ${FUSE3_CFLAGS_OTHER} ${GMOCK_CFLAGS} ${GTEST_CFLAGS}) + +add_custom_command(OUTPUT server-key.pem + COMMAND openssl req -x509 -newkey rsa:4096 -keyout server-key.pem -out server-cert.pem -days 365 -nodes -batch -subj '/CN=localhost' + COMMAND openssl req -x509 -newkey rsa:4096 -keyout client-key.pem -out client-cert.pem -days 365 -nodes -batch -subj '/CN=localhost' +) + +add_custom_target(gen-tls DEPENDS server-key.pem) +add_dependencies(alltests gen-tls) + +enable_testing() +gtest_discover_tests(alltests TEST_PREFIX alltests:) + +add_custom_target(coverage + mkdir -p coverage + COMMAND lcov --initial --capture --directory . --output-file coverage/lcov_base.info --rc lcov_branch_coverage=1 + COMMAND ./alltests + COMMAND lcov --capture --directory . --output-file coverage/lcov.info --rc lcov_branch_coverage=1 + COMMAND lcov --remove coverage/lcov.info '/usr/*' --output-file coverage/lcov.info --rc lcov_branch_coverage=1 + COMMAND lcov --remove coverage/lcov.info '*/test/*' --output-file coverage/lcov.info --rc lcov_branch_coverage=1 +) +add_dependencies(coverage alltests) + +add_custom_target(coverage-report + COMMAND genhtml --branch-coverage --highlight --legend coverage/lcov.info --output-directory coverage/report +) +add_dependencies(coverage-report coverage) + +endif(NOT WITHOUT_TESTS AND NOT WITHOUT_ADAPTER AND NOT WITHOUT_PROVIDER) diff --git a/cmake/webfuse_adapter.cmake b/cmake/webfuse_adapter.cmake new file mode 100644 index 0000000..b15b655 --- /dev/null +++ b/cmake/webfuse_adapter.cmake @@ -0,0 +1,79 @@ +if(NOT WITHOUT_ADAPTER) + +pkg_check_modules(FUSE3 REQUIRED fuse3) + +add_library(webfuse-adapter-static STATIC + lib/webfuse/adapter/api.c + lib/webfuse/adapter/impl/filesystem.c + lib/webfuse/adapter/impl/server.c + lib/webfuse/adapter/impl/server_config.c + lib/webfuse/adapter/impl/server_protocol.c + lib/webfuse/adapter/impl/session.c + lib/webfuse/adapter/impl/session_manager.c + lib/webfuse/adapter/impl/authenticator.c + lib/webfuse/adapter/impl/authenticators.c + lib/webfuse/adapter/impl/credentials.c + lib/webfuse/adapter/impl/mountpoint.c + lib/webfuse/adapter/impl/mountpoint_factory.c + lib/webfuse/adapter/impl/operation/context.c + lib/webfuse/adapter/impl/operation/lookup.c + lib/webfuse/adapter/impl/operation/getattr.c + lib/webfuse/adapter/impl/operation/readdir.c + lib/webfuse/adapter/impl/operation/open.c + lib/webfuse/adapter/impl/operation/close.c + lib/webfuse/adapter/impl/operation/read.c +) + +target_include_directories(webfuse-adapter-static PRIVATE + lib + ${FUSE3_INCLUDE_DIRS} +) + +target_compile_options(webfuse-adapter-static PUBLIC + ${FUSE3_CFLAGS_OTHER} +) + +set_target_properties(webfuse-adapter-static PROPERTIES OUTPUT_NAME webfuse-adapter) +set_target_properties(webfuse-adapter-static PROPERTIES C_VISIBILITY_PRESET hidden) + +add_library(webfuse-adapter SHARED + lib/webfuse/adapter/api.c +) + +target_include_directories(webfuse-adapter PRIVATE + ${FUSE3_INCLUDE_DIRS} +) + +target_compile_options(webfuse-adapter PUBLIC + ${FUSE3_CFLAGS_OTHER} +) + + +set_target_properties(webfuse-adapter PROPERTIES VERSION ${PROJECT_VERSION}) +set_target_properties(webfuse-adapter PROPERTIES SOVERSION 0) +set_target_properties(webfuse-adapter PROPERTIES C_VISIBILITY_PRESET hidden) +set_target_properties(webfuse-adapter PROPERTIES COMPILE_DEFINITIONS "WF_API=WF_EXPORT") + +target_link_libraries(webfuse-adapter PRIVATE webfuse-adapter-static webfuse-core) + +file(WRITE "${PROJECT_BINARY_DIR}/libwebfuse-adapter.pc" +"prefix=\"${CMAKE_INSTALL_PREFIX}\" + +exec_prefix=\${prefix} +libdir=\${exec_prefix}/lib${LIB_SUFFIX} +includedir=\${prefix}/include +Name: libwebfuse +Description: Websockets filesystem server library +Version: ${PROJECT_VERSION} + +Libs: -L\${libdir} -lwebfuse-adapter -l${FUSE3_LIBRARIES} -l${LWS_LIBRARIES} -l${JANSSON_LIBRARIES} +Cflags: -I\${includedir}" +) + +install(TARGETS webfuse-adapter DESTINATION lib${LIB_SUFFIX} COMPONENT libraries) +install(FILES "${PROJECT_BINARY_DIR}/libwebfuse-adapter.pc" DESTINATION lib${LIB_SUFFIX}/pkgconfig COMPONENT libraries) + +install(FILES include/webfuse_adapter.h DESTINATION include COMPONENT headers) +install(DIRECTORY include/webfuse/adapter DESTINATION include/webfuse COMPONENT headers) + +endif(NOT WITHOUT_ADAPTER) diff --git a/cmake/webfuse_core.cmake b/cmake/webfuse_core.cmake new file mode 100644 index 0000000..c0b1b88 --- /dev/null +++ b/cmake/webfuse_core.cmake @@ -0,0 +1,29 @@ +# libwebfuse-core + +add_library(webfuse-core STATIC + lib/webfuse/core/slist.c + 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/timer/manager.c + lib/webfuse/core/timer/timepoint.c + lib/webfuse/core/timer/timer.c + lib/webfuse/core/jsonrpc/proxy.c + lib/webfuse/core/jsonrpc/proxy_variadic.c + lib/webfuse/core/jsonrpc/server.c + lib/webfuse/core/jsonrpc/method.c + lib/webfuse/core/jsonrpc/request.c + lib/webfuse/core/jsonrpc/response.c + lib/webfuse/core/jsonrpc/error.c + +) + +set_target_properties(webfuse-core PROPERTIES OUTPUT_NAME webfuse-core) +target_include_directories(webfuse-core PUBLIC lib lib/wf/jsonrpc/include) +set_target_properties(webfuse-core PROPERTIES C_VISIBILITY_PRESET hidden) + +install(DIRECTORY include/webfuse/core DESTINATION include/webfuse COMPONENT headers) diff --git a/cmake/webfuse_provider.cmake b/cmake/webfuse_provider.cmake new file mode 100644 index 0000000..fbfa104 --- /dev/null +++ b/cmake/webfuse_provider.cmake @@ -0,0 +1,58 @@ +if(NOT WITHOUT_PROVIDER) + +add_library(webfuse-provider-static STATIC + 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 + lib/webfuse/provider/impl/provider.c + lib/webfuse/provider/impl/request.c + lib/webfuse/provider/impl/dirbuffer.c + lib/webfuse/provider/impl/credentials.c + lib/webfuse/provider/impl/operation/lookup.c + lib/webfuse/provider/impl/operation/getattr.c + lib/webfuse/provider/impl/operation/readdir.c + lib/webfuse/provider/impl/operation/open.c + lib/webfuse/provider/impl/operation/close.c + lib/webfuse/provider/impl/operation/read.c +) + +set_target_properties(webfuse-provider-static PROPERTIES OUTPUT_NAME webfuse-provider) +set_target_properties(webfuse-provider-static PROPERTIES C_VISIBILITY_PRESET hidden) +target_include_directories(webfuse-provider-static PRIVATE + lib +) + +add_library(webfuse-provider SHARED + lib/webfuse/provider/api.c +) + +set_target_properties(webfuse-provider PROPERTIES VERSION ${PROJECT_VERSION}) +set_target_properties(webfuse-provider PROPERTIES SOVERSION 0) +set_target_properties(webfuse-provider PROPERTIES C_VISIBILITY_PRESET hidden) +set_target_properties(webfuse-provider PROPERTIES COMPILE_DEFINITIONS "WFP_API=WFP_EXPORT") + +target_include_directories(webfuse-provider PUBLIC lib) +target_link_libraries(webfuse-provider PRIVATE webfuse-provider-static webfuse-core) + +file(WRITE "${PROJECT_BINARY_DIR}/libwebfuse-provider.pc" +"prefix=\"${CMAKE_INSTALL_PREFIX}\" +exec_prefix=\${prefix} +libdir=\${exec_prefix}/lib${LIB_SUFFIX} +includedir=\${prefix}/include +Name: libwebfuse-provider +Description: Provider library for websockets filesystem +Version: ${PROJECT_VERSION} + +Libs: -L\${libdir} -lwebfuse-provider -l${LWS_LIBRARIES} -l${JANSSON_LIBRARIES} +Cflags: -I\${includedir}" +) + +install(TARGETS webfuse-provider DESTINATION lib${LIB_SUFFIX} COMPONENT libraries) +install(FILES "${PROJECT_BINARY_DIR}/libwebfuse-provider.pc" DESTINATION lib${LIB_SUFFIX}/pkgconfig COMPONENT libraries) + +install(FILES include/webfuse_provider.h DESTINATION include COMPONENT headers) +install(DIRECTORY include/webfuse/provider DESTINATION include/webfuse COMPONENT headers) + +endif(NOT WITHOUT_PROVIDER) diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..e0d4bce --- /dev/null +++ b/codecov.yml @@ -0,0 +1,12 @@ +ignore: + - test + - lib/wf/timer/test + - lib/wf/jsonrpc/test + +parsers: + gcov: + branch_detection: + conditional: yes + loop: yes + method: yes + macro: yes diff --git a/doc/api.md b/doc/api.md new file mode 100644 index 0000000..6cd8340 --- /dev/null +++ b/doc/api.md @@ -0,0 +1,55 @@ +# Webfuse API introduction + +This introduction provides a general overview to webfuse API. +Please refer to the [build instructions](build.md) to generate API reference documentation. + +## Contents + +- [Authentication](#Authentication) + +## Authentication + +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. + +When authentication is enabled, filesystem calls are only redirected to a connected provider, after `authenticate` +has succeeded. + +![authenticate](authenticate.png) + +### Enable authentication + +Authentication is enabled, if one or more authenticators are registered via `wf_server_config`. + + static bool authenticate(struct wf_credentials * creds, void * user_data) + { + char const * username = wf_credentials_get(creds, "username"); + char const * password = wf_credentials_get(creds, "password"); + + return ((NULL != username) && (0 == strcmp(username, "bob")) && + (NULL != password) && (0 == strcmp(password, "???"))); + } + + wf_server_config * config = wf_server_config_create(); + wf_server_config_add_authenticator(config, "username", &authenticate, NULL); + + wf_server * server = wf_server_create(config); + //... + +### Authenticator types and credentidals + +Each authenticator is identified by a user defined string, called `type`. The type is provided by the `authenticate` request, so you can define different authenticators for different authentication types, e.g. username, certificate, token. + +Actually, only one type is used: **username** +**It is strongly recommended to prefix custom authenticator types with an underscore (`_`) to avoid name clashes.** + +The `wf_credentials`struct represents a map to access credentials as key-value pairs, where both, key and value, are of type string. + +#### username + +The authenticator type **username** is used to authenticate via username and password. Valid credentials should contain two keys. + +- **username** refers to the name of the user +- **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. diff --git a/doc/build.md b/doc/build.md new file mode 100644 index 0000000..3aa6bec --- /dev/null +++ b/doc/build.md @@ -0,0 +1,102 @@ +# Build Instructions + +To install dependencies, see below. + + cd webfuse + mkdir .build + cd .build + cmake .. + make + +## Build options + +By default, unit tests are enabled. You can disable them using the following cmake options: + +- **WITHOUT_TESTS**: disable tests + `cmake -DWITHOUT_TESTS=ON ..` + +Since webfuse consists of two libraries, it is possible to disable one of them +in order to reduce build dependencies. +*Note that unit tests are only available, when both libraries are built.* + +- **WITHOUT_ADAPTER**: omit adapter library + `cmake -DWITHOUT_ADAPTER=ON` + +- **WIHTOU_PROVIDER**: omit provider library + `cmake -DWITHOUT_PROVIDER=ON` + +## Build using Meson (experimental) + +_Note: Meson build support is experimental. Do not rely on it._ + + meson .build + cd .build + ninja build + +_Note: Build options are not supported yet._ + +## Create API documentation + +To create API documentation, you must install doxygen and dot first. +After that, run doxygen in the project root directory. + + doxygen + +After that, you will find the API documentation in the doc/api subdirectory. + +## Dependencies + +- [libfuse3](https://github.com/libfuse/libfuse/) +- [libwebsockets](https://libwebsockets.org/) +- [Jansson](https://jansson.readthedocs.io) +- [GoogleTest](https://github.com/google/googletest) *(optional)* + +### Installation from source + +#### libfuse + +To install libfuse, meson is needed. Please refer to [meson quick guide](https://mesonbuild.com/Quick-guide.html) for setup instructions. + + wget -O fuse-3.9.1.tar.gz https://github.com/libfuse/libfuse/archive/fuse-3.9.1.tar.gz + tar -xf fuse-3.9.1.tar.gz + cd libfuse-fuse-3.9.1 + mkdir .build + cd .build + meson .. + ninja + sudo ninja install + +#### libwebsockets + + wget -O libwebsockets-4.0.10.tar.gz https://github.com/warmcat/libwebsockets/archive/v4.0.10.tar.gz + tar -xf libwebsockets-4.0.10.tar.gz + cd libwebsockets-4.0.10 + mkdir .build + cd .build + cmake .. + make + sudo make install + +#### Jansson + + wget -O libjansson-2.12.tar.gz https://github.com/akheron/jansson/archive/v2.12.tar.gz + tar -xf libjansson-2.12.tar.gz + cd jansson-2.12 + mkdir .build + cd .build + cmake .. + make + sudo make install + +#### GoogleTest + +Installation of GoogleTest is optional webfuse library, but required to compile tests. + + wget -O gtest-1.10.0.tar.gz https://github.com/google/googletest/archive/release-1.10.0.tar.gz + tar -xf gtest-1.10.0.tar.gz + cd googletest-release-1.10.0 + mkdir .build + cd .build + cmake .. + make + sudo make install diff --git a/doc/filesystem.png b/doc/filesystem.png deleted file mode 100644 index 4b4106a..0000000 Binary files a/doc/filesystem.png and /dev/null differ diff --git a/doc/filesystem.uml b/doc/filesystem.uml deleted file mode 100644 index 1b78837..0000000 --- a/doc/filesystem.uml +++ /dev/null @@ -1,16 +0,0 @@ -@startuml - -salt -{ -{T -+ mount_point -++ fwupdate -+++ default -> 7c029f81-6bdf-4d3c-82dc-26f748164012 -+++ 7c029f81-6bdf-4d3c-82dc-26f748164012 -++++ update.raucb -+++ f93de23b-4535-4a47-a287-a381b78a11b8 -++++ update.raucb -} -} - -@enduml \ No newline at end of file diff --git a/doc/protocol.md b/doc/protocol.md new file mode 100644 index 0000000..32eaa9d --- /dev/null +++ b/doc/protocol.md @@ -0,0 +1,253 @@ +# Webfuse Protocol + +## Requests, responses and notifications + +There are three types of messages, used for communication between webfuse daemon and filesystem provider. All message types are encoded in [JSON](https://www.json.org/) and strongly inspired by [JSON-RPC](https://www.jsonrpc.org/). + +### Request + +A request is used by a sender to invoke a method on the receiver. The sender awaits a response from the receiver. Since requests and responses can be sendet or answered in any order, an id is provided in each request to identify it. + + { + "method": , + "params": , + "id" : + } + +| Item | Data type | Description | +| ----------- |:---------:| --------------------------------- | +| method_name | string | name of the method to invoke | +| params | array | method specific parameters | +| id | integer | id, which is repeated in response | + +### Response + +A response is used to answer a prior request. There are two kinds of responses: + +#### Successful Results + + { + "result": , + "id": + } + +| Item | Data type | Description | +| ----------- |:---------:| ----------------------- | +| result | any | request specific result | +| id | integer | id, same as request | + +#### Error notifications + + { + "error": { + "code": + }, + "id": + } + +| Item | Data type | Description | +| ----------- |:---------:| ------------------- | +| code | integer | error code | +| id | integer | id, same as request | + +#### Error codes + +| Symbolic name | Code | Description | +| ------------------ | ---------:| ---------------------- | +| GOOD | 0 | no error | +| BAD | 1 | generic error | +| BAD_NOTIMPLEMENTED | 2 | method not implemented | +| BAD_TIMEOUT | 3 | timeout occured | +| BAD_BUSY | 4 | resource busy | +| BAD_FORMAT | 5 | invalid formt | +| BAD_NOENTRY | 101 | invalid entry | +| BAD_ACCESS_DENIED | 102 | access not allowed | + +### Notification + +Notfications are used to inform a receiver about something. Unlike requests, notifications are not answered. Therefore, an id is not supplied. + + { + "method": , + "params": + } + +| Item | Data type | Description | +| ----------- |:---------:| --------------------------------- | +| method_name | string | name of the method to invoke | +| params | array | method specific parameters | + +## Requests (Adapter -> Provider) + +### lookup + +Retrieve information about a filesystem entry by name. + + webfuse daemon: {"method": "lookup", "params": [, , ], "id": } + fs provider: {"result": { + "inode": , + "mode" : , + "type" : , + "size" : , + "atime": , + "mtime": , + "ctime": + }, "id": } + +| Item | Data type | Description | +| ----------- | --------------- | ------------------------------------------- | +| filesystem | string | name of the filesystem | +| parent | integer | inode of parent directory (1 = root) | +| name | string | name of the filesystem object to look up | +| inode | integer | inode of the filesystem object | +| mode | integer | unix file mode | +| type | "file" or "dir" | type of filesystem object | +| size | integer | required for files; file size in bytes | +| atime | integer | optional; unix time of last access | +| mtime | integer | optional; unix time of last modification | +| ctime | intefer | optional; unix time of last metadata change | + +### getattr + +Get file attributes. + + webfuse daemon: {"method": "getattr", "params": [, ], "id": } + fs provider: {"result": { + "mode" : , + "type" : , + "size" : , + "atime": , + "mtime": , + "ctime": + }, "id": } + +| Item | Data type | Description | +| ----------- | --------------- | ------------------------------------------- | +| filesystem | string | name of the filesystem | +| inode | integer | inode of the filesystem object | +| mode | integer | unix file mode | +| type | "file" or "dir" | type of filesystem object | +| size | integer | required for files; file size in bytes | +| atime | integer | optional; unix time of last access | +| mtime | integer | optional; unix time of last modification | +| ctime | intefer | optional; unix time of last metadata change | + +### readdir + +Read directory contents. +Result is an array of name-inode pairs for each entry. The generic entries +"." and ".." should also be provided. + + webfuse daemon: {"method": "readdir", "params": [, ], "id": } + fs provider: {"result": [ + {"name": , "inode": }, + ... + ], "id": } + +| Item | Data type | Description | +| ----------- | --------------- | ------------------------------ | +| filesystem | string | name of the filesystem | +| dir_inode | integer | inode of the directory to read | +| name | integer | name of the entry | +| inode | integer | inode of the entry | + +### open + +Open a file. + + webfuse daemon: {"method": "readdir", "params": [, , ], "id": } + fs provider: {"result": {"handle": }, "id": } + +| Item | Data type | Description | +| ----------- | ----------| ----------------------------- | +| filesystem | string | name of the filesystem | +| inode | integer | inode of the file | +| flags | integer | access mode flags (see below) | +| handle | integer | handle of the file | + +#### Flags + +| Symbolic name | Code | Description | +| --------------| ---------:| --------------------------- | +| O_ACCMODE | 0x003 | access mode mask | +| O_RDONLY | 0x000 | open for reading only | +| O_WRONLY | 0x001 | open for writing only | +| O_RDWR | 0x002 | open for reading an writing | +| O_CREAT | 0x040 | create (a new) file | +| O_EXCL | 0x080 | open file exclusivly | +| O_TRUNC | 0x200 | open file to truncate | +| O_APPEND | 0x400 | open file to append | + +### close + +Informs filesystem provider, that a file is closed. +Since `close` is a notification, it cannot fail. + + webfuse daemon: {"method": "close", "params": [, , , ], "id": } + +| Item | Data type | Description | +| ----------- | ----------| ---------------------------- | +| filesystem | string | name of the filesystem | +| inode | integer | inode of the file | +| handle | integer | handle of the file | +| flags | integer | access mode flags (see open) | + +### read + +Read from an open file. + + webfuse daemon: {"method": "close", "params": [, , , , ], "id": } + fs provider: {"result": { + "data": , + "format": , + "count": + }, "id": } + +| Item | Data type | Description | +| ----------- | ----------| ----------------------------- | +| filesystem | string | name of the filesystem | +| inode | integer | inode of the file | +| handle | integer | handle of the file | +| offset | integer | Offet to start read operation | +| length | integer | Max. number of bytes to read | +| data | integer | handle of the file | +| format | string | Encoding of data (see below) | +| count | integer | Actual number of bytes read | + +#### Format + +| Format | Description | +| ---------- | -------------------------------------------------------- | +| "identiy" | Use data as is; note that JSON strings are UTF-8 encoded | +| "base64" | data is base64 encoded | + +## Requests (Provider -> Adapter) + +### add_filesystem + +Adds a filesystem. + + fs provider: {"method": "add_filesystem", "params": [], "id": } + webfuse daemon: {"result": {"id": }, "id": } + +| Item | Data type | Description | +| ----------- | ----------| ------------------------------- | +| name | string | name and id of filesystem | + +### authtenticate + +Authenticate the provider. +If authentication is enabled, a provider must be authenticated by the adapter before filesystems can be added. + + fs provider: {"method": "authenticate", "params": [, ], "id": } + webfuse daemon: {"result": {}, "id": } + +| Item | Data type | Description | +| ----------- | ----------| ------------------------------- | +| type | string | authentication type (see below) | +| credentials | object | credentials to authenticate | + +#### authentication types + +- **username**: authenticate via username and password + `{"username": , "password": }` diff --git a/example/daemon/main.c b/example/daemon/main.c deleted file mode 100644 index 2cde4ab..0000000 --- a/example/daemon/main.c +++ /dev/null @@ -1,196 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#define SERVICE_TIMEOUT (1 * 1000) - -struct args -{ - struct wf_server_config * config; - char * passwd_path; - bool show_help; -}; - -static bool shutdown_requested = false; - -static void show_help(void) -{ - printf( - "webfused, Copyright (c) 2019, webfuse authors \n" - "Websocket file system daemon\n" - "\n" - "Usage: webfused [m ] [-d ] [-p ]\n" - " [-c ] [-k ] [-P ]\n" - "\n" - "Options:\n" - "\t-m, --mount_point Path of mount point (required)\n" - "\t-d, --document_root Path of www directory (default: not set, www disabled)\n" - "\t-c, --server_cert_path Path of servers own certificate (default: not set, TLS disabled)\n" - "\t-k, --server_key_path Path of servers private key (default: not set, TLS disabled)\n" - "\t-n, --vhost_name Name of virtual host (default: \"localhost\")\n" - "\t-p, --port Number of servers port (default: 8080)\n" - "\t-P, --passwd_path Path to password file (default: not set, authentication disabled)\n" - "\n"); -} - -static bool authenticate(struct wf_credentials * creds, void * user_data) -{ - bool result = false; - struct args * args = user_data; - - char const * username = wf_credentials_get(creds, "username"); - char const * password = wf_credentials_get(creds, "password"); - if ((NULL != username) && (NULL != password)) - { - struct userdb * db = userdb_create(""); - result = userdb_load(db, args->passwd_path); - if (result) - { - result = userdb_check(db, username, password); - } - - userdb_dispose(db); - } - - return result; -} - -static int parse_arguments(int argc, char * argv[], struct args * args) -{ - static struct option const options[] = - { - {"mount_point", required_argument, NULL, 'm'}, - {"document_root", required_argument, NULL, 'd'}, - {"server_cert_path", required_argument, NULL, 'c'}, - {"server_key_path", required_argument, NULL, 'k'}, - {"vhost_name", required_argument, NULL, 'n'}, - {"port", required_argument, NULL, 'p'}, - {"passwd_path", required_argument, NULL, 'P'}, - {"help", no_argument, NULL, 'h'}, - {NULL, 0, NULL, 0} - }; - - bool result = EXIT_SUCCESS; - bool finished = false; - bool has_mountpoint = false; - while ((!finished) && (EXIT_SUCCESS == result)) - { - int option_index = 0; - int const c = getopt_long(argc, argv, "m:d:c:k:n:p:P:h", options, &option_index); - - switch (c) - { - case -1: - finished = true; - break; - case 'h': - args->show_help = true; - finished = true; - break; - case 'm': - wf_server_config_set_mountpoint(args->config, optarg); - has_mountpoint = true; - break; - case 'd': - wf_server_config_set_documentroot(args->config, optarg); - break; - case 'c': - wf_server_config_set_certpath(args->config, optarg); - break; - case 'k': - wf_server_config_set_keypath(args->config, optarg); - break; - case 'n': - wf_server_config_set_vhostname(args->config, optarg); - break; - case 'p': - wf_server_config_set_port(args->config, atoi(optarg)); - break; - case 'P': - free(args->passwd_path); - args->passwd_path = strdup(optarg); - wf_server_config_add_authenticator(args->config, - "username", - &authenticate, - args); - break; - default: - fprintf(stderr, "error: unknown argument\n"); - result = EXIT_FAILURE; - break; - } - } - - if ((EXIT_SUCCESS == result) && (!args->show_help)) - { - if (!has_mountpoint) - { - fprintf(stderr, "error: missing mount point\n"); - result = EXIT_FAILURE; - } - } - - if (EXIT_SUCCESS != result) - { - args->show_help = true; - } - - return result; -} - -static void on_interrupt(int signal_id) -{ - (void) signal_id; - - shutdown_requested = true; -} - -int main(int argc, char * argv[]) -{ - struct args args; - args.config = wf_server_config_create(); - wf_server_config_set_vhostname(args.config, "localhost"); - wf_server_config_set_port(args.config, 8080); - args.passwd_path = NULL; - args.show_help = false; - - int result = parse_arguments(argc, argv, &args); - - if (!args.show_help) - { - signal(SIGINT, on_interrupt); - struct wf_server * server = wf_server_create(args.config); - if (NULL != server) - { - while (!shutdown_requested) - { - wf_server_service(server, SERVICE_TIMEOUT); - } - - wf_server_dispose(server); - } - else - { - fprintf(stderr, "fatal: unable start server\n"); - result = EXIT_FAILURE; - } - } - else - { - show_help(); - } - - free(args.passwd_path); - wf_server_config_dispose(args.config); - return result; -} - diff --git a/example/daemon/www/index.html b/example/daemon/www/index.html deleted file mode 100644 index 0cb0918..0000000 --- a/example/daemon/www/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - WebFuse Example - - - - - - -
-
-
Connection
-
-
-
- - - \ No newline at end of file diff --git a/example/daemon/www/js/.eslintrc.js b/example/daemon/www/js/.eslintrc.js deleted file mode 100644 index 9090e7d..0000000 --- a/example/daemon/www/js/.eslintrc.js +++ /dev/null @@ -1,287 +0,0 @@ -module.exports = { - "env": { - "browser": true, - "es6": true - }, - "extends": "eslint:recommended", - "globals": { - "Atomics": "readonly", - "SharedArrayBuffer": "readonly" - }, - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module" - }, - "rules": { - "accessor-pairs": "error", - "array-bracket-newline": "error", - "array-bracket-spacing": "error", - "array-callback-return": "error", - "array-element-newline": ["error", "consistent"], - "arrow-body-style": "error", - "arrow-parens": [ - "error", - "always" - ], - "arrow-spacing": [ - "error", - { - "after": true, - "before": true - } - ], - "block-scoped-var": "error", - "block-spacing": [ - "error", - "always" - ], - "brace-style": "off", - "callback-return": "off", - "camelcase": "error", - "capitalized-comments": [ - "error", - "never" - ], - "class-methods-use-this": "off", - "comma-dangle": "error", - "comma-spacing": [ - "error", - { - "after": true, - "before": false - } - ], - "comma-style": [ - "error", - "last" - ], - "complexity": "error", - "computed-property-spacing": [ - "error", - "never" - ], - "consistent-return": "error", - "consistent-this": "error", - "curly": "error", - "default-case": "error", - "dot-location": "error", - "dot-notation": "error", - "eol-last": "off", - "eqeqeq": "off", - "func-call-spacing": "error", - "func-name-matching": "error", - "func-names": "error", - "func-style": [ - "error", - "declaration" - ], - "function-paren-newline": "error", - "generator-star-spacing": "error", - "global-require": "error", - "guard-for-in": "error", - "handle-callback-err": "error", - "id-blacklist": "error", - "id-length": "error", - "id-match": "error", - "implicit-arrow-linebreak": [ - "error", - "beside" - ], - "indent": "off", - "indent-legacy": "off", - "init-declarations": "off", - "jsx-quotes": "error", - "key-spacing": "off", - "keyword-spacing": "off", - "line-comment-position": "error", - "linebreak-style": [ - "error", - "unix" - ], - "lines-around-comment": "error", - "lines-around-directive": "error", - "lines-between-class-members": "off", - "max-classes-per-file": "error", - "max-depth": "error", - "max-len": "off", - "max-lines": "error", - "max-lines-per-function": "off", - "max-nested-callbacks": "error", - "max-params": "off", - "max-statements": "off", - "max-statements-per-line": "off", - "multiline-comment-style": "error", - "new-cap": "error", - "new-parens": "error", - "newline-after-var": "off", - "newline-before-return": "error", - "newline-per-chained-call": "error", - "no-alert": "error", - "no-array-constructor": "error", - "no-async-promise-executor": "error", - "no-await-in-loop": "error", - "no-bitwise": "off", - "no-buffer-constructor": "error", - "no-caller": "error", - "no-catch-shadow": "error", - "no-confusing-arrow": "error", - "no-continue": "error", - "no-div-regex": "error", - "no-duplicate-imports": "error", - "no-else-return": "off", - "no-empty-function": "off", - "no-eq-null": "error", - "no-eval": "error", - "no-extend-native": "error", - "no-extra-bind": "error", - "no-extra-label": "error", - "no-extra-parens": "off", - "no-floating-decimal": "error", - "no-implicit-coercion": "error", - "no-implicit-globals": "off", - "no-implied-eval": "error", - "no-inline-comments": "error", - "no-invalid-this": "error", - "no-iterator": "error", - "no-label-var": "error", - "no-labels": "error", - "no-lone-blocks": "error", - "no-lonely-if": "error", - "no-loop-func": "error", - "no-magic-numbers": "off", - "no-misleading-character-class": "error", - "no-mixed-operators": "error", - "no-mixed-requires": "error", - "no-multi-assign": "error", - "no-multi-spaces": "off", - "no-multi-str": "error", - "no-multiple-empty-lines": "error", - "no-native-reassign": "error", - "no-negated-condition": "off", - "no-negated-in-lhs": "error", - "no-nested-ternary": "error", - "no-new": "error", - "no-new-func": "error", - "no-new-object": "error", - "no-new-require": "error", - "no-new-wrappers": "error", - "no-octal-escape": "error", - "no-param-reassign": "error", - "no-path-concat": "error", - "no-plusplus": "error", - "no-process-env": "error", - "no-process-exit": "error", - "no-proto": "error", - "no-prototype-builtins": "error", - "no-restricted-globals": "error", - "no-restricted-imports": "error", - "no-restricted-modules": "error", - "no-restricted-properties": "error", - "no-restricted-syntax": "error", - "no-return-assign": "error", - "no-return-await": "error", - "no-script-url": "error", - "no-self-compare": "error", - "no-sequences": "error", - "no-shadow": "off", - "no-shadow-restricted-names": "error", - "no-spaced-func": "error", - "no-sync": "error", - "no-tabs": "off", - "no-template-curly-in-string": "error", - "no-ternary": "off", - "no-throw-literal": "error", - "no-trailing-spaces": "off", - "no-undef-init": "error", - "no-undefined": "error", - "no-unmodified-loop-condition": "error", - "no-unneeded-ternary": "error", - "no-unused-expressions": "error", - "no-use-before-define": "error", - "no-useless-call": "error", - // "no-useless-catch": "error", - "no-useless-computed-key": "error", - "no-useless-concat": "error", - "no-useless-constructor": "error", - "no-useless-rename": "error", - "no-useless-return": "error", - "no-var": "error", - "no-void": "error", - "no-warning-comments": "error", - "no-whitespace-before-property": "error", - "no-with": "error", - "nonblock-statement-body-position": "error", - "object-curly-newline": "error", - "object-curly-spacing": "off", - "object-shorthand": "off", - "one-var": "off", - "one-var-declaration-per-line": "error", - "operator-assignment": "error", - "operator-linebreak": "error", - "padded-blocks": "off", - "padding-line-between-statements": "error", - "prefer-arrow-callback": "error", - "prefer-const": "off", - "prefer-destructuring": "off", - "prefer-numeric-literals": "off", - "prefer-object-spread": "error", - "prefer-promise-reject-errors": "error", - "prefer-reflect": "error", - "prefer-rest-params": "error", - "prefer-spread": "error", - "prefer-template": "error", - "quote-props": "off", - "quotes": "off", - "radix": "error", - "require-atomic-updates": "error", - "require-await": "off", - "require-jsdoc": "off", - "require-unicode-regexp": "off", - "rest-spread-spacing": "error", - "semi": "error", - "semi-spacing": "error", - "semi-style": [ - "error", - "last" - ], - "sort-imports": "error", - "sort-keys": "off", - "sort-vars": "error", - "space-before-blocks": "error", - "space-before-function-paren": "off", - "space-in-parens": [ - "error", - "never" - ], - "space-infix-ops": "error", - "space-unary-ops": [ - "error", - { - "nonwords": false, - "words": false - } - ], - "spaced-comment": [ - "error", - "always" - ], - "strict": [ - "error", - "never" - ], - "switch-colon-spacing": "error", - "symbol-description": "error", - "template-curly-spacing": "error", - "template-tag-spacing": "error", - "unicode-bom": [ - "error", - "never" - ], - "valid-jsdoc": "error", - "vars-on-top": "error", - "wrap-iife": "error", - "wrap-regex": "error", - "yield-star-spacing": "error", - "yoda": "off" - } -}; \ No newline at end of file diff --git a/example/daemon/www/js/connection_view.js b/example/daemon/www/js/connection_view.js deleted file mode 100644 index 31576ae..0000000 --- a/example/daemon/www/js/connection_view.js +++ /dev/null @@ -1,93 +0,0 @@ -export class ConnectionView { - constructor(client, provider) { - this._provider = provider; - this._client = client; - this._client.onopen = () => { this._onConnectionOpened(); }; - this._client.onclose = () => { this._onConnectionClosed(); }; - - this.element = document.createElement("div"); - - const connectBox = document.createElement("div"); - this.element.appendChild(connectBox); - - const urlLabel = document.createElement("span"); - urlLabel.textContent = "URL:"; - connectBox.appendChild(urlLabel); - - this.urlTextbox = document.createElement("input"); - this.urlTextbox.type = "text"; - this.urlTextbox.value = window.location.href.replace(/^http/, "ws"); - connectBox.appendChild(this.urlTextbox); - - this.connectButton = document.createElement("input"); - this.connectButton.type = "button"; - this.connectButton.value = "connect"; - this.connectButton.addEventListener("click", () => { this._onConnectButtonClicked(); }); - connectBox.appendChild(this.connectButton); - - - const authenticateBox = document.createElement("div"); - this.element.appendChild(authenticateBox); - - const authLabel = document.createElement("span"); - authLabel.textContent = "use authentication:"; - authenticateBox.appendChild(authLabel); - - this.authenticateCheckbox = document.createElement("input"); - this.authenticateCheckbox.type = "checkbox"; - authenticateBox.appendChild(this.authenticateCheckbox); - - const usernameLabel = document.createElement("span"); - usernameLabel.textContent = "user:"; - authenticateBox.appendChild(usernameLabel); - - this.usernameTextbox = document.createElement("input"); - this.usernameTextbox.type = "text"; - this.usernameTextbox.value = "bob"; - authenticateBox.appendChild(this.usernameTextbox); - - const passwordLabel = document.createElement("span"); - passwordLabel.textContent = "user:"; - authenticateBox.appendChild(passwordLabel); - - this.passwordTextbox = document.createElement("input"); - this.passwordTextbox.type = "password"; - this.passwordTextbox.value = "secret"; - authenticateBox.appendChild(this.passwordTextbox); - } - - _onConnectButtonClicked() { - if (!this._client.isConnected()) { - let url = this.urlTextbox.value; - this._client.connectTo(url); - } - else { - this._client.disconnect(); - } - } - - _onAuthenticateButtonClicked() { - if (this._client.isConnected()) { - - } - } - - _onConnectionOpened() { - if (this.authenticateCheckbox.checked) { - const username = this.usernameTextbox.value; - const password = this.passwordTextbox.value; - - const promise = this._client.authenticate("username", { username, password }); - promise.then(() => { this._client.addProvider("test", this._provider); }); - } else { - this._client.addProvider("test", this._provider); - } - - this.connectButton.value = "disconnect"; - } - - _onConnectionClosed() { - this.connectButton.value = "connect"; - } - -} diff --git a/example/daemon/www/js/filesystem_provider.js b/example/daemon/www/js/filesystem_provider.js deleted file mode 100644 index 85ad287..0000000 --- a/example/daemon/www/js/filesystem_provider.js +++ /dev/null @@ -1,122 +0,0 @@ -/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */ - -import { BadState } from "./webfuse/bad_state.js"; -import { FileMode } from "./webfuse/file_mode.js"; -import { Provider } from "./webfuse/provider.js"; - -export class FileSystemProvider extends Provider { - constructor(root) { - super(); - - this.root = root; - this._inodes = { }; - - this._walk(this.root, (entry) => { this._inodes[entry.inode] = entry; }); - } - - _walk(node, callback) { - callback(node); - - const entries = node.entries; - if (entries) { - for(let entry of Object.entries(entries)) { - this._walk(entry[1], callback); - } - } - } - - - async lookup(parent, name) { - const parentEntry = this._inodes[parent]; - const entry = (parentEntry && parentEntry.entries && parentEntry.entries[name]) || null; - if (entry) { - return { - inode: entry.inode, - mode: entry.mode || parseInt("755", 8), - type: entry.type || "file", - size: entry.size || (entry.contents && entry.contents.length) || 0, - atime: entry.atime || 0, - mtime: entry.mtime || 0, - ctime: entry.ctime || 0 - }; - } - else { - throw new BadState(BadState.NO_ENTRY); - } - } - - - async getattr(inode) { - let entry = this._inodes[inode]; - if (entry) { - return { - mode: entry.mode || parseInt("755", 8), - type: entry.type || "file", - size: entry.size || (entry.contents && entry.contents.length) || 0, - atime: entry.atime || 0, - mtime: entry.mtime || 0, - ctime: entry.ctime || 0 - }; - } - else { - throw new BadState(BadState.NO_ENTRY); - } - } - - async readdir(inode) { - let entry = this._inodes[inode]; - - if ((entry) && ("dir" === entry.type)) { - let result = [ - {name: ".", inode: entry.inode}, - {name: "..", inode: entry.inode} - ]; - for(let subdir of Object.entries(entry.entries)) { - const name = subdir[0]; - const inode = subdir[1].inode; - result.push({name, inode}); - } - - return result; - } - else { - throw new BadState(BadState.NO_ENTRY); - } - - } - - async open(inode, mode) { - let entry = this._inodes[inode]; - - if (entry.type === "file") { - if ((mode & FileMode.ACCESS_MODE) === FileMode.READONLY) { - return {handle: 1337}; - } - else { - throw new BadState(BadState.NO_ACCESS); - } - } - else { - throw new BadState(BadState.NO_ENTRY); - } - } - - close(_inode, _handle, _mode) { - // do nothing - return true; - } - - async read(inode, handle, offset, length) { - let entry = this._inodes[inode]; - - if (entry.type === "file") { - const end = Math.min(offset + length, entry.contents.length); - const data = (offset < entry.contents.length) ? entry.contents.substring(offset, end) : ""; - - return data; - } - else { - throw new BadState(BadState.NO_ENTRY); - } - } -} \ No newline at end of file diff --git a/example/daemon/www/js/package.json b/example/daemon/www/js/package.json deleted file mode 100644 index 6c553ef..0000000 --- a/example/daemon/www/js/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "webfuse-provider", - "version": "0.2.0", - "description": "Provider for websocket filesystem (webfuse)", - "main": "startup.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "Falk Werner", - "license": "LGPL-3.0" -} diff --git a/example/daemon/www/js/startup.js b/example/daemon/www/js/startup.js deleted file mode 100644 index 284d229..0000000 --- a/example/daemon/www/js/startup.js +++ /dev/null @@ -1,25 +0,0 @@ -import { Client } from "./webfuse/client.js"; -import { ConnectionView } from "./connection_view.js"; -import { FileSystemProvider } from "./filesystem_provider.js"; - - -function mode(value) { - return parseInt(value, 8); -} - -function startup() { - const provider = new FileSystemProvider({ - inode: 1, - mode: mode("0755"), - type: "dir", - entries: { - "hello.txt" : { inode: 2, mode: mode("0444"), type: "file", contents: "Hello, World!"}, - "say_hello.sh": { inode: 3, mode: mode("0555"), type: "file", contents: "#!/bin/sh\necho hello\n"} - } - }); - const client = new Client(); - const connectionView = new ConnectionView(client, provider); - document.getElementById('connection').appendChild(connectionView.element); -} - -window.onload = startup; diff --git a/example/daemon/www/js/webfuse/bad_state.js b/example/daemon/www/js/webfuse/bad_state.js deleted file mode 100644 index fdb05d9..0000000 --- a/example/daemon/www/js/webfuse/bad_state.js +++ /dev/null @@ -1,15 +0,0 @@ -export class BadState extends Error { - static get BAD() { return 1; } - - static get NOT_IMPLEMENTED() { return 2; } - static get TIMEOUT() { return 3; } - static get FORMAT() { return 4; } - - static get NO_ENTRY() { return 101; } - static get NO_ACCESS() { return 102; } - - constructor(code) { - super("Bad State"); - this.code = code; - } -} \ No newline at end of file diff --git a/example/daemon/www/js/webfuse/client.js b/example/daemon/www/js/webfuse/client.js deleted file mode 100644 index 65d24f2..0000000 --- a/example/daemon/www/js/webfuse/client.js +++ /dev/null @@ -1,223 +0,0 @@ -import { BadState } from "./bad_state.js"; - -export class Client { - static get _PROTOCOL() { return "fs"; } - - constructor(provider) { - this._provider = { }; - this._pendingRequests = {}; - this._id = 0; - this._ws = null; - this.onopen = () => { }; - this.onclose = () => { }; - this.onerror = () => { }; - } - - connectTo(url) { - this.disconnect(); - - this._ws = new WebSocket(url, Client._PROTOCOL); - this._ws.onopen = this.onopen; - this._ws.onclose = this.onclose; - this._ws.onerror = this.onerror; - - this._ws.onmessage = (message) => { - this._onmessage(message); - }; - } - - _invokeRequest(method, params) { - this._id += 1; - const id = this._id; - const request = {method, params, id}; - - return new Promise((resolve, reject) => { - this._pendingRequests[id] = {resolve, reject}; - this._ws.send(JSON.stringify(request)); - }); - } - - authenticate(type, credentials) { - return this._invokeRequest("authenticate", [type, credentials]); - } - - addProvider(name, provider) { - this._provider[name] = provider; - const request = { - "method": "add_filesystem", - "params": [name], - "id": 23 - }; - - this._ws.send(JSON.stringify(request)); - } - - disconnect() { - if (this._ws) { - this._ws.close(); - this._ws = null; - } - } - - isConnected() { - return ((this._ws) && (this._ws.readyState === WebSocket.OPEN)); - } - - _isRequest(request) { - const method = request.method; - - return (("string" === typeof(method)) && ("params" in request)); - } - - _isResponse(response) { - const id = response.id; - - return (("number" === typeof(id)) && (("result" in response) || ("error" in response))); - } - - _removePendingRequest(id) { - let result = null; - - if (id in this._pendingRequests) { - result = this._pendingRequests[id]; - Reflect.deleteProperty(this._pendingRequests, id); - } - - return result; - } - - _onmessage(message) { - try { - const data = JSON.parse(message.data); - - if (this._isRequest(data)) { - const method = data.method; - const id = data.id; - const params = data.params; - - if ("number" === typeof(id)) { - this._invoke(method, params, id); - } - else { - this._notify(method, params); - } - } - else if (this._isResponse(data)) { - const id = data.id; - const result = data.result; - const error = data.error; - - const request = this._removePendingRequest(id); - if (request) { - if (result) { - request.resolve(result); - } - else { - request.reject(error); - } - } - } - - } - catch (ex) { - // swallow - } - } - - _invoke(method, params, id) { - this._invokeAsync(method, params). - then((result) => { - const response = { result, id }; - this._ws.send(JSON.stringify(response)); - }). - catch((ex) => { - const code = ex.code || BadState.BAD; - const response = {error: {code}, id}; - this._ws.send(JSON.stringify(response)); - }); - - } - - async _invokeAsync(method, params) { - switch(method) - { - case "lookup": - return this._lookup(params); - case "getattr": - return this._getattr(params); - case "readdir": - return this._readdir(params); - case "open": - return this._open(params); - case "read": - return this._read(params); - default: - throw new BadState(BadState.NOT_IMPLEMENTED); - } - } - - _notify(method, params) { - switch(method) { - case 'close': - this._close(params); - break; - default: - throw new Error(`Invalid method: "${method}"`); - } - } - - _getProvider(name) { - if (name in this._provider) { - return this._provider[name]; - } - else { - throw new Error('Unknown provider'); - } - } - - async _lookup([providerName, parent, name]) { - const provider = this._getProvider(providerName); - - return provider.lookup(parent, name); - } - - async _getattr([providerName, inode]) { - const provider = this._getProvider(providerName); - - return provider.getattr(inode); - } - - async _readdir([providerName, inode]) { - const provider = this._getProvider(providerName); - - return provider.readdir(inode); - } - - async _open([providerName, inode, mode]) { - const provider = this._getProvider(providerName); - - return provider.open(inode, mode); - } - - _close([providerName, inode, handle, mode]) { - const provider = this._getProvider(providerName); - - provider.close(inode, handle, mode); - } - - async _read([providerName, inode, handle, offset, length]) { - const provider = this._getProvider(providerName); - const data = await provider.read(inode, handle, offset, length); - - if ("string" === typeof(data)) { - return { - data: btoa(data), - format: "base64", - count: data.length - }; - } - else { - throw new BadState(BadState.BAD); - } - } -} \ No newline at end of file diff --git a/example/daemon/www/js/webfuse/file_mode.js b/example/daemon/www/js/webfuse/file_mode.js deleted file mode 100644 index b81a1ea..0000000 --- a/example/daemon/www/js/webfuse/file_mode.js +++ /dev/null @@ -1,10 +0,0 @@ -export class FileMode { - static get ACCESS_MODE() { return 0x003; } - static get READONLY() { return 0x000; } - static get WRITEONLY() { return 0x001; } - static get READWRITE() { return 0x002; } - static get CREATE() { return 0x040; } - static get EXCLUSIVE() { return 0x080; } - static get TRUNKATE() { return 0x200; } - static get APPEND() { return 0x400; } -} \ No newline at end of file diff --git a/example/daemon/www/js/webfuse/provider.js b/example/daemon/www/js/webfuse/provider.js deleted file mode 100644 index b9c5dc2..0000000 --- a/example/daemon/www/js/webfuse/provider.js +++ /dev/null @@ -1,30 +0,0 @@ -/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */ - -import { BadState } from "./bad_state.js"; - -export class Provider { - - async lookup(_parent, _name) { - throw new BadState(BadState.NOT_IMPLEMENTED); - } - - async getattr(_inode) { - throw new BadState(BadState.NOT_IMPLEMENTED); - } - - async readdir(_inode) { - throw new BadState(BadState.NOT_IMPLEMENTED); - } - - async open(_inode, _mode) { - throw new BadState(BadState.NOT_IMPLEMENTED); - } - - close(_inode, _handle, _mode) { - // empty - } - - async read(_inode, _handle, _offset, _length) { - throw new BadState(BadState.NOT_IMPLEMENTED); - } -} diff --git a/example/daemon/www/style/main.css b/example/daemon/www/style/main.css deleted file mode 100644 index 399bfd1..0000000 --- a/example/daemon/www/style/main.css +++ /dev/null @@ -1,47 +0,0 @@ -html, body { - font-family: monospace; - background-color: #c0c0c0; -} - -.page { - margin-left: 50px; - margin-right: 50px; - width: auto; -} - -.window { - border: 1px solid black; - background-color: black; - border-radius: 5px; - padding: 10px; - margin-bottom: 25px; - color: white; -} - -.window .title { - text-align: center; - color: #dba329; - font-weight: bold; - padding-bottom: 10px; - margin-bottom: 10px; - border-bottom: 1px solid #dba329; -} - -.commands { - text-align: right; -} - -.content { - column-count: 2; - column-width: 50%; -} - -.content > div { - display: inline-block; - width: 100%; -} - -#connection { - text-align: center; -} - diff --git a/example/lib/userdb/include/userdb.h b/example/lib/userdb/include/userdb.h deleted file mode 100644 index cf133d0..0000000 --- a/example/lib/userdb/include/userdb.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef USERDB_H -#define USERDB_H - -#ifndef __cplusplus -#include -#endif - -#ifdef __cplusplus -extern "C" -{ -#endif - -struct userdb; - -extern struct userdb * userdb_create( - char const * pepper); - -extern void userdb_dispose(struct userdb * db); - -extern bool userdb_save( - struct userdb * db, - char const * filename); - -extern bool userdb_load( - struct userdb * db, - char const * filename); - -extern void userdb_add( - struct userdb * db, - char const * username, - char const * password); - -extern void userdb_remove( - struct userdb * db, - char const * user); - -extern bool userdb_check( - struct userdb * db, - char const * username, - char const * password); - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file diff --git a/example/lib/userdb/src/userdb.c b/example/lib/userdb/src/userdb.c deleted file mode 100644 index 172742a..0000000 --- a/example/lib/userdb/src/userdb.c +++ /dev/null @@ -1,277 +0,0 @@ -#include "userdb.h" - -#include -#include -#include -#include - -#include -#include - -#define USERDB_HASH_ALGORITHM "sha512" -#define USERDB_MAJOR 1 -#define USERDB_MINOR 0 - -#define USERDB_SALT_SIZE 32 - -struct userdb -{ - json_t * users; - char * pepper; - char * hash_algorithm; -}; - -static bool is_compatible(json_t * meta) -{ - bool result = false; - if (json_is_object(meta)) - { - json_t * type = json_object_get(meta, "type"); - json_t * major = json_object_get(meta, "major"); - json_t * minor = json_object_get(meta, "minor"); - json_t * hash_algorithm = json_object_get(meta, "hash_algorithm"); - - result = ( - json_is_string(type) && - (0 == strcmp(json_string_value(type), "wf-userdb")) && - json_is_integer(major) && - (USERDB_MAJOR == json_integer_value(major)) && - json_is_integer(minor) && - json_is_string(hash_algorithm) - ); - - if (result) - { - char const * algorithm = json_string_value(hash_algorithm); - result = (NULL != EVP_get_digestbyname(algorithm)); - } - - } - return result; -} - -static char hex_char(unsigned char value) -{ - switch (value) - { - case 0x00: return '0'; - case 0x01: return '1'; - case 0x02: return '2'; - case 0x03: return '3'; - case 0x04: return '4'; - case 0x05: return '5'; - case 0x06: return '6'; - case 0x07: return '7'; - case 0x08: return '8'; - case 0x09: return '9'; - case 0x0a: return 'a'; - case 0x0b: return 'b'; - case 0x0c: return 'c'; - case 0x0d: return 'd'; - case 0x0e: return 'e'; - case 0x0f: return 'f'; - default: return '?'; - } -} - -static char * to_hex(unsigned char const * value, size_t length) -{ - char * result = malloc((2 * length) + 1); - if (NULL != result) - { - for (size_t i = 0, j = 0; i < length; i++, j+=2) - { - unsigned char high = (value[i] >> 4) & 0x0f; - unsigned char low = value[i] & 0x0f; - - result[j ] = hex_char(high); - result[j + 1] = hex_char(low); - } - - result[2 * length] = '\0'; - } - - return result; -} - -static char * generate_salt(void) -{ - unsigned char buffer[USERDB_SALT_SIZE]; - int rc = RAND_bytes(buffer, USERDB_SALT_SIZE); - if (1 != rc) - { - fprintf(stderr, "fatal: failed to generate salt (OpenSSL RAND_bytes failed)\n"); - exit(EXIT_FAILURE); - } - - return to_hex(buffer, USERDB_SALT_SIZE); -} - -static char * compute_hash( - struct userdb * db, - char const * password, - char const * salt) -{ - EVP_MD const * digest = EVP_get_digestbyname(db->hash_algorithm); - if (NULL == digest) - { - fprintf(stderr, "error: hash algorithm %s not supported\n", db->hash_algorithm); - return NULL; - } - - char * result = NULL; - unsigned int hash_size = EVP_MD_size(digest); - unsigned char * hash = malloc(hash_size); - - if (NULL != hash) - { - EVP_MD_CTX * context = EVP_MD_CTX_new(); - EVP_DigestInit_ex(context, digest, NULL); - EVP_DigestUpdate(context, password, strlen(password)); - EVP_DigestUpdate(context, salt, strlen(salt)); - EVP_DigestUpdate(context, db->pepper, strlen(db->pepper)); - EVP_DigestFinal_ex(context, hash, &hash_size); - EVP_MD_CTX_free(context); - - result = to_hex(hash, hash_size); - free(hash); - } - - return result; -} - -struct userdb * userdb_create( - char const * pepper) -{ - struct userdb * db = malloc(sizeof(struct userdb)); - if (NULL != db) - { - db->users = json_object(); - db->pepper = strdup(pepper); - db->hash_algorithm = strdup(USERDB_HASH_ALGORITHM); - } - - return db; -} - -void userdb_dispose( - struct userdb * db) -{ - json_decref(db->users); - free(db->pepper); - free(db->hash_algorithm); - free(db); -} - -bool userdb_save( - struct userdb * db, - char const * filename) -{ - json_t * container = json_object(); - - json_t * meta = json_object(); - json_object_set_new(meta, "type", json_string("wf-userdb")); - json_object_set_new(meta, "major", json_integer(USERDB_MAJOR)); - json_object_set_new(meta, "minor", json_integer(USERDB_MINOR)); - json_object_set_new(meta, "hash_algorithm", json_string(db->hash_algorithm)); - json_object_set_new(container, "meta", meta); - - json_object_set(container, "users", db->users); - - int result = json_dump_file(container, filename, JSON_INDENT(2)); - json_decref(container); - - return (0 == result); -} - - -bool userdb_load( - struct userdb * db, - char const * filename) -{ - bool result = false; - json_t * container = json_load_file(filename, 0, NULL); - if (NULL != container) - { - json_t * meta = json_object_get(container, "meta"); - json_t * users = json_object_get(container, "users"); - - if ((is_compatible(meta)) && (json_is_object(users))) { - json_t * hash_algorithm = json_object_get(meta, "hash_algorithm"); - free(db->hash_algorithm); - db->hash_algorithm = strdup(json_string_value(hash_algorithm)); - - json_decref(db->users); - json_incref(users); - db->users = users; - - result = true; - } - - json_decref(container); - } - - return result; -} - -void userdb_add( - struct userdb * db, - char const * username, - char const * password) -{ - char * salt = generate_salt(); - char * hash = compute_hash(db, password, salt); - - json_t * user = json_object(); - json_object_set_new(user, "password_hash", json_string(hash)); - json_object_set_new(user, "salt", json_string(salt)); - - json_object_set_new(db->users, username, user); - - free(salt); - free(hash); -} - -void userdb_remove( - struct userdb * db, - char const * user) -{ - json_object_del(db->users, user); -} - -static char const * json_object_get_string( - json_t * object, - char const * key) -{ - char const * result = NULL; - - json_t * string_holder = json_object_get(object, key); - if (json_is_string(string_holder)) - { - result = json_string_value(string_holder); - } - - return result; -} - -bool userdb_check( - struct userdb * db, - char const * username, - char const * password) -{ - bool result = false; - - json_t * user = json_object_get(db->users, username); - if (json_is_object(user)) - { - char const * salt = json_object_get_string(user, "salt"); - char const * hash = json_object_get_string(user, "password_hash"); - - char * computed_hash = compute_hash(db, password, salt); - - result = (0 == strcmp(computed_hash, hash)); - free(computed_hash); - } - - return result; -} \ No newline at end of file diff --git a/example/passwd/main.c b/example/passwd/main.c deleted file mode 100644 index 83862cc..0000000 --- a/example/passwd/main.c +++ /dev/null @@ -1,304 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - - -struct args -{ - char * file; - char * command; - char * username; - char * password; - char * pepper; - bool show_help; -}; - -typedef int command_invoke_fn( - struct args * args); - -struct command -{ - char const * name; - command_invoke_fn * invoke; -}; - -static void print_usage(void) -{ - printf( - "webfuse-passwd, Copyright (c) 2019, webfuse authors \n" - "Manage webfuse passwd file\n" - "\n" - "Usage: webfuse-passwd -f -c [-u ] [-p ] [-P ]\n" - "\n" - "Options:\n" - "\t-f, --file Path of wf passwd file\n" - "\t-c, --command Command to execute\n" - "\t-u, --username Name of user\n" - "\t-p, --password Password of user\n" - "\t-P, --pepper pepper\n" - "\t-h, --help Shows this message\n" - "\n" - "Commands:\n" - "\tcreate Creates an empty passwd file (or cleans an existing)\n" - "\t Example: webfuse-passwd -f passwd.json -c create\n" - "\tadd Adds or replaces a user\n" - "\t Example: webfuse-passwd -f passwd.json -c add -u bob -p secret\n" - "\tremove Removes a user\n" - "\t Example: webfuse-passwd -f passwd.json -c remove -u bob\n" - "\tcheck Checks password of a user\n" - "\t Example: webfuse-passwd -f passwd.json -c check -u bob -p secret\n" - "\n" - ); -} - -static int parse_args(struct args * args, int argc, char * argv[]) -{ - static struct option const options[] = - { - {"file", required_argument, NULL, 'f'}, - {"command", required_argument, NULL, 'c'}, - {"username", required_argument, NULL, 'u'}, - {"password", required_argument, NULL, 'p'}, - {"Pepper", required_argument, NULL, 'P'}, - {"help", required_argument, NULL, 'h'}, - {NULL, 0, NULL, 0} - }; - - int result = EXIT_SUCCESS; - bool finished = false; - while ((!finished) && (EXIT_SUCCESS == result)) - { - int option_index = 0; - int const c = getopt_long(argc, argv, "f:c:u:p:P:h", options, &option_index); - - switch (c) - { - case -1: - finished = true; - break; - case 'h': - args->show_help = true; - finished = true; - break; - case 'f': - free(args->file); - args->file = strdup(optarg); - break; - case 'c': - free(args->command); - args->command = strdup(optarg); - break; - case 'u': - free(args->username); - args->username = strdup(optarg); - break; - case 'p': - free(args->password); - args->password = strdup(optarg); - break; - case 'P': - free(args->pepper); - args->pepper = strdup(optarg); - break; - default: - fprintf(stderr, "error: unknown argument\n"); - result = EXIT_FAILURE; - break; - } - } - - if ((result == EXIT_SUCCESS) && (!args->show_help)) - { - if (NULL == args->file) - { - fprintf(stderr, "error: missing file\n"); - args->show_help = true; - result = EXIT_FAILURE; - } - else if (NULL == args->command) - { - fprintf(stderr, "error: missing command\n"); - args->show_help = true; - result = EXIT_FAILURE; - } - } - - return result; -} - -static void args_init(struct args * args) -{ - args->file = NULL; - args->command = NULL; - args->username = NULL; - args->password = NULL; - args->pepper = strdup(""); - args->show_help = false; -} - -static int create_passwd(struct args * args) -{ - struct userdb * db = userdb_create(args->pepper); - bool result = userdb_save(db, args->file); - userdb_dispose(db); - - return (result) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -static int add_user(struct args * args) -{ - if (NULL == args->username) - { - fprintf(stderr, "error: missing username"); - args->show_help = true; - return EXIT_FAILURE; - } - - if (NULL == args->password) - { - fprintf(stderr, "error: missing password"); - args->show_help = true; - return EXIT_FAILURE; - } - - struct userdb * db = userdb_create(args->pepper); - userdb_load(db, args->file); - userdb_add(db, args->username, args->password); - bool result = userdb_save(db, args->file); - userdb_dispose(db); - - return (result) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -static int remove_user(struct args * args) -{ - if (NULL == args->username) - { - fprintf(stderr, "error: missing username"); - args->show_help = true; - return EXIT_FAILURE; - } - - struct userdb * db = userdb_create(args->pepper); - userdb_load(db, args->file); - userdb_remove(db, args->username); - bool result = userdb_save(db, args->file); - userdb_dispose(db); - - return (result) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -static int check_password(struct args * args) -{ - if (NULL == args->username) - { - fprintf(stderr, "error: missing username"); - args->show_help = true; - return EXIT_FAILURE; - } - - if (NULL == args->password) - { - fprintf(stderr, "error: missing password"); - args->show_help = true; - return EXIT_FAILURE; - } - - struct userdb * db = userdb_create(args->pepper); - userdb_load(db, args->file); - bool result = userdb_check(db, args->username, args->password); - userdb_dispose(db); - - printf("%s\n", (result) ? "OK" : "FAILURE"); - return (result) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -static int invoke_invalid_command(struct args * args) -{ - (void) args; - - fprintf(stderr, "error: unknown command\n"); - return EXIT_FAILURE; -} - -static struct command const commands[] = -{ - {"create", &create_passwd}, - {"add", &add_user}, - {"remove", &remove_user}, - {"check", &check_password}, - {NULL, NULL} -}; - -static struct command const invalid_command = -{ - "", - &invoke_invalid_command -}; - -static struct command const * get_command(char const * name) -{ - for(size_t i = 0; NULL != commands[i].name; i++) - { - if (0 == strcmp(name, commands[i].name)) - { - return &commands[i]; - } - } - - return &invalid_command; -} - -static void args_cleanup(struct args * args) -{ - free(args->file); - free(args->command); - free(args->username); - free(args->password); - free(args->pepper); -} - -static void openssl_cleanup(void) -{ - FIPS_mode_set(0); - ENGINE_cleanup(); - CONF_modules_unload(1); - EVP_cleanup(); - CRYPTO_cleanup_all_ex_data(); - ERR_free_strings(); -} - -int main(int argc, char * argv[]) -{ - OPENSSL_init(); - OPENSSL_add_all_algorithms_conf(); - - struct args args; - args_init(&args); - int result = parse_args(&args, argc, argv); - if ((EXIT_SUCCESS == result) && (!args.show_help)) - { - struct command const * command = get_command(args.command); - result = command->invoke(&args); - } - - if (args.show_help) - { - print_usage(); - } - - args_cleanup(&args); - openssl_cleanup(); - return result; -} \ No newline at end of file diff --git a/example/provider/main.c b/example/provider/main.c deleted file mode 100644 index 18fe42e..0000000 --- a/example/provider/main.c +++ /dev/null @@ -1,385 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "webfuse_provider.h" - -#define SERVICE_TIMEOUT (1 * 1000) - -struct config -{ - char * url; - struct wfp_client_config * client_config; - bool show_help; -}; - -enum fs_entry_type -{ - FS_FILE, - FS_DIR -}; - -struct fs_entry -{ - ino_t parent; - ino_t inode; - char const * name; - int mode; - enum fs_entry_type type; - size_t content_length; - char const * content; -}; - -struct fs -{ - struct fs_entry const * entries; -}; - -static void show_help() -{ - printf( - "webfuse-provider, Copyright (c) 2019, webfuse authors \n" - "Example for websocket file system provider\n" - "\n" - "Usage: webfuse-provider -u [-k ] [-c ]\n" - "\n" - "Options:\n" - "\t-u, --url URL of webfuse server (required)\n" - "\t-k, --key_path Path to private key of provider (default: not set, TLS disabled)\n" - "\t-c, --cert_path Path to certificate of provider (defautl: not set, TLS disabled)\n" - "\t-h, --help prints this message\n" - "\n" - "Example:\n" - "\twebfuse-provider -u ws://localhost:8080/\n" - "\n" - ); -} - -static int parse_arguments( - int argc, - char* argv[], - struct config * config) -{ - static struct option const options[] = - { - {"url", required_argument, NULL, 'u'}, - {"key_path", required_argument, NULL, 'k'}, - {"cert_path", required_argument, NULL, 'c'}, - {"help", no_argument, NULL, 'h'}, - {NULL, 0, NULL, 0} - }; - - int result = EXIT_SUCCESS; - bool finished = false; - while (!finished) - { - int option_index = 0; - int const c = getopt_long(argc, argv, "u:k:c:h", options, &option_index); - - switch (c) - { - case -1: - finished = true; - break; - case 'h': - config->show_help = true; - finished = true; - break; - case 'u': - free(config->url); - config->url = strdup(optarg); - break; - case 'k': - wfp_client_config_set_keypath(config->client_config, optarg); - break; - case 'c': - wfp_client_config_set_certpath(config->client_config, optarg); - break; - default: - fprintf(stderr, "error: unknown argument\n"); - finished = true; - result = EXIT_FAILURE; - break; - } - - if (NULL == config->url) - { - fprintf(stderr, "error: missing required argument \"-u\"\n"); - result = EXIT_FAILURE; - } - - if (result != EXIT_SUCCESS) - { - config->show_help = true; - } - } - - return result; -} - -static struct fs_entry const * fs_getentry( - struct fs * fs, - ino_t inode) -{ - for (size_t i = 0; 0 != fs->entries[i].inode; i++) - { - struct fs_entry const * entry = &fs->entries[i]; - if (inode == entry->inode) - { - return entry; - } - } - - return NULL; -} - -static struct fs_entry const * fs_getentry_byname( - struct fs * fs, - ino_t parent, - char const * name) -{ - for( size_t i = 0; 0 != fs->entries[i].inode; i++) - { - struct fs_entry const * entry = &fs->entries[i]; - if ((parent == entry->parent) && (0 == strcmp(name, entry->name))) - { - return entry; - } - } - - return NULL; -} - -static void fs_stat( - struct fs_entry const * entry, - struct stat * stat) -{ - memset(stat, 0, sizeof(struct stat)); - - stat->st_ino = entry->inode; - stat->st_mode = entry->mode; - - if (FS_DIR == entry->type) - { - stat->st_mode |= S_IFDIR; - } - - if (FS_FILE == entry->type) - { - stat->st_mode |= S_IFREG; - stat->st_size = entry->content_length; - } -} - -static void fs_lookup( - struct wfp_request * request, - ino_t parent, - char const * name, - void * user_data) -{ - struct fs * fs = (struct fs*) user_data; - struct fs_entry const * entry = fs_getentry_byname(fs, parent, name); - if (NULL != entry) - { - struct stat stat; - fs_stat(entry, &stat); - - wfp_respond_lookup(request, &stat); - } - else - { - wfp_respond_error(request, WF_BAD_NOENTRY); - } -} - - -static void fs_getattr( - struct wfp_request * request, - ino_t inode, - void * user_data) -{ - struct fs * fs = (struct fs*) user_data; - struct fs_entry const * entry = fs_getentry(fs, inode); - - if (NULL != entry) - { - struct stat stat; - fs_stat(entry, &stat); - - wfp_respond_getattr(request, &stat); - } - else - { - wfp_respond_error(request, WF_BAD_NOENTRY); - } -} - -static void fs_readdir( - struct wfp_request * request, - ino_t directory, - void * user_data) -{ - struct fs * fs = (struct fs*) user_data; - - struct fs_entry const * dir = fs_getentry(fs, directory); - if ((NULL != dir) && (FS_DIR == dir->type)) - { - struct wfp_dirbuffer * buffer = wfp_dirbuffer_create(); - wfp_dirbuffer_add(buffer, ".", dir->inode); - wfp_dirbuffer_add(buffer, "..", dir->inode); - - for(size_t i = 0; 0 != fs->entries[i].inode; i++) - { - struct fs_entry const * entry = &fs->entries[i]; - if (directory == entry->parent) - { - wfp_dirbuffer_add(buffer, entry->name, entry->inode); - } - } - - wfp_respond_readdir(request, buffer); - wfp_dirbuffer_dispose(buffer); - } - else - { - wfp_respond_error(request, WF_BAD_NOENTRY); - } -} - -static void fs_open( - struct wfp_request * request, - ino_t inode, - int flags, - void * user_data) -{ - struct fs * fs = (struct fs*) user_data; - - struct fs_entry const * entry = fs_getentry(fs, inode); - if ((NULL != entry) && (FS_FILE == entry->type)) - { - if (O_RDONLY == (flags & O_ACCMODE)) - { - wfp_respond_open(request, 0U); - } - else - { - wfp_respond_error(request, WF_BAD_ACCESS_DENIED); - } - } - else - { - wfp_respond_error(request, WF_BAD_NOENTRY); - } -} - -static size_t min(size_t const a, size_t const b) -{ - return (a < b) ? a : b; -} - -static void fs_read( - struct wfp_request * request, - ino_t inode, - uint32_t handle, - size_t offset, - size_t length, - void * user_data) -{ - (void) handle; - - struct fs * fs = (struct fs*) user_data; - struct fs_entry const * entry = fs_getentry(fs, inode); - if ((NULL != entry) && (FS_FILE == entry->type)) - { - if (entry->content_length > offset) - { - size_t const remaining = entry->content_length - offset; - size_t const count = min(remaining, length); - - wfp_respond_read(request, &entry->content[offset], count); - } - else - { - wfp_respond_error(request, WF_BAD); - } - } - else - { - wfp_respond_error(request, WF_BAD_NOENTRY); - } -} - -static volatile bool shutdown_requested = false; - -static void on_interrupt(int signal_id) -{ - (void) signal_id; - shutdown_requested = true; -} - -int main(int argc, char* argv[]) -{ - struct config config; - config.url = NULL; - config.show_help = false; - config.client_config = wfp_client_config_create(); - int result = parse_arguments(argc, argv, &config); - - if (EXIT_SUCCESS == result) - { - static struct fs_entry const entries[]= - { - {.parent = 0, .inode = 1, .name = "", .mode = 0555, .type = FS_DIR}, - { - .parent = 1, - .inode = 2, - .name = "hello.txt", - .mode = 0444, - .type = FS_FILE, - .content="hello, world!", - .content_length = 13, - }, - {.parent = 0, .inode = 0, .name = NULL} - }; - - struct fs fs = - { - .entries = entries - }; - - signal(SIGINT, &on_interrupt); - - wfp_client_config_set_userdata(config.client_config, &fs); - wfp_client_config_set_onlookup(config.client_config, &fs_lookup); - wfp_client_config_set_ongetattr(config.client_config, &fs_getattr); - wfp_client_config_set_onreaddir(config.client_config, &fs_readdir); - wfp_client_config_set_onopen(config.client_config, &fs_open); - wfp_client_config_set_onread(config.client_config, &fs_read); - - struct wfp_client * client = wfp_client_create(config.client_config); - wfp_client_connect(client, config.url); - - while (!shutdown_requested) - { - wfp_client_service(client, SERVICE_TIMEOUT); - } - - wfp_client_dispose(client); - } - - if (config.show_help) - { - show_help(); - } - - free(config.url); - wfp_client_config_dispose(config.client_config); - return result; -} \ No newline at end of file diff --git a/example/provider/static_filesystem.c b/example/provider/static_filesystem.c deleted file mode 100644 index ee06489..0000000 --- a/example/provider/static_filesystem.c +++ /dev/null @@ -1,104 +0,0 @@ -#include -#include -#include -#include -#include - -#include "webfuse_provider.h" - -#define SERVICE_TIMEOUT (1 * 1000) - -struct args -{ - char const * url; - bool show_help; -}; - -static int -parse_args( - struct args * args, - int argc, - char * argv[]) -{ - int result = EXIT_FAILURE; - args->show_help = true; - args->url = NULL; - - if (2 == argc) - { - result = EXIT_SUCCESS; - - char const * url = argv[1]; - if ((0 != strcmp(url, "-h")) && (0 != strcmp(url, "--help"))) - { - args->show_help = false; - args->url = url; - } - } - else - { - fprintf(stderr, "error: missing argument\n"); - } - - return result; -} - -static volatile bool shutdown_requested = false; - -static void on_interrupt(int signal_id) -{ - (void) signal_id; - shutdown_requested = true; -} - -static void print_usage() -{ - printf( - "static-filesystem-provider Copyright (c) 2019, webfuse authors \n" - "Example of webfuse static filesystem provider\n" - "\n" - "Usage: static-filesystem-provider \n" - "\n" - "Arguments:\n" - "\t URL of webfuse server (required)\n" - "\t-h, --help prints this message\n" - "\n" - "Example:\n" - "\tstatic-filesystem-provider ws://localhost:8080/\n" - "\n" - ); -} - -int main(int argc, char* argv[]) -{ - signal(SIGINT, &on_interrupt); - - struct args args; - int result = parse_args(&args, argc, argv); - if (EXIT_SUCCESS == result) - { - struct wfp_client_config * config = wfp_client_config_create(); - - struct wfp_static_filesystem * fs = wfp_static_filesystem_create(config); - wfp_static_filesystem_add_text(fs, "hello.txt", 0444, "Hello, World!"); - - struct wfp_client * client = wfp_client_create(config); - wfp_client_connect(client, args.url); - - while (!shutdown_requested) - { - wfp_client_service(client, SERVICE_TIMEOUT); - } - - wfp_client_dispose(client); - wfp_static_filesystem_dispose(fs); - wfp_client_config_dispose(config); - } - - if (args.show_help) - { - print_usage(); - } - - return result; -} diff --git a/include/webfuse/adapter/api.h b/include/webfuse/adapter/api.h index 5d00900..6ab7d30 100644 --- a/include/webfuse/adapter/api.h +++ b/include/webfuse/adapter/api.h @@ -1,10 +1,26 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file adapter/api.h +/// \brief API define for webfuse adapter. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WF_ADAPTER_API_H #define WF_ADAPTER_API_H +//------------------------------------------------------------------------------ +/// \def WF_API +/// \brief Marks public symbols of libwebfuse_adapter. +//------------------------------------------------------------------------------ #ifndef WF_API #define WF_API #endif +//------------------------------------------------------------------------------ +/// \def WF_EXPORT +/// \brief Marks exported symbols as visible. +/// +/// Set WF_API to WF_EXPORT when building libwebfuse_adapter.so to export +/// public symbols. +//------------------------------------------------------------------------------ #ifndef WF_EXPORT #ifdef __GNUC__ #define WF_EXPORT __attribute__ ((visibility ("default"))) diff --git a/include/webfuse/adapter/authenticate.h b/include/webfuse/adapter/authenticate.h index e9113a6..e11e300 100644 --- a/include/webfuse/adapter/authenticate.h +++ b/include/webfuse/adapter/authenticate.h @@ -1,3 +1,8 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file adapter/authenticate.h +/// \brief Authenticate function. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WF_ADAPTER_AUTHENTICATE_H #define WF_ADAPTER_AUTHENTICATE_H @@ -12,6 +17,19 @@ extern "C" struct wf_credentials; +//------------------------------------------------------------------------------ +/// \brief Authentication function type. +/// +/// Functions of this type are used to authenticate a user by some provided +/// credentials. +/// +/// \param credentials credentials to authenticate the user +/// \param user_data context of the authentication function +/// \return true, if authentication was successful, false otherwise +/// +/// \see wf_server_config_add_authenticator +/// \see wf_server_protocol_add_authenticator +//------------------------------------------------------------------------------ typedef bool wf_authenticate_fn( struct wf_credentials * credentials, void * user_data); diff --git a/include/webfuse/adapter/credentials.h b/include/webfuse/adapter/credentials.h index 82a8094..e3e65fc 100644 --- a/include/webfuse/adapter/credentials.h +++ b/include/webfuse/adapter/credentials.h @@ -1,3 +1,8 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file adapter/credentials.h +/// \brief Credentials used for user authentication. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WF_ADAPTER_CREDENTIALS_H #define WF_ADAPTER_CREDENTIALS_H @@ -8,11 +13,54 @@ extern "C" { #endif +//------------------------------------------------------------------------------ +/// \struct wf_credentials +/// \brief Credentials used for user authentication. +/// +/// Credentials are used during authentication to authenticate a user. +/// In order to support multiple types of credentials, e.g. username + password, +/// certifactes and / or authentication tokens, credentials are modelled as +/// opaque type with some access functions. +/// +/// \see wf_authenticate_fn +/// \see wf_credentials_type +/// \see wf_credentials_get +//------------------------------------------------------------------------------ struct wf_credentials; +//------------------------------------------------------------------------------ +/// \brief Returns the type of the credentials object. +/// +/// The type of the credentials objects defines, which keys are available +/// for a given instance. +/// +/// \note When an authenticate function is called, the credentials type +/// matches the type provided during _add_authenticator function call. +/// Therefore, it is not necessary to check credentials type within +/// the authenticate function. +/// +/// \note Within webfuse protocol documentation, only one type of credentials +/// is described byte now: username +/// +/// \param credentials Pointer to credentials object +/// \return type of the credentials object +/// +/// \see wf_server_config_add_authenticator +/// \see wf_server_protocol_add_authenticator +/// \see wf_authenticate_fn +//------------------------------------------------------------------------------ extern WF_API char const * wf_credentials_type( struct wf_credentials const * credentials); +//------------------------------------------------------------------------------ +/// \brief Return the value of a credentials item identified by the provided +/// key. +/// +/// \param credentials Pointer to credentials object. +/// \param key String to identify the item. +/// \return value of credentials item or null, if there is no item with that +/// key +//------------------------------------------------------------------------------ extern WF_API char const * wf_credentials_get( struct wf_credentials const * credentials, char const * key); diff --git a/include/webfuse/adapter/mountpoint.h b/include/webfuse/adapter/mountpoint.h new file mode 100644 index 0000000..9b77165 --- /dev/null +++ b/include/webfuse/adapter/mountpoint.h @@ -0,0 +1,84 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file adapter/mountpoint.h +/// \brief Mointpoint. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WF_ADAPTER_MOUNTPOINT_H +#define WF_ADAPTER_MOUNTPOINT_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +//------------------------------------------------------------------------------ +/// \struct wf_mountpoint +/// \brief Mointpoint. +//------------------------------------------------------------------------------ +struct wf_mountpoint; + +//------------------------------------------------------------------------------ +/// \brief Disposes the user defined context of a mountpoint. +/// +/// \param user_data user defined context of the mointpoint. +/// +/// \see wf_mountpoint_set_userdata +//------------------------------------------------------------------------------ +typedef void +wf_mountpoint_userdata_dispose_fn( + void * user_data); + +//------------------------------------------------------------------------------ +/// \brief Creates a mountpoint. +/// +/// \param path local path of the mounpoint +/// \return Newly created mountpoint. +//------------------------------------------------------------------------------ +extern WF_API struct wf_mountpoint * +wf_mountpoint_create( + char const * path); + +//------------------------------------------------------------------------------ +/// \brief Disposes a mountpoint. +/// +/// \param mountpoint pointer to the mountpoint +//------------------------------------------------------------------------------ +extern WF_API void +wf_mountpoint_dispose( + struct wf_mountpoint * mountpoint); + +//------------------------------------------------------------------------------ +/// \brief Returns the local path of the mountpoint. +/// +/// \param mountpoint pointer to the mountpoint +/// \return local path of the mountpoint +//------------------------------------------------------------------------------ +extern WF_API char const * +wf_mountpoint_get_path( + struct wf_mountpoint const * mountpoint); + +//------------------------------------------------------------------------------ +/// \brief Sets user data of the mointpoint. +/// +/// \note This function is intended for custom mountpoint factories to +/// annotate mountpoints with a user specified context. +/// +/// \param mounpoint pointer to the mountpoint +/// \param user_data user data +/// \param dispose pointer to dipose function of user data or NULL, +/// if there is no need to dispose user data +//------------------------------------------------------------------------------ +extern WF_API void +wf_mountpoint_set_userdata( + struct wf_mountpoint * mointpoint, + void * user_data, + wf_mountpoint_userdata_dispose_fn * dispose); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/include/webfuse/adapter/mountpoint_factory.h b/include/webfuse/adapter/mountpoint_factory.h new file mode 100644 index 0000000..7a10bb1 --- /dev/null +++ b/include/webfuse/adapter/mountpoint_factory.h @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file adapter/mountpoint_factory.h +/// \brief Defines a factory function to create mointpoints. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WF_ADAPTER_MOUNTPOINT_FACTORY_H +#define WF_ADAPTER_MOUNTPOINT_FACTORY_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_mountpoint; + +//------------------------------------------------------------------------------ +/// \brief Factory function to create mountpoints. +/// +/// \param filesystem name the filesystem +/// \param user_data context of the factory +/// \return newly created mountpoint or NULL, on error +/// +/// \see wf_server_config_set_mountpoint_factory +/// \see wf_server_protocol_create +//------------------------------------------------------------------------------ +typedef struct wf_mountpoint * +wf_create_mountpoint_fn( + char const * filesystem, + void * user_data); + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/include/webfuse/adapter/server.h b/include/webfuse/adapter/server.h index 8a6a3a1..f46f3ef 100644 --- a/include/webfuse/adapter/server.h +++ b/include/webfuse/adapter/server.h @@ -1,3 +1,8 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file adapter/server.h +/// \brief Adapter server. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WF_ADAPTER_SERVER_H #define WF_ADAPTER_SERVER_H @@ -8,18 +13,57 @@ extern "C" { #endif +//------------------------------------------------------------------------------ +/// \struct wf_server +/// \brief Webfuse adapter server object. +//------------------------------------------------------------------------------ struct wf_server; + struct wf_server_config; +//------------------------------------------------------------------------------ +/// \brief Creates a new server. +/// +/// \param config pointer to server configuration. +/// \return newly created server or NULL in case of an error +//------------------------------------------------------------------------------ extern WF_API struct wf_server * wf_server_create( struct wf_server_config * config); +//------------------------------------------------------------------------------ +/// \brief Disposes a server. +/// +/// \note server configuration is not managed by server. +/// +/// \param server pointer to server +//------------------------------------------------------------------------------ extern WF_API void wf_server_dispose( struct wf_server * server); +//------------------------------------------------------------------------------ +/// \brief Triggers the server. +/// +/// This function must be invoked in a loop while the server is running. It +/// makes the server wait for the next event and processes it. +/// +/// \param server pointer to server +/// +/// \see wf_server_interrupt +//------------------------------------------------------------------------------ extern WF_API void wf_server_service( - struct wf_server * server, - int timeout_ms); + struct wf_server * server); + +//------------------------------------------------------------------------------ +/// \brief Interrupts wf_server_service +/// +/// This function can be used from another thread. +/// +/// \param server pointer to server +/// +/// \see wf_server_service +//------------------------------------------------------------------------------ +extern WF_API void wf_server_interrupt( + struct wf_server * server); #ifdef __cplusplus } diff --git a/include/webfuse/adapter/server_config.h b/include/webfuse/adapter/server_config.h index cc1b6d3..e140c4d 100644 --- a/include/webfuse/adapter/server_config.h +++ b/include/webfuse/adapter/server_config.h @@ -1,52 +1,150 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file adapter/server_config.h +/// \brief Server configuration. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WF_ADAPTER_SERVER_CONFIG_H #define WF_ADAPTER_SERVER_CONFIG_H #include "webfuse/adapter/api.h" #include "webfuse/adapter/authenticate.h" +#include "webfuse/adapter/mountpoint_factory.h" #ifdef __cplusplus extern "C" { #endif +//------------------------------------------------------------------------------ +/// \struct wf_server_config +/// \brief Holds configuration of webfuse adapter server. +//------------------------------------------------------------------------------ struct wf_server_config; +//------------------------------------------------------------------------------ +/// \brief Creates a new server configuration. +/// +/// \return newly created server configuration +//------------------------------------------------------------------------------ extern WF_API struct wf_server_config * wf_server_config_create(void); +//------------------------------------------------------------------------------ +/// \brief Disposes a server configuration. +/// +/// \note Contexts of mounpoint factory and added authenticators are not +/// disposed by default. +/// +/// \param config pointer of configuration object +/// +/// \see wf_server_config_set_mountpoint_factory +/// \see wf_server_config_add_authenticator +//------------------------------------------------------------------------------ extern WF_API void wf_server_config_dispose( struct wf_server_config * config); - -extern WF_API void wf_server_config_set_mountpoint( +//------------------------------------------------------------------------------ +/// \brief Sets the mountpoint factory of the configuration. +/// +/// The mountpoint factory is called when a authenticated user adds a +/// filesystem. +/// +/// \note The user is responsible to manage the lifetime of the mountpoint +/// factory. +/// +/// \note A valid configuration needs either a mountpoint or a mounpoint +/// factory. +/// +/// \param config pointer of configuration object +/// \param create_mountpoint factory function to create a mountpoint +/// \param user_data context of mountpoint factory +//------------------------------------------------------------------------------ +extern WF_API void wf_server_config_set_mountpoint_factory( struct wf_server_config * config, - char const * mount_point); + wf_create_mountpoint_fn * create_mountpoint, + void * user_data); +//------------------------------------------------------------------------------ +/// \brief Sets the path of HTTP servers root context. +/// +/// Webfuse adapter server is capable of serving static HTTP context. This +/// function is used to specify the path of HTTP servers root context. +/// If not specified, no HTTP content is served. +/// +/// \param config pointer of configuration object +/// \param document_root path to static HTTP content +//------------------------------------------------------------------------------ extern WF_API void wf_server_config_set_documentroot( struct wf_server_config * config, char const * document_root); +//------------------------------------------------------------------------------ +/// \brief Sets the path to the servers private key. +/// +/// \note To enable TLS, private key and server certificate must be set. +/// Otherwise, only plain HTTP is supported. +/// +/// \param config pointer of configuration object +/// \param key_path path to servers private key (pem file) +//------------------------------------------------------------------------------ extern WF_API void wf_server_config_set_keypath( struct wf_server_config * config, char const * key_path); +//------------------------------------------------------------------------------ +/// \brief Sets path to servers certificate. +/// +/// \note To enable TLS, private key and server certificate must be set. +/// Otherwise, only plain HTTP is supported. +/// +/// \param config pointer of configuration object +/// \param cert_path path to servers certificate (pem file) +//------------------------------------------------------------------------------ extern WF_API void wf_server_config_set_certpath( struct wf_server_config * config, char const * cert_path); +//------------------------------------------------------------------------------ +/// \brief Sets the virtual hostname of the websockets server. +/// +/// \param config pointer of configuration object +/// \param vhost_name virtual hostname of the websockets server +//------------------------------------------------------------------------------ extern WF_API void wf_server_config_set_vhostname( struct wf_server_config * config, char const * vhost_name); +//------------------------------------------------------------------------------ +/// \brief Sets the port number of the websockets server. +/// +/// \param config pointer of configuration object +/// \param port port number of the websockets server +//------------------------------------------------------------------------------ extern WF_API void wf_server_config_set_port( struct wf_server_config * config, int port); +//------------------------------------------------------------------------------ +/// \brief Adds an authenticator. +/// +/// Authenticators are used to authenticate users by some provided credentials. +/// Multiple providers can be specified to support different types of +/// credentials. +/// +/// \note Adding multiple providers for the same credentials type results +/// in undefined behavior. +/// +/// \note The user is responsible to manage the lifetime of user data. +/// +/// \param config pointer to configuration object +/// \param type type of the credentials the authenticator supports +/// \param authenticate function called to authenticate a user +/// \param user_data context of authenticate function +//------------------------------------------------------------------------------ extern WF_API void wf_server_config_add_authenticator( struct wf_server_config * config, char const * type, wf_authenticate_fn * authenticate, - void * user_data -); + void * user_data); #ifdef __cplusplus } diff --git a/include/webfuse/adapter/server_protocol.h b/include/webfuse/adapter/server_protocol.h index d77cbc3..68b1dbe 100644 --- a/include/webfuse/adapter/server_protocol.h +++ b/include/webfuse/adapter/server_protocol.h @@ -1,27 +1,89 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file adapter/server_protocol.h +/// \brief Provides low level access to libwebsockets protocol. +/// +/// By default, libwebfuse encapsulates libwebsockets protocol by \ref +/// wf_server. But sometimes it might come in handy to have access to +/// libwebsockets protocol. This allows to integrate libwebfuse in existing +/// libwebsockets applications. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WF_ADAPTER_SERVER_PROTOCOL_H #define WF_ADAPTER_SERVER_PROTOCOL_H #include #include +#include #ifdef __cplusplus extern "C" { #endif +//------------------------------------------------------------------------------ +/// \struct wf_server_protocol +/// \brief Opaque webfuse server protocol. +//------------------------------------------------------------------------------ struct wf_server_protocol; + +//------------------------------------------------------------------------------ +/// \struct lws_protocols +/// \brief Forward declaration of libwebsockets protocols structure. +//------------------------------------------------------------------------------ struct lws_protocols; +//------------------------------------------------------------------------------ +/// \brief Creates a new protocol by a mountpoint factory. +/// +/// \note This function might be renamed in future releases. +/// +/// \note The user is responsible to manage the lifetime of mountpoint factory. +/// +/// \param create_mountpoint factory function to create mountpoints +/// \param create_mountpoint_context context of mountpoint factory +//------------------------------------------------------------------------------ extern WF_API struct wf_server_protocol * wf_server_protocol_create( - char * mount_point); + wf_create_mountpoint_fn * create_mountpoint, + void * create_mountpoint_context); +//------------------------------------------------------------------------------ +/// \brief Disposes a protocol. +/// +/// \note Contexts of mountpoint factory and added authenticators are not +/// managed by dispose. +/// +/// \param protocol pointer to protocol +//------------------------------------------------------------------------------ extern WF_API void wf_server_protocol_dispose( struct wf_server_protocol * protocol); +//------------------------------------------------------------------------------ +/// \brief Intializes a libwebsockets protocol structure. +/// +/// \param protocol pointer to protocol +/// \param lws_protocols pointer to libwebsockets protocol structure +//------------------------------------------------------------------------------ extern WF_API void wf_server_protocol_init_lws( struct wf_server_protocol * protocol, struct lws_protocols * lws_protocol); +//------------------------------------------------------------------------------ +/// \brief Adds an authenticator. +/// +/// Authenticators are used to authenticate users by some provided credentials. +/// Multiple providers can be specified to support different types of +/// credentials. +/// +/// \note Adding multiple providers for the same credentials type results +/// in undefined behavior. +/// +/// \note The user is responsible to manage the lifetime of user data. +/// +/// \param protocol pointer to protocol +/// \param type type of the credentials the authenticator supports +/// \param authenticate function called to authenticate a user +/// \param user_data context of authenticate function +//------------------------------------------------------------------------------ extern WF_API void wf_server_protocol_add_authenticator( struct wf_server_protocol * protocol, char const * type, diff --git a/include/webfuse/core/protocol_names.h b/include/webfuse/core/protocol_names.h new file mode 100644 index 0000000..4de8c4a --- /dev/null +++ b/include/webfuse/core/protocol_names.h @@ -0,0 +1,21 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file protocol_names.h +/// \brief Names of websocket protocol. +//////////////////////////////////////////////////////////////////////////////// +#ifndef WF_PROTOCOL_NAMES_H +#define WF_PROTOCOL_NAMES_H + + +//------------------------------------------------------------------------------ +/// \def WF_PROTOCOL_NAME_ADAPTER_SERVER +/// \brief Name of the websocket protocol an adapter server is running. +//------------------------------------------------------------------------------ +#define WF_PROTOCOL_NAME_ADAPTER_SERVER ("webfuse-adapter-server") + +//------------------------------------------------------------------------------ +/// \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") + +#endif diff --git a/include/webfuse/core/status.h b/include/webfuse/core/status.h index 7b3c95f..49e53e5 100644 --- a/include/webfuse/core/status.h +++ b/include/webfuse/core/status.h @@ -1,17 +1,23 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file status.h +/// \brief Generic status code. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WF_STATUS_H #define WF_STATUS_H -#define WF_GOOD 0 -#define WF_BAD 1 +#define WF_GOOD 0 ///< Positive status code. +#define WF_BAD 1 ///< Generic negative status code. -#define WF_BAD_NOTIMPLEMENTED 2 -#define WF_BAD_TIMEOUT 3 -#define WF_BAD_BUSY 4 -#define WF_BAD_FORMAT 5 +#define WF_BAD_NOTIMPLEMENTED 2 ///< The called function is not implemented (yet). +#define WF_BAD_TIMEOUT 3 ///< A timeout occured. +#define WF_BAD_BUSY 4 ///< Resource is busy, try again later. +#define WF_BAD_FORMAT 5 ///< Invalid format. -#define WF_BAD_NOENTRY 101 -#define WF_BAD_ACCESS_DENIED 102 +#define WF_BAD_NOENTRY 101 ///< Entry not found. +#define WF_BAD_ACCESS_DENIED 102 ///< Access is denied. +/// Status code. typedef int wf_status; #endif diff --git a/include/webfuse/provider/api.h b/include/webfuse/provider/api.h index 5b3c572..963e038 100644 --- a/include/webfuse/provider/api.h +++ b/include/webfuse/provider/api.h @@ -1,10 +1,26 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/api.h +/// \brief API define for webfuse provider. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WFP_PROVIDER_API_H #define WFP_PROVIDER_API_H +//------------------------------------------------------------------------------ +/// \def WFP_API +/// \brief Marks public symbols of libwebfuse_provider. +//------------------------------------------------------------------------------ #ifndef WFP_API #define WFP_API #endif +//------------------------------------------------------------------------------ +/// \def WFP_EXPORT +/// \brief Marks exported symbols as visible. +/// +/// Set WFP_API to WFP_EXPORT when building libwebfuse_provider.so to export +/// public symbols. +//------------------------------------------------------------------------------ #ifndef WFP_EXPORT #ifdef __GNUC__ #define WFP_EXPORT __attribute__ ((visibility ("default"))) diff --git a/include/webfuse/provider/client.h b/include/webfuse/provider/client.h index 142bfae..1848717 100644 --- a/include/webfuse/provider/client.h +++ b/include/webfuse/provider/client.h @@ -1,3 +1,8 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/client.h +/// \brief Webfuse provider client. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WF_PROVIDER_CLIENT_H #define WF_PROVIDER_CLIENT_H @@ -8,25 +13,89 @@ extern "C" { #endif +//------------------------------------------------------------------------------ +/// \struct wfp_client +/// \brief Webfuse provider client. +//------------------------------------------------------------------------------ struct wfp_client; + struct wfp_client_config; +//------------------------------------------------------------------------------ +/// \brief Creates a webfuse provider client. +/// +/// \note Client configuration is not managed by the client. +/// +/// \param config pointer to client configuration. +/// \return newly created client or NULL in case of an error. +//------------------------------------------------------------------------------ extern WFP_API struct wfp_client * wfp_client_create( struct wfp_client_config * config); +//------------------------------------------------------------------------------ +/// \brief Connects the client to a remote webfuse adapter server. +/// +/// \note This call starts to establish a connection. A callback is invoked, +/// when the connection is estanlished. +/// +/// \param client pointer to client +/// \param url URL of remote webfuse adapter server +/// +/// \see wfp_connected_fn +/// \see wfp_client_config_set_onconnected +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_connect( struct wfp_client * client, char const * url); +//------------------------------------------------------------------------------ +/// \brief Disconnects a connected client. +/// +/// \note This call starts to disconnect the connection. A callback is invoked +/// when conntection is disconnected. +/// +/// \param client pointer to client +/// +/// \see wfp_disconnected_fn +/// \see wfp_client_config_set_ondisconnected +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_disconnect( struct wfp_client * client); +//------------------------------------------------------------------------------ +/// \brief Disposes a client. +/// +/// \note Client configuration is not managed by client. +/// +/// \param client pointer to client +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_dispose( struct wfp_client * client); +//------------------------------------------------------------------------------ +/// \brief Triggers the client. +/// +/// This function must be invoked in a loop while the client is running. It +/// makes the server wait for the next event and processes it. +/// +/// \param client pointer to client +/// +/// \see wfp_client_interrupt +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_service( - struct wfp_client * client, - int timeout_ms); + struct wfp_client * client); + +//------------------------------------------------------------------------------ +/// \brief interrupt wfp_client_service +/// +/// This function can be called from another thread. +/// +/// \param client pointer to client +/// +/// \see wfp_client_service +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_interrupt( + struct wfp_client * client); #ifdef __cplusplus } diff --git a/include/webfuse/provider/client_config.h b/include/webfuse/provider/client_config.h index 4a9cebc..26654bc 100644 --- a/include/webfuse/provider/client_config.h +++ b/include/webfuse/provider/client_config.h @@ -1,3 +1,8 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/client_config.h +/// \brief Client configuration of webfuse provider. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WF_PROVIDER_CLIENT_CONFIG_H #define WF_PROVIDER_CLIENT_CONFIG_H @@ -9,77 +14,224 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { #endif +//------------------------------------------------------------------------------ +/// \struct wfp_client_config +/// \brief Provider client configuration object. +/// +/// Holds configuration of webfuse provider client. +//------------------------------------------------------------------------------ struct wfp_client_config; +//------------------------------------------------------------------------------ +/// \brief Callback to signal when the client's connection is established. +/// +/// \param user_data user defined context +//------------------------------------------------------------------------------ typedef void wfp_connected_fn( void * user_data); +//------------------------------------------------------------------------------ +/// \brief Callback to signal when a client's connection is disconnected. +/// +/// \param user_data user defined context +//------------------------------------------------------------------------------ typedef void wfp_disconnected_fn( void * user_data); -typedef void wfp_ontimer_fn( - void * user_data); - +//------------------------------------------------------------------------------ +/// \brief Creates a new client configuration. +/// +/// \return newly created client configuration +//------------------------------------------------------------------------------ extern WFP_API struct wfp_client_config * wfp_client_config_create(void); +//------------------------------------------------------------------------------ +/// \brief Disposes a client configuration. +/// +/// \note The user defined context is not managed by the client configuration. +/// +/// \param config pointer to client configuration +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_config_dispose( struct wfp_client_config * config); +//------------------------------------------------------------------------------ +/// \brief Sets a user defined context. +/// +/// \note The user is responsible to manage the lifetime of user data. +/// +/// \param config pointer to client configuration +/// \param user_data user defined context +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_config_set_userdata( struct wfp_client_config * config, void * user_data); +//------------------------------------------------------------------------------ +/// \brief Sets the path to clients private key. +/// +/// \note To enable TLS both, private key and certificate, must be specified. +/// Otherwise, TLS is not used. +/// +/// \param config pointer to client configuration +/// \param key_path path of clients private key (pem file) +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_config_set_keypath( struct wfp_client_config * config, char const * key_path); +//------------------------------------------------------------------------------ +/// \brief Sets the path of clients certificate. +/// +/// \note To enable TLS both, private key and certificate, must be specified. +/// Otherwise, TLS is not used. +/// +/// \param config pointer to client configuration +/// \param cert_path path of the clients certificate (pem file) +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_config_set_certpath( struct wfp_client_config * config, char const * cert_path); +//------------------------------------------------------------------------------ +/// \brief Sets the path of ca file to verify servers. +/// +/// \note To enable TLS both, private key and certificate, must be specified. +/// Otherwise, TLS is not used. +/// +/// \param config pointer to client configuration +/// \param ca_filepath path of the ca file (pem file) +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_config_set_ca_filepath( + struct wfp_client_config * config, + char const * ca_filepath); + +//------------------------------------------------------------------------------ +/// \brief Sets the onconnected handler. +/// +/// The handler is invoked, when the client's conntection is established. +/// +/// \param config pointer to client configuration +/// \param handler pointer to handler +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_config_set_onconnected( struct wfp_client_config * config, wfp_connected_fn * handler); +//------------------------------------------------------------------------------ +/// \brief Sets ondisconnected handler +/// +/// The handler is invoked, when the client's conntection is lost. +/// +/// \param config pointer to client configuration +/// \param handler pointer to handler +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_config_set_ondisconnected( struct wfp_client_config * config, wfp_disconnected_fn * handler); -extern WFP_API void wfp_client_config_set_ontimer( - struct wfp_client_config * config, - wfp_ontimer_fn * handler); - - +//------------------------------------------------------------------------------ +/// \brief Sets onlookup handler. +/// +/// The handler is invoked, when the identifier of a file is requested. +/// +/// \param config pointer to client configuration +/// \param handler pointer to handler +/// +/// \see wfp_lookup_fn +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_config_set_onlookup( struct wfp_client_config * config, wfp_lookup_fn * handler); +//------------------------------------------------------------------------------ +/// \brief Sets ongetattr handler. +/// +/// The handler is invoked, when attributes of a file are requested. +/// +/// \param config pointer to client configuration +/// \param handler pointer to handler +/// +/// \see wfp_getattr_fn +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_config_set_ongetattr( struct wfp_client_config * config, wfp_getattr_fn * handler); +//------------------------------------------------------------------------------ +/// \brief Sets onreaddir handler. +/// +/// The handler is invoked, when the contents of directory are requested- +/// +/// \param config pointer to client configuration +/// \param handler pointer to handler +/// +/// \see wfp_readdir_fn +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_config_set_onreaddir( struct wfp_client_config * config, wfp_readdir_fn * handler); +//------------------------------------------------------------------------------ +/// \brief Sets onopen handler. +/// +/// The handler is invoked, whe a file should be opened. +/// +/// \param config pointer to client configuration +/// \param handler pointer to handler +/// +/// \see wfp_open_fn +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_config_set_onopen( struct wfp_client_config * config, wfp_open_fn * handler); +//------------------------------------------------------------------------------ +/// \brief Sets onclose handler. +/// +/// The handler is invoked, when a file is closed. +/// +/// \param config pointer to client configuration +/// \param handler pointer to handler +/// +/// \see wfp_close_fn +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_config_set_onclose( struct wfp_client_config * config, wfp_close_fn * handler); +//------------------------------------------------------------------------------ +/// \brief Sets onread handler. +/// +/// The handler is invoked, when a files content is requested. +/// +/// \param config pointer to client configuration +/// \param handler pointer to handler +/// +/// \see wfp_read_fn +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_config_set_onread( struct wfp_client_config * config, wfp_read_fn * handler); +//------------------------------------------------------------------------------ +/// \brief Enabled authentication. +/// +/// \param config pointer to client configuration +/// \param get_credentials pointer to function providing credentials when +// needed. +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_config_enable_authentication( + struct wfp_client_config * config, + wfp_get_credentials_fn * get_credentials); + #ifdef __cplusplus } #endif diff --git a/include/webfuse/provider/client_protocol.h b/include/webfuse/provider/client_protocol.h index 8bda896..8108030 100644 --- a/include/webfuse/provider/client_protocol.h +++ b/include/webfuse/provider/client_protocol.h @@ -1,3 +1,13 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/client_protocol.h +/// \brief Provides low level access to libwebsockets protocol. +/// +/// By default, libwebfuse encapsulates libwebsockets protocol by \ref +/// wfp_client. But sometimes it might come in handy to have access to +/// libwebsockets protocol. This allows to integrate libwebfuse in existing +/// libwebsockets applications. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WF_PROVIDER_CLIENT_PROTOCOL_H #define WF_PROVIDER_CLIENT_PROTOCOL_H @@ -8,21 +18,96 @@ extern "C" { #endif +//------------------------------------------------------------------------------ +/// \struct wfp_client_protocol +/// \brief Opaque webfuse client protocol.. +//------------------------------------------------------------------------------ struct wfp_client_protocol; -struct wfp_provider; + +//------------------------------------------------------------------------------ +/// \struct lws_protocols +/// \brief Forward declaration of libwebsockets protocols structure. +//------------------------------------------------------------------------------ struct lws_protocols; -extern WFP_API struct wfp_client_protocol * wfp_client_protocol_create( - struct wfp_provider const * provider, - void * user_data); +//------------------------------------------------------------------------------ +/// \struct lws_context +/// \brief Forward declaration of libwebsockets context structure. +//------------------------------------------------------------------------------ +struct lws_context; +//------------------------------------------------------------------------------ +/// \struct wfp_client_config +/// \copydoc wfp_client_config +//------------------------------------------------------------------------------ +struct wfp_client_config; + +//------------------------------------------------------------------------------ +/// \brief Creates a new webfuse provider client protocol. +/// +/// \note The user is responsible to manage lifetime of \arg config. +/// +/// \note TLS configuration is ignored, since TLS is managed by libwebsockets. +/// +/// \param config pointer to client config +/// \return newly created protocol +//------------------------------------------------------------------------------ +extern WFP_API struct wfp_client_protocol * wfp_client_protocol_create( + struct wfp_client_config const * config); + +//------------------------------------------------------------------------------ +/// \brief Disposes a protocol. +/// +/// \note The user defined context is not managed by the protocol. +/// +/// \param protocol pointer to protocol. +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_protocol_dispose( struct wfp_client_protocol * protocol); +//------------------------------------------------------------------------------ +/// \brief Initialized libwebsockets protocol structure. +/// +/// \param protocol pointer to protocol +/// \param lws_protocol pointer to libwebsockets protocol structure. +//------------------------------------------------------------------------------ extern WFP_API void wfp_client_protocol_init_lws( struct wfp_client_protocol * protocol, struct lws_protocols * lws_protocol); + +//------------------------------------------------------------------------------ +/// \brief Connects the protocol to a remote webfuse adapter server. +/// +/// \note This call starts to establish a connection. A callback is invoked, +/// when the connection is estanlished. +/// +/// \param protocol pointer to protocol +/// \param context lws context +/// \param url URL of remote webfuse adapter server +/// +/// \see wfp_connected_fn +/// \see wfp_client_config_set_onconnected +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_protocol_connect( + struct wfp_client_protocol * protocol, + struct lws_context * context, + char const * url); + +//------------------------------------------------------------------------------ +/// \brief Disconnects the protocol from a remote webfuse adapter server. +/// +/// \note This call starts to disconnect. A callback is invoked, +/// when the connection is estanlished. +/// +/// \param protocol pointer to protocol +/// +/// \see wfp_connected_fn +/// \see wfp_client_config_set_ondisconnected +//------------------------------------------------------------------------------ +extern WFP_API void wfp_client_protocol_disconnect( + struct wfp_client_protocol * protocol); + #ifdef __cplusplus } #endif diff --git a/include/webfuse/provider/credentials.h b/include/webfuse/provider/credentials.h new file mode 100644 index 0000000..a6b0806 --- /dev/null +++ b/include/webfuse/provider/credentials.h @@ -0,0 +1,30 @@ +#ifndef WF_PROVIDER_CREDENTIALS_H +#define WF_PROVIDER_CREDENTIALS_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_credentials; + +typedef void wfp_get_credentials_fn( + struct wfp_credentials * credentials, + void * user_data); + +extern WFP_API void wfp_credentials_set_type( + struct wfp_credentials * credentials, + char const * type); + +extern WFP_API void wfp_credentials_add( + struct wfp_credentials * credentials, + char const * key, + char const * value); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/webfuse/provider/dirbuffer.h b/include/webfuse/provider/dirbuffer.h index d0770e5..2737ab4 100644 --- a/include/webfuse/provider/dirbuffer.h +++ b/include/webfuse/provider/dirbuffer.h @@ -1,3 +1,8 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/dirbuffer.h +/// \brief Buffer used for directory listing. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WF_PROVIDER_DIRBUFFER_H #define WF_PROVIDER_DIRBUFFER_H @@ -12,13 +17,36 @@ extern "C" { #endif +//------------------------------------------------------------------------------ +/// \struct wfp_dirbuffer +/// \brief Buffer used for directory listing. +/// +/// \see wfp_respond_readdir +//------------------------------------------------------------------------------ struct wfp_dirbuffer; +//------------------------------------------------------------------------------ +/// \brief Creates a new dir buffer. +/// +/// \return newly created dir buffer. +//------------------------------------------------------------------------------ extern WFP_API struct wfp_dirbuffer * wfp_dirbuffer_create(void); +//------------------------------------------------------------------------------ +/// \brief Disposes a dir buffer. +/// +/// \param buffer pointer to dir buffer +//------------------------------------------------------------------------------ extern WFP_API void wfp_dirbuffer_dispose( struct wfp_dirbuffer * buffer); +//------------------------------------------------------------------------------ +/// \brief Adds an entry to dir buffer. +/// +/// \param buffer pointer to dir buffer +/// \param name name of the entry (file or directory) +/// \param inode inode of the entry +//------------------------------------------------------------------------------ extern WFP_API void wfp_dirbuffer_add( struct wfp_dirbuffer * buffer, char const * name, diff --git a/include/webfuse/provider/operation/close.h b/include/webfuse/provider/operation/close.h index bb1eef1..ae17a28 100644 --- a/include/webfuse/provider/operation/close.h +++ b/include/webfuse/provider/operation/close.h @@ -1,3 +1,8 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/operation/close.h +/// \brief Provider's close callback. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WFP_OPERATION_CLOSE_H #define WFP_OPERATION_CLOSE_H @@ -18,6 +23,16 @@ extern "C" { #endif +//------------------------------------------------------------------------------ +/// \brief Callback invoked when a file is invoked. +/// +/// This function does not respond. +/// +/// \param inode inode of file to close +/// \param handle handle of file to close +/// \param flags file close flags +/// \param user_data user defined context +//------------------------------------------------------------------------------ typedef void wfp_close_fn( ino_t inode, uint32_t handle, diff --git a/include/webfuse/provider/operation/error.h b/include/webfuse/provider/operation/error.h index 4e8e1fe..556a630 100644 --- a/include/webfuse/provider/operation/error.h +++ b/include/webfuse/provider/operation/error.h @@ -1,3 +1,8 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/operation/error.h +/// \brief Respond with error code. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WFP_OPERATION_ERROR_H #define WFP_OPERATION_ERROR_H @@ -11,6 +16,15 @@ extern "C" struct wfp_request; +//------------------------------------------------------------------------------ +/// \brief Respond to a request with an error. +/// +/// A client's callback must respond with exactly one responde, either with a +/// valid reponse regarding to the concrete request or with an error response. +/// +/// \param request pointer to request +/// \param status error code +//------------------------------------------------------------------------------ extern WFP_API void wfp_respond_error( struct wfp_request * request, wf_status status); diff --git a/include/webfuse/provider/operation/getattr.h b/include/webfuse/provider/operation/getattr.h index 14af3dc..4c9564f 100644 --- a/include/webfuse/provider/operation/getattr.h +++ b/include/webfuse/provider/operation/getattr.h @@ -1,3 +1,8 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/operation/getattr.h +/// \brief Get file attributes. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WFP_OPERATION_GETATTR_H #define WFP_OPERATION_GETATTR_H @@ -14,11 +19,30 @@ extern "C" struct wfp_request; +//------------------------------------------------------------------------------ +/// \brief Get file attributes. +/// +/// \note After this function is called, exactly one response must be sent, +/// either via \ref wfp_respond_getattr or via \ref wfp_respond_error. +/// +/// \param request pointer to request +/// \param inode inode of file to get attributes +/// \param user_data user defined context +/// +/// \see wfp_respond_getattr +/// \see wfp_respond_error +//------------------------------------------------------------------------------ typedef void wfp_getattr_fn( struct wfp_request * request, ino_t inode, void * user_data); +//------------------------------------------------------------------------------ +/// \brief Respond to a get attributes request. +/// +/// \param request pointer to request +/// \param stat file attributes +//------------------------------------------------------------------------------ extern WFP_API void wfp_respond_getattr( struct wfp_request * request, struct stat const * stat); diff --git a/include/webfuse/provider/operation/lookup.h b/include/webfuse/provider/operation/lookup.h index 5e177d7..e8f9ca6 100644 --- a/include/webfuse/provider/operation/lookup.h +++ b/include/webfuse/provider/operation/lookup.h @@ -1,3 +1,8 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/operation/lookup.h +/// \brief Lookup file. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WFP_OPERATION_LOOKUP_H #define WFP_OPERATION_LOOKUP_H @@ -14,12 +19,32 @@ extern "C" struct wfp_request; +//------------------------------------------------------------------------------ +/// \brief Lookup a file or directory. +/// +/// \note After this function is called, exactly one response must be sent, +/// either via \ref wfp_respond_lookup or via \ref wfp_respond_error. +/// +/// \param request pointer to request +/// \param parent inode of parent +/// \param name name of the filesystem object to lookup +/// \param user_data pointer to user defined context +/// +/// \see wfp_respond_lookup +/// \see wfp_respond_error +//------------------------------------------------------------------------------ typedef void wfp_lookup_fn( struct wfp_request * request, ino_t parent, char const * name, void * user_data); +//------------------------------------------------------------------------------ +/// \brief Respond to lookup request. +/// +/// \param request pointer to request +/// \param stat attributes of filesystem object +//------------------------------------------------------------------------------ extern WFP_API void wfp_respond_lookup( struct wfp_request * request, struct stat const * stat); diff --git a/include/webfuse/provider/operation/open.h b/include/webfuse/provider/operation/open.h index 9114828..d5d9f9a 100644 --- a/include/webfuse/provider/operation/open.h +++ b/include/webfuse/provider/operation/open.h @@ -1,3 +1,8 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/operation/open.h +/// \brief Open a file. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WFP_OPERATION_OPEN_H #define WFP_OPERATION_OPEN_H @@ -20,12 +25,32 @@ extern "C" struct wfp_request; +//------------------------------------------------------------------------------ +/// \brief Open a file. +/// +/// \note After this function is called, exactly one response must be sent, +/// either via \ref wfp_respond_open or via \ref wfp_respond_error. +/// +/// \param request pointer to request +/// \param inode inode of the file to open +/// \param flags file open flags +/// \param user_data user defined context +/// +/// \see wfp_respond_open +/// \see wfp_respond_error +//------------------------------------------------------------------------------ typedef void wfp_open_fn( struct wfp_request * request, ino_t inode, int flags, void * user_data); +//------------------------------------------------------------------------------ +/// \brief Respond to open file. +/// +/// \param request pointer to request +/// \param handle handle of the opened file +//------------------------------------------------------------------------------ extern WFP_API void wfp_respond_open( struct wfp_request * request, uint32_t handle); diff --git a/include/webfuse/provider/operation/read.h b/include/webfuse/provider/operation/read.h index 570c310..64d0047 100644 --- a/include/webfuse/provider/operation/read.h +++ b/include/webfuse/provider/operation/read.h @@ -1,3 +1,8 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/operation/read.h +/// \brief Read contents of a file. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WFP_OPERATION_READ_H #define WFP_OPERATION_READ_H @@ -23,6 +28,25 @@ extern "C" struct wfp_request; +//------------------------------------------------------------------------------ +/// \brief Requests content of a file. +/// +/// On success, up to \arg length bytes should be returned via \ref +/// wfp_respond_read. +/// +/// \note After this function is called, exactly one response must be sent, +/// either via \ref wfp_respond_read or via \ref wfp_respond_error. +/// +/// \param request pointer to request +/// \param inode inode of the file to read +/// \param handle handle of the file to read (returned by open) +/// \param offset offset within the file where to start reading +/// \param length amount of bytes to read +/// \param user_data used defined context +/// +/// \see wfp_respond_read +/// \see wfp_respond_error +//------------------------------------------------------------------------------ typedef void wfp_read_fn( struct wfp_request * request, ino_t inode, @@ -31,12 +55,20 @@ typedef void wfp_read_fn( size_t length, void * user_data); +//------------------------------------------------------------------------------ +/// \brief Respond to read. +/// +/// \note The user is responsible to manage lifetime of \arg data. +/// +/// \param request pointer to request +/// \param data data read from file +/// \param length amount of bytes read +//------------------------------------------------------------------------------ extern WFP_API void wfp_respond_read( struct wfp_request * request, char const * data, size_t length); - #ifdef __cplusplus } #endif diff --git a/include/webfuse/provider/operation/readdir.h b/include/webfuse/provider/operation/readdir.h index 968b7f3..aef2f07 100644 --- a/include/webfuse/provider/operation/readdir.h +++ b/include/webfuse/provider/operation/readdir.h @@ -1,3 +1,8 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file provider/operation/readdir.h +/// \brief List directory contents. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WFP_OPERATION_READDIR_H #define WFP_OPERATION_READDIR_H @@ -15,11 +20,33 @@ extern "C" struct wfp_dirbuffer; struct wfp_request; +//------------------------------------------------------------------------------ +/// \brief Requests the contents of a directory. +/// +/// \note After this function is called, exactly one response must be sent, +/// either via \ref wfp_respond_readdir or via \ref wfp_respond_error. +/// +/// \param request pointer to request +/// \param directory inode of directory to list +/// \param user_data user defined context +/// +/// \see wfp_respond_readdir +/// \see wfp_respond_error +//------------------------------------------------------------------------------ typedef void wfp_readdir_fn( struct wfp_request * request, ino_t directory, void * user_data); +//------------------------------------------------------------------------------ +/// \brief Respond to list directory contents. +/// +/// \note The user is responsible to manage dirbuffe, p.e. to dispose +/// it after this function is called. +/// +/// \param request pointer to request +/// \param dirbuffer contains contents of directory +//------------------------------------------------------------------------------ extern WFP_API void wfp_respond_readdir( struct wfp_request * request, struct wfp_dirbuffer * dirbuffer); diff --git a/include/webfuse_adapter.h b/include/webfuse_adapter.h index 8edad51..1ff048f 100644 --- a/include/webfuse_adapter.h +++ b/include/webfuse_adapter.h @@ -1,7 +1,13 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file webfuse_adapter.h +/// \brief Convenience header to include all functionality of libfuse_adapter. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WF_ADAPTER_H #define WF_ADAPTER_H #include +#include #include #include @@ -9,5 +15,6 @@ #include #include #include +#include #endif diff --git a/include/webfuse_provider.h b/include/webfuse_provider.h index e37fd68..19afad2 100644 --- a/include/webfuse_provider.h +++ b/include/webfuse_provider.h @@ -1,13 +1,20 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file webfuse_provider.h +/// \brief Convenience header to include all functionality of libfuse_provider. +//////////////////////////////////////////////////////////////////////////////// + #ifndef WF_PROVIDER_H #define WF_PROVIDER_H #include +#include #include #include #include #include #include +#include #include #include @@ -17,6 +24,4 @@ #include #include -#include - #endif diff --git a/install_deps.sh b/install_deps.sh deleted file mode 100755 index cb1e4a8..0000000 --- a/install_deps.sh +++ /dev/null @@ -1,172 +0,0 @@ -#!/bin/bash -# -# Checks for dependencies and install them, if needed. -# -# A valid package description is defined as follows: -# local _name= -# local _type= -# local _archive= -# local _dir= -# local _url= -# -# - symbolid id of package -# - name of the package (used for pkg-config) -# - type of project ("cmake" or "autotools") -# - archive file name -# - dir of package sources -# - url of the project archive -# -# For examples, see below. -# -# Since _ variables are not used directly, shellcheck will complain: -# shellcheck disable=SC2034 - - -####################################### -# Installs a cmake package. -# -# Arguments: -# package - name of package -# Returns: -# 0 on success, otherwise failure -####################################### -function install_cmake_package() { - local package="$1" - local archive="${package}_archive" - local dir="${package}_dir" - local url="${package}_url" - - wget -O "${!archive}" "${!url}" && \ - tar -xf "${!archive}" && \ - cd "${!dir}" && \ - mkdir .build && \ - cd .build && \ - cmake .. && \ - make && \ - sudo make install && \ - cd .. && \ - cd .. - - return "$?" -} - -####################################### -# Installs a autotools package. -# -# Arguments: -# package - name of package -# Returns: -# 0 on success, otherwise failure -####################################### -function install_autotools_package() { - local package="$1" - local archive="${package}_archive" - local dir="${package}_dir" - local url="${package}_url" - - wget -O "${!archive}" "${!url}" && \ - tar -xf "${!archive}" && \ - cd "${!dir}" && \ - ./makeconf.sh && \ - ./configure && \ - make && \ - sudo make install && \ - cd .. - - return "$?" -} - -####################################### -# Checks, whether a package exists. -# -# Arguments: -# package_name - name of package -# Returns: -# 0 if package exists, otherwise failure -####################################### -function package_exists() { - local package_name="$1" - - pkg-config --exists "${package_name}" - return "$?" -} - -####################################### -# Installs a package, if it is not available. -# -# Arguments: -# package - name of package -# Returns: -# 0 if package exists, otherwise failure -####################################### -function install_if_needed() { - local package="$1" - local package_name="${package}_name" - local package_type="${package}_type" - local result=0 - - package_exists "${!package_name}" - if [[ "$?" -ne 0 ]]; then - "install_${!package_type}_package" "$package" - result="$?" - fi - - return "${result}" -} - -####################################### -# Main -# -# Arguments: -# None -# Returns: -# 0 on success, otherwise failure -####################################### -function main() { - rm -rf .deps - mkdir .deps - cd .deps - - local packages=(fuse3 lws jansson gtest) - - local fuse3_name=fuse3 - local fuse3_type=autotools - local fuse3_archive=fuse-3.1.1.tar.gz - local fuse3_dir=libfuse-fuse-3.1.1 - local fuse3_url=https://github.com/libfuse/libfuse/archive/fuse-3.1.1.tar.gz - - local lws_name=libwebsockets - local lws_type=cmake - local lws_archive=libwebsockets-3.1.0.tar.gz - local lws_dir=libwebsockets-3.1.0 - local lws_url=https://github.com/warmcat/libwebsockets/archive/v3.1.0.tar.gz - - local jansson_name=jansson - local jansson_type=cmake - local jansson_archive=libjansson-2.12.tar.gz - local jansson_dir=jansson-2.12 - local jansson_url=https://github.com/akheron/jansson/archive/v2.12.tar.gz - - local gtest_name=gtest_main - local gtest_type=cmake - local gtest_archive=gtest-1.8.1.tar.gz - local gtest_dir=googletest-release-1.8.1 - local gtest_url=https://github.com/google/googletest/archive/release-1.8.1.tar.gz - - local result=0 - local package="" - for package in "${packages[@]}" ; do - install_if_needed $package - result="$?" - - if [[ "${result}" -ne 0 ]] ; then - cd .. - return "${result}" - fi - done - - cd .. - return 0 -} - -main "$@" diff --git a/lib/webfuse/adapter/api.c b/lib/webfuse/adapter/api.c index af68a8e..5049e16 100644 --- a/lib/webfuse/adapter/api.c +++ b/lib/webfuse/adapter/api.c @@ -4,6 +4,9 @@ #include "webfuse/adapter/impl/server_protocol.h" #include "webfuse/adapter/impl/server_config.h" #include "webfuse/adapter/impl/credentials.h" +#include "webfuse/adapter/impl/mountpoint.h" + +#include "webfuse/core/util.h" // server @@ -20,18 +23,25 @@ void wf_server_dispose( } void wf_server_service( - struct wf_server * server, - int timeout_ms) + struct wf_server * server) { - wf_impl_server_service(server, timeout_ms); + wf_impl_server_service(server); } +void wf_server_interrupt( + struct wf_server * server) +{ + wf_impl_server_interrupt(server); +} + + // server protocol struct wf_server_protocol * wf_server_protocol_create( - char * mount_point) + wf_create_mountpoint_fn * create_mountpoint, + void * create_mountpoint_context) { - return wf_impl_server_protocol_create(mount_point); + return wf_impl_server_protocol_create(create_mountpoint, create_mountpoint_context); } void wf_server_protocol_dispose( @@ -69,11 +79,13 @@ void wf_server_config_dispose( wf_impl_server_config_dispose(config); } -void wf_server_config_set_mountpoint( +void wf_server_config_set_mountpoint_factory( struct wf_server_config * config, - char const * mount_point) + wf_create_mountpoint_fn * create_mountpoint, + void * user_data) { - wf_impl_server_config_set_mountpoint(config, mount_point); + wf_impl_server_config_set_mountpoint_factory( + config, create_mountpoint, user_data); } void wf_server_config_set_documentroot( @@ -134,3 +146,35 @@ char const * wf_credentials_get( { return wf_impl_credentials_get(credentials, key); } + +// mountpoint + +struct wf_mountpoint * +wf_mountpoint_create( + char const * path) +{ + return wf_impl_mountpoint_create(path); +} + +void +wf_mountpoint_dispose( + struct wf_mountpoint * mountpoint) +{ + wf_impl_mountpoint_dispose(mountpoint); +} + +char const * +wf_mountpoint_get_path( + struct wf_mountpoint const * mountpoint) +{ + return wf_impl_mountpoint_get_path(mountpoint); +} + +void +wf_mountpoint_set_userdata( + struct wf_mountpoint * mountpoint, + void * user_data, + wf_mountpoint_userdata_dispose_fn * dispose) +{ + wf_impl_mountpoint_set_userdata(mountpoint, user_data, dispose); +} diff --git a/lib/webfuse/adapter/impl/authenticator.c b/lib/webfuse/adapter/impl/authenticator.c index 0be4b62..562a1dd 100644 --- a/lib/webfuse/adapter/impl/authenticator.c +++ b/lib/webfuse/adapter/impl/authenticator.c @@ -11,13 +11,10 @@ struct wf_impl_authenticator * wf_impl_authenticator_create( void * user_data) { struct wf_impl_authenticator * authenticator = malloc(sizeof(struct wf_impl_authenticator)); - if (NULL != authenticator) - { - authenticator->type = strdup(type); - authenticator->authenticate = authenticate; - authenticator->user_data = user_data; - authenticator->next = NULL; - } + authenticator->type = strdup(type); + authenticator->authenticate = authenticate; + authenticator->user_data = user_data; + authenticator->next = NULL; return authenticator; } diff --git a/lib/webfuse/adapter/impl/filesystem.c b/lib/webfuse/adapter/impl/filesystem.c index 3cfcca7..a033190 100644 --- a/lib/webfuse/adapter/impl/filesystem.c +++ b/lib/webfuse/adapter/impl/filesystem.c @@ -1,11 +1,17 @@ #include "webfuse/adapter/impl/filesystem.h" -#include "webfuse/adapter/impl/operations.h" +#include "webfuse/adapter/impl/operation/context.h" +#include "webfuse/adapter/impl/operation/open.h" +#include "webfuse/adapter/impl/operation/close.h" +#include "webfuse/adapter/impl/operation/read.h" +#include "webfuse/adapter/impl/operation/readdir.h" +#include "webfuse/adapter/impl/operation/getattr.h" +#include "webfuse/adapter/impl/operation/lookup.h" #include "webfuse/adapter/impl/session.h" +#include "webfuse/adapter/impl/mountpoint.h" #include "webfuse/core/string.h" #include -#include #include #include @@ -27,58 +33,6 @@ static struct fuse_lowlevel_ops const filesystem_operations = .read = &wf_impl_operation_read }; -static char * wf_impl_filesystem_create_id(void) -{ - uuid_t uuid; - uuid_generate(uuid); - char id[UUID_STR_LEN]; - uuid_unparse(uuid, id); - - return strdup(id); -} - -static bool wf_impl_filesystem_is_link_broken(char const * path, char const * id) -{ - bool result = false; - - char buffer[UUID_STR_LEN]; - ssize_t count = readlink(path, buffer, UUID_STR_LEN); - if ((0 < count) && (count < UUID_STR_LEN)) - { - buffer[count] = '\0'; - result = (0 == strcmp(buffer, id)); - } - - return result; -} - -static bool wf_impl_filesystem_link_first_subdir( - char const * link_path, - char const * path) -{ - bool result = false; - DIR * dir = opendir(path); - if (NULL != dir) - { - struct dirent * entry = readdir(dir); - while (NULL != entry) - { - if ((DT_DIR == entry->d_type) && ('.' != entry->d_name[0])) - { - symlink(entry->d_name, link_path); - result = true; - break; - } - - entry = readdir(dir); - } - - closedir(dir); - } - - return result; -} - static void wf_impl_filesystem_cleanup( struct wf_impl_filesystem * filesystem) { @@ -90,32 +44,17 @@ static void wf_impl_filesystem_cleanup( free(filesystem->buffer.mem); fuse_opt_free_args(&filesystem->args); - rmdir(filesystem->root_path); - - if (wf_impl_filesystem_is_link_broken(filesystem->default_path, filesystem->id)) - { - unlink(filesystem->default_path); - - bool const success = wf_impl_filesystem_link_first_subdir(filesystem->default_path, filesystem->service_path); - if (!success) - { - rmdir(filesystem->service_path); - } - } - + wf_mountpoint_dispose(filesystem->mountpoint); free(filesystem->user_data.name); - free(filesystem->id); - free(filesystem->root_path); - free(filesystem->default_path); - free(filesystem->service_path); } static bool wf_impl_filesystem_init( struct wf_impl_filesystem * filesystem, struct wf_impl_session * session, - char const * name) + char const * name, + struct wf_mountpoint * mountpoint) { bool result = false; @@ -129,15 +68,7 @@ static bool wf_impl_filesystem_init( filesystem->user_data.name = strdup(name); memset(&filesystem->buffer, 0, sizeof(struct fuse_buf)); - filesystem->service_path = wf_create_string("%s/%s", session->mount_point, name); - mkdir(filesystem->service_path, 0755); - - filesystem->id = wf_impl_filesystem_create_id(); - filesystem->root_path = wf_create_string("%s/%s/%s", session->mount_point, name, filesystem->id); - mkdir(filesystem->root_path, 0755); - - filesystem->default_path = wf_create_string("%s/%s/default", session->mount_point, name); - symlink(filesystem->id, filesystem->default_path); + filesystem->mountpoint = mountpoint; filesystem->session = fuse_session_new( &filesystem->args, @@ -146,7 +77,8 @@ static bool wf_impl_filesystem_init( &filesystem->user_data); if (NULL != filesystem->session) { - result = (0 == fuse_session_mount(filesystem->session, filesystem->root_path)); + char const * path = wf_mountpoint_get_path(filesystem->mountpoint); + result = (0 == fuse_session_mount(filesystem->session, path)); } if (result) @@ -169,17 +101,15 @@ static bool wf_impl_filesystem_init( struct wf_impl_filesystem * wf_impl_filesystem_create( struct wf_impl_session * session, - char const * name) + char const * name, + struct wf_mountpoint * mountpoint) { struct wf_impl_filesystem * filesystem = malloc(sizeof(struct wf_impl_filesystem)); - if (NULL != filesystem) + bool success = wf_impl_filesystem_init(filesystem, session, name, mountpoint); + if (!success) { - bool success = wf_impl_filesystem_init(filesystem, session, name); - if (!success) - { - free(filesystem); - filesystem = NULL; - } + free(filesystem); + filesystem = NULL; } return filesystem; @@ -200,9 +130,4 @@ void wf_impl_filesystem_process_request( { fuse_session_process_buf(filesystem->session, &filesystem->buffer); } - else if (-EINTR != result) - { - // ToDo - } - } diff --git a/lib/webfuse/adapter/impl/filesystem.h b/lib/webfuse/adapter/impl/filesystem.h index 648dfbd..791170a 100644 --- a/lib/webfuse/adapter/impl/filesystem.h +++ b/lib/webfuse/adapter/impl/filesystem.h @@ -6,7 +6,7 @@ #endif #include "webfuse/adapter/impl/fuse_wrapper.h" -#include "webfuse/adapter/impl/operations.h" +#include "webfuse/adapter/impl/operation/context.h" #include "webfuse/core/slist.h" #ifdef __cplusplus @@ -14,6 +14,7 @@ extern "C" { #endif +struct wf_mountpoint; struct wf_impl_session; struct lws; @@ -23,18 +24,15 @@ struct wf_impl_filesystem struct fuse_args args; struct fuse_session * session; struct fuse_buf buffer; - struct wf_impl_operations_context user_data; + struct wf_impl_operation_context user_data; struct lws * wsi; - char * name; - char * id; - char * service_path; - char * default_path; - char * root_path; + struct wf_mountpoint * mountpoint; }; extern struct wf_impl_filesystem * wf_impl_filesystem_create( struct wf_impl_session * session, - char const * name); + char const * name, + struct wf_mountpoint * mountpoint); extern void wf_impl_filesystem_dispose( struct wf_impl_filesystem * filesystem); diff --git a/lib/webfuse/adapter/impl/jsonrpc/method.c b/lib/webfuse/adapter/impl/jsonrpc/method.c deleted file mode 100644 index 8485471..0000000 --- a/lib/webfuse/adapter/impl/jsonrpc/method.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "webfuse/adapter/impl/jsonrpc/method.h" -#include -#include - -struct wf_impl_jsonrpc_method * wf_impl_jsonrpc_method_create( - char const * method_name, - wf_impl_jsonrpc_method_invoke_fn * invoke, - void * user_data) -{ - struct wf_impl_jsonrpc_method * method = malloc(sizeof(struct wf_impl_jsonrpc_method)); - if (NULL != method) - { - method->next = NULL; - method->name = strdup(method_name); - method->invoke = invoke; - method->user_data = user_data; - } - - return method; -} - -void wf_impl_jsonrpc_method_dispose( - struct wf_impl_jsonrpc_method * method) -{ - free(method->name); - free(method); -} diff --git a/lib/webfuse/adapter/impl/jsonrpc/method.h b/lib/webfuse/adapter/impl/jsonrpc/method.h deleted file mode 100644 index 739f692..0000000 --- a/lib/webfuse/adapter/impl/jsonrpc/method.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef WF_ADAPTER_IMPL_JSONRPC_METHOD_H -#define WF_ADAPTER_IMPL_JSONRPC_METHOD_H - -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - -struct wf_impl_jsonrpc_request; - -typedef void wf_impl_jsonrpc_method_invoke_fn( - struct wf_impl_jsonrpc_request * request, - char const * method_name, - json_t * params, - void * user_data); - -struct wf_impl_jsonrpc_method -{ - struct wf_impl_jsonrpc_method * next; - char * name; - wf_impl_jsonrpc_method_invoke_fn * invoke; - void * user_data; -}; - -extern struct wf_impl_jsonrpc_method * wf_impl_jsonrpc_method_create( - char const * method_name, - wf_impl_jsonrpc_method_invoke_fn * invoke, - void * user_data); - -extern void wf_impl_jsonrpc_method_dispose( - struct wf_impl_jsonrpc_method * method); - -#ifdef __cplusplus -} -#endif - - -#endif diff --git a/lib/webfuse/adapter/impl/jsonrpc/proxy.c b/lib/webfuse/adapter/impl/jsonrpc/proxy.c deleted file mode 100644 index ec0da2e..0000000 --- a/lib/webfuse/adapter/impl/jsonrpc/proxy.c +++ /dev/null @@ -1,194 +0,0 @@ -#include "webfuse/adapter/impl/jsonrpc/proxy.h" -#include - -#include "webfuse/adapter/impl/jsonrpc/response.h" - -static void wf_impl_jsonrpc_proxy_timeout( - struct wf_impl_timer * timer) -{ - struct wf_impl_jsonrpc_proxy * proxy = timer->user_data; - - if (proxy->request.is_pending) - { - wf_impl_jsonrpc_proxy_finished_fn * finished = proxy->request.finished; - void * user_data = proxy->request.user_data; - - proxy->request.is_pending = false; - proxy->request.id = 0; - proxy->request.user_data = NULL; - proxy->request.finished = NULL; - wf_impl_timer_cancel(&proxy->request.timer); - - finished(user_data, WF_BAD_TIMEOUT, NULL); - } -} - -static json_t * wf_impl_jsonrpc_request_create( - char const * method, - int id, - char const * param_info, - va_list args) -{ - json_t * request = json_object(); - json_object_set_new(request, "method", json_string(method)); - json_t * params = json_array(); - - for (char const * param_type = param_info; '\0' != *param_type; param_type++) - { - switch(*param_type) - { - case 's': - { - char const * const value = va_arg(args, char const *); - json_array_append_new(params, json_string(value)); - } - break; - case 'i': - { - int const value = va_arg(args, int); - json_array_append_new(params, json_integer(value)); - } - break; - default: - fprintf(stderr, "fatal: unknown param_type '%c'\n", *param_type); - json_decref(params); - json_decref(request); - return NULL; - } - } - - - json_object_set_new(request, "params", params); - if (0 != id) - { - json_object_set_new(request, "id", json_integer(id)); - } - - return request; -} - -void wf_impl_jsonrpc_proxy_init( - struct wf_impl_jsonrpc_proxy * proxy, - struct wf_impl_timeout_manager * timeout_manager, - int timeout, - wf_impl_jsonrpc_send_fn * send, - void * user_data) -{ - proxy->send = send; - proxy->timeout = timeout; - proxy->user_data = user_data; - proxy->request.is_pending = false; - - wf_impl_timer_init(&proxy->request.timer, timeout_manager); -} - -void wf_impl_jsonrpc_proxy_cleanup( - struct wf_impl_jsonrpc_proxy * proxy) -{ - if (proxy->request.is_pending) - { - void * user_data = proxy->request.user_data; - wf_impl_jsonrpc_proxy_finished_fn * finished = proxy->request.finished; - - proxy->request.is_pending = false; - proxy->request.finished = NULL; - proxy->request.user_data = NULL; - proxy->request.id = 0; - wf_impl_timer_cancel(&proxy->request.timer); - - finished(user_data, WF_BAD, NULL); - } - - wf_impl_timer_cleanup(&proxy->request.timer); -} - -void wf_impl_jsonrpc_proxy_invoke( - struct wf_impl_jsonrpc_proxy * proxy, - wf_impl_jsonrpc_proxy_finished_fn * finished, - void * user_data, - char const * method_name, - char const * param_info, - ... -) -{ - if (!proxy->request.is_pending) - { - proxy->request.is_pending = true; - proxy->request.finished = finished; - proxy->request.user_data = user_data; - proxy->request.id = 42; - wf_impl_timer_start(&proxy->request.timer, wf_impl_timepoint_in_msec(proxy->timeout), - &wf_impl_jsonrpc_proxy_timeout, proxy); - - va_list args; - va_start(args, param_info); - json_t * request = wf_impl_jsonrpc_request_create(method_name, proxy->request.id, param_info, args); - va_end(args); - - bool const is_send = ((NULL != request) && (proxy->send(request, proxy->user_data))); - if (!is_send) - { - proxy->request.is_pending = false; - proxy->request.finished = NULL; - proxy->request.user_data = NULL; - proxy->request.id = 0; - wf_impl_timer_cancel(&proxy->request.timer); - - finished(user_data, WF_BAD, NULL); - - } - - if (NULL != request) - { - json_decref(request); - } - } - else - { - finished(user_data, WF_BAD_BUSY, NULL); - } -} - -extern void wf_impl_jsonrpc_proxy_notify( - struct wf_impl_jsonrpc_proxy * proxy, - char const * method_name, - char const * param_info, - ... -) -{ - va_list args; - va_start(args, param_info); - json_t * request = wf_impl_jsonrpc_request_create(method_name, 0, param_info, args); - va_end(args); - if (NULL != request) - { - proxy->send(request, proxy->user_data); - json_decref(request); - } -} - - -void wf_impl_jsonrpc_proxy_onresult( - struct wf_impl_jsonrpc_proxy * proxy, - json_t * message) -{ - struct wf_impl_jsonrpc_response response; - wf_impl_jsonrpc_response_init(&response, message); - - if ((proxy->request.is_pending) && (response.id == proxy->request.id)) - { - wf_impl_jsonrpc_proxy_finished_fn * finished = proxy->request.finished; - void * user_data = proxy->request.user_data; - - proxy->request.is_pending = false; - proxy->request.id = 0; - proxy->request.user_data = NULL; - proxy->request.finished = NULL; - wf_impl_timer_cancel(&proxy->request.timer); - - finished(user_data, response.status, response.result); - } - - wf_impl_jsonrpc_response_cleanup(&response); -} - diff --git a/lib/webfuse/adapter/impl/jsonrpc/proxy.h b/lib/webfuse/adapter/impl/jsonrpc/proxy.h deleted file mode 100644 index 0433747..0000000 --- a/lib/webfuse/adapter/impl/jsonrpc/proxy.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef WF_ADAPTER_IMPL_JSONRPC_PROXY_H -#define WF_ADAPTER_IMPL_JSONRPC_PROXY_H - -#ifndef __cplusplus -#include -#include -#include -#else -#include -#include -using std::size_t; -#endif - -#include -#include "webfuse/adapter/impl/jsonrpc/send_fn.h" -#include "webfuse/adapter/impl/time/timeout_manager.h" -#include "webfuse/adapter/impl/time/timer.h" -#include "webfuse/core/status.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void wf_impl_jsonrpc_proxy_finished_fn( - void * user_data, - wf_status status, - struct json_t const * result); - - -struct wf_impl_jsonrpc_request -{ - bool is_pending; - wf_impl_jsonrpc_proxy_finished_fn * finished; - void * user_data; - int id; - struct wf_impl_timer timer; -}; - -struct wf_impl_jsonrpc_proxy -{ - struct wf_impl_jsonrpc_request request; - int timeout; - wf_impl_jsonrpc_send_fn * send; - void * user_data; -}; - -extern void wf_impl_jsonrpc_proxy_init( - struct wf_impl_jsonrpc_proxy * proxy, - struct wf_impl_timeout_manager * manager, - int timeout, - wf_impl_jsonrpc_send_fn * send, - void * user_data); - -extern void wf_impl_jsonrpc_proxy_cleanup( - struct wf_impl_jsonrpc_proxy * proxy); - -extern void wf_impl_jsonrpc_proxy_invoke( - struct wf_impl_jsonrpc_proxy * proxy, - wf_impl_jsonrpc_proxy_finished_fn * finished, - void * user_data, - char const * method_name, - char const * param_info, - ... -); - -extern void wf_impl_jsonrpc_proxy_notify( - struct wf_impl_jsonrpc_proxy * proxy, - char const * method_name, - char const * param_info, - ... -); - -extern void wf_impl_jsonrpc_proxy_onresult( - struct wf_impl_jsonrpc_proxy * proxy, - json_t * message); - -#ifdef __cplusplus -} -#endif - - -#endif diff --git a/lib/webfuse/adapter/impl/jsonrpc/request.c b/lib/webfuse/adapter/impl/jsonrpc/request.c deleted file mode 100644 index e016e1c..0000000 --- a/lib/webfuse/adapter/impl/jsonrpc/request.c +++ /dev/null @@ -1,82 +0,0 @@ -#include "webfuse/adapter/impl/jsonrpc/request.h" -#include "webfuse/core/status_intern.h" -#include - -struct wf_impl_jsonrpc_request -{ - int id; - wf_impl_jsonrpc_send_fn * send; - void * user_data; -}; - -bool wf_impl_jsonrpc_is_request( - json_t * message) -{ - json_t * id = json_object_get(message, "id"); - json_t * method = json_object_get(message, "method"); - json_t * params = json_object_get(message, "params"); - - return (json_is_integer(id) && json_is_string(method) && - (json_is_array(params) || json_is_object(params))); -} - - -struct wf_impl_jsonrpc_request * wf_impl_jsonrpc_request_create( - int id, - wf_impl_jsonrpc_send_fn * send, - void * user_data) -{ - struct wf_impl_jsonrpc_request * request = malloc(sizeof(struct wf_impl_jsonrpc_request)); - if (NULL != request) - { - request->id = id; - request->send = send; - request->user_data = user_data; - } - - return request; -} - -void wf_impl_jsonrpc_request_dispose( - struct wf_impl_jsonrpc_request * request) -{ - free(request); -} - -void * wf_impl_jsonrpc_request_get_userdata( - struct wf_impl_jsonrpc_request * request) -{ - return request->user_data; -} - - -void wf_impl_jsonrpc_respond( - struct wf_impl_jsonrpc_request * request, - json_t * result) -{ - json_t * response = json_object(); - json_object_set_new(response, "result", result); - json_object_set_new(response, "id", json_integer(request->id)); - - request->send(response, request->user_data); - json_decref(response); - wf_impl_jsonrpc_request_dispose(request); -} - -void wf_impl_jsonrpc_respond_error( - struct wf_impl_jsonrpc_request * request, - wf_status status) -{ - json_t * err = json_object(); - json_object_set_new(err, "code", json_integer(status)); - json_object_set_new(err, "message", json_string(wf_status_tostring(status))); - - json_t * response = json_object(); - json_object_set_new(response, "error", err); - json_object_set_new(response, "id", json_integer(request->id)); - - request->send(response, request->user_data); - json_decref(response); - wf_impl_jsonrpc_request_dispose(request); -} - diff --git a/lib/webfuse/adapter/impl/jsonrpc/request.h b/lib/webfuse/adapter/impl/jsonrpc/request.h deleted file mode 100644 index 7d26228..0000000 --- a/lib/webfuse/adapter/impl/jsonrpc/request.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef WF_ADAPTER_IMPL_JSONRPC_REQUEST_H -#define WF_ADAPTER_IMPL_JSONRPC_REQUEST_H - -#ifndef __cplusplus -#include -#include -#include -#else -#include -#include -using std::size_t; -#endif - -#include -#include "webfuse/core/status.h" -#include "webfuse/adapter/impl/jsonrpc/send_fn.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - -struct wf_impl_jsonrpc_request; - -extern bool wf_impl_jsonrpc_is_request( - json_t * message); - -extern struct wf_impl_jsonrpc_request * wf_impl_jsonrpc_request_create( - int id, - wf_impl_jsonrpc_send_fn * send, - void * user_data); - -extern void wf_impl_jsonrpc_request_dispose( - struct wf_impl_jsonrpc_request * request); - -extern void * wf_impl_jsonrpc_request_get_userdata( - struct wf_impl_jsonrpc_request * request); - -extern void wf_impl_jsonrpc_respond( - struct wf_impl_jsonrpc_request * request, - json_t * result); - -extern void wf_impl_jsonrpc_respond_error( - struct wf_impl_jsonrpc_request * request, - wf_status status); - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/lib/webfuse/adapter/impl/jsonrpc/response.c b/lib/webfuse/adapter/impl/jsonrpc/response.c deleted file mode 100644 index 00eb396..0000000 --- a/lib/webfuse/adapter/impl/jsonrpc/response.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "webfuse/adapter/impl/jsonrpc/response.h" - -extern bool wf_impl_jsonrpc_is_response( - json_t * message) -{ - json_t * id = json_object_get(message, "id"); - json_t * err = json_object_get(message, "error"); - json_t * result = json_object_get(message, "result"); - - return (json_is_integer(id) && - (json_is_object(err) || (NULL != result))); -} - - -void wf_impl_jsonrpc_response_init( - struct wf_impl_jsonrpc_response * result, - json_t * response) -{ - result->status = WF_BAD; - result->id = -1; - result->result = NULL; - - json_t * id_holder = json_object_get(response, "id"); - if ((NULL == id_holder) || (!json_is_integer(id_holder))) - { - result->status = WF_BAD_FORMAT; - return; - } - - result->status = WF_GOOD; - result->id = json_integer_value(id_holder); - result->result = json_object_get(response, "result"); - if (NULL != result->result) - { - json_incref(result->result); - } - else - { - result->status = WF_BAD_FORMAT; - - json_t * error = json_object_get(response, "error"); - if (NULL != error) - { - json_t * error_code = json_object_get(error, "code"); - if ((NULL != error_code) && (json_is_integer(error_code))) - { - result->status = json_integer_value(error_code); - } - } - } -} - -void wf_impl_jsonrpc_response_cleanup( - struct wf_impl_jsonrpc_response * response) -{ - if (NULL != response->result) - { - json_decref(response->result); - } -} diff --git a/lib/webfuse/adapter/impl/jsonrpc/response.h b/lib/webfuse/adapter/impl/jsonrpc/response.h deleted file mode 100644 index d53f667..0000000 --- a/lib/webfuse/adapter/impl/jsonrpc/response.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef WF_ADAPTER_IMPL_JSONRPC_RESPONSE_H -#define WF_ADAPTER_IMPL_JSONRPC_RESPONSE_H - -#ifndef __cplusplus -#include -#include -#else -#include -using std::size_t; -#endif - -#include -#include "webfuse/core/status.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct wf_impl_jsonrpc_response -{ - wf_status status; - int id; - json_t * result; -}; - -extern bool wf_impl_jsonrpc_is_response( - json_t * message); - -extern void wf_impl_jsonrpc_response_init( - struct wf_impl_jsonrpc_response * response, - json_t * message); - -extern void wf_impl_jsonrpc_response_cleanup( - struct wf_impl_jsonrpc_response * response); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/lib/webfuse/adapter/impl/jsonrpc/server.c b/lib/webfuse/adapter/impl/jsonrpc/server.c deleted file mode 100644 index 717d71b..0000000 --- a/lib/webfuse/adapter/impl/jsonrpc/server.c +++ /dev/null @@ -1,95 +0,0 @@ -#include "webfuse/adapter/impl/jsonrpc/server.h" -#include "webfuse/adapter/impl/jsonrpc/method.h" -#include "webfuse/adapter/impl/jsonrpc/request.h" -#include "webfuse/core/util.h" - -#include - -void wf_impl_jsonrpc_server_init( - struct wf_impl_jsonrpc_server * server) -{ - server->methods = NULL; -} - -void wf_impl_jsonrpc_server_cleanup( - struct wf_impl_jsonrpc_server * server) -{ - struct wf_impl_jsonrpc_method * current = server->methods; - while (NULL != current) - { - struct wf_impl_jsonrpc_method * next = current->next; - wf_impl_jsonrpc_method_dispose(current); - current = next; - } - server->methods = NULL; -} - -void wf_impl_jsonrpc_server_add( - struct wf_impl_jsonrpc_server * server, - char const * method_name, - wf_impl_jsonrpc_method_invoke_fn * invoke, - void * user_data) -{ - struct wf_impl_jsonrpc_method * method = wf_impl_jsonrpc_method_create(method_name, invoke, user_data); - method->next = server->methods; - server->methods = method; -} - -static void wf_impl_jsonrpc_server_invalid_method_invoke( - struct wf_impl_jsonrpc_request * request, - char const * WF_UNUSED_PARAM(method_name), - json_t * WF_UNUSED_PARAM(params), - void * WF_UNUSED_PARAM(user_data)) -{ - wf_impl_jsonrpc_respond_error(request, WF_BAD_NOTIMPLEMENTED); -} - -static struct wf_impl_jsonrpc_method const wf_impl_jsonrpc_server_invalid_method = -{ - .next = NULL, - .name = "", - .invoke = &wf_impl_jsonrpc_server_invalid_method_invoke, - .user_data = NULL -}; - -static struct wf_impl_jsonrpc_method const * wf_impl_jsonrpc_server_get_method( - struct wf_impl_jsonrpc_server * server, - char const * method_name) -{ - struct wf_impl_jsonrpc_method const * current = server->methods; - while (NULL != current) - { - if (0 == strcmp(method_name, current->name)) - { - return current; - } - - current = current->next; - } - - return &wf_impl_jsonrpc_server_invalid_method; -} - -void wf_impl_jsonrpc_server_process( - struct wf_impl_jsonrpc_server * server, - json_t * request_data, - wf_impl_jsonrpc_send_fn * send, - void * user_data) -{ - json_t * method_holder = json_object_get(request_data, "method"); - json_t * params = json_object_get(request_data, "params"); - json_t * id_holder = json_object_get(request_data, "id"); - - if (json_is_string(method_holder) && - (json_is_array(params) || (json_is_object(params))) && - json_is_integer(id_holder)) - { - char const * method_name = json_string_value(method_holder); - int id = json_integer_value(id_holder); - struct wf_impl_jsonrpc_request * request = wf_impl_jsonrpc_request_create(id, send, user_data); - struct wf_impl_jsonrpc_method const * method = wf_impl_jsonrpc_server_get_method(server, method_name); - - method->invoke(request, method_name, params, method->user_data); - } -} - diff --git a/lib/webfuse/adapter/impl/jsonrpc/server.h b/lib/webfuse/adapter/impl/jsonrpc/server.h deleted file mode 100644 index 51c827b..0000000 --- a/lib/webfuse/adapter/impl/jsonrpc/server.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef WF_ADAPTER_IMPL_JSONRPC_SERVER_H -#define WF_ADAPTER_IMPL_JSONRPC_SERVER_H - -#ifndef __cplusplus -#include -#include -#else -#include -#endif - -#include -#include "webfuse/core/status.h" -#include "webfuse/adapter/impl/jsonrpc/send_fn.h" -#include "webfuse/adapter/impl/jsonrpc/method.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - -struct wf_impl_jsonrpc_server -{ - struct wf_impl_jsonrpc_method * methods; -}; - -extern void wf_impl_jsonrpc_server_init( - struct wf_impl_jsonrpc_server * server); - -extern void wf_impl_jsonrpc_server_cleanup( - struct wf_impl_jsonrpc_server * server); - -extern void wf_impl_jsonrpc_server_add( - struct wf_impl_jsonrpc_server * server, - char const * method_name, - wf_impl_jsonrpc_method_invoke_fn * invoke, - void * user_data); - -extern void wf_impl_jsonrpc_server_process( - struct wf_impl_jsonrpc_server * server, - json_t * request, - wf_impl_jsonrpc_send_fn * send, - void * user_data); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/lib/webfuse/adapter/impl/jsonrpc/util.h b/lib/webfuse/adapter/impl/jsonrpc/util.h deleted file mode 100644 index 0d5c6ab..0000000 --- a/lib/webfuse/adapter/impl/jsonrpc/util.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef WF_ADAPTER_IMPL_JSON_UTIL_H -#define WF_ADAPTER_IMPL_JSON_UTIL_H - -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - -extern int wf_impl_json_get_int(json_t const * object, char const * key, int default_value); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/lib/webfuse/adapter/impl/mountpoint.c b/lib/webfuse/adapter/impl/mountpoint.c new file mode 100644 index 0000000..a459510 --- /dev/null +++ b/lib/webfuse/adapter/impl/mountpoint.c @@ -0,0 +1,53 @@ +#include "webfuse/adapter/impl/mountpoint.h" + +#include +#include + +struct wf_mountpoint +{ + char * path; + void * user_data; + wf_mountpoint_userdata_dispose_fn * dispose; +}; + +struct wf_mountpoint * +wf_impl_mountpoint_create( + char const * path) +{ + struct wf_mountpoint * mountpoint = malloc(sizeof(struct wf_mountpoint)); + mountpoint->path = strdup(path); + mountpoint->user_data = NULL; + mountpoint->dispose = NULL; + + return mountpoint; +} + +void +wf_impl_mountpoint_dispose( + struct wf_mountpoint * mountpoint) +{ + if (NULL != mountpoint->dispose) + { + mountpoint->dispose(mountpoint->user_data); + } + + free(mountpoint->path); + free(mountpoint); +} + +char const * +wf_impl_mountpoint_get_path( + struct wf_mountpoint const * mountpoint) +{ + return mountpoint->path; +} + +extern void +wf_impl_mountpoint_set_userdata( + struct wf_mountpoint * mountpoint, + void * user_data, + wf_mountpoint_userdata_dispose_fn * dispose) +{ + mountpoint->user_data = user_data; + mountpoint->dispose = dispose; +} diff --git a/lib/webfuse/adapter/impl/mountpoint.h b/lib/webfuse/adapter/impl/mountpoint.h new file mode 100644 index 0000000..77fc0ea --- /dev/null +++ b/lib/webfuse/adapter/impl/mountpoint.h @@ -0,0 +1,33 @@ +#ifndef WF_IMPL_MOUNTPOINT_H +#define WF_IMPL_MOUNTPOINT_H + +#include "webfuse/adapter/mountpoint.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern struct wf_mountpoint * +wf_impl_mountpoint_create( + char const * path); + +extern void +wf_impl_mountpoint_dispose( + struct wf_mountpoint * mountpoint); + +extern char const * +wf_impl_mountpoint_get_path( + struct wf_mountpoint const * mountpoint); + +extern void +wf_impl_mountpoint_set_userdata( + struct wf_mountpoint * mountpoint, + void * user_data, + wf_mountpoint_userdata_dispose_fn * dispose); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/adapter/impl/mountpoint_factory.c b/lib/webfuse/adapter/impl/mountpoint_factory.c new file mode 100644 index 0000000..3062888 --- /dev/null +++ b/lib/webfuse/adapter/impl/mountpoint_factory.c @@ -0,0 +1,54 @@ +#include "webfuse/adapter/impl/mountpoint_factory.h" +#include + +void +wf_impl_mountpoint_factory_init_default( + struct wf_impl_mountpoint_factory * factory) +{ + factory->create_mountpoint = NULL; + factory->user_data = NULL; +} + +void +wf_impl_mountpoint_factory_init( + struct wf_impl_mountpoint_factory * factory, + wf_create_mountpoint_fn * create_mountpoint, + void * user_data) +{ + factory->create_mountpoint = create_mountpoint; + factory->user_data = user_data; +} + +void +wf_impl_mountpoint_factory_clone( + struct wf_impl_mountpoint_factory * factory, + struct wf_impl_mountpoint_factory * other) +{ + other->create_mountpoint = factory->create_mountpoint; + other->user_data = factory->user_data; +} + +bool +wf_impl_mountpoint_factory_isvalid( + struct wf_impl_mountpoint_factory * factory) +{ + return (NULL != factory->create_mountpoint); +} + +void +wf_impl_mountpoint_factory_cleanup( + struct wf_impl_mountpoint_factory * factory) +{ + + factory->create_mountpoint = NULL; + factory->user_data = NULL; +} + +struct wf_mountpoint * +wf_impl_mountpoint_factory_create_mountpoint( + struct wf_impl_mountpoint_factory * factory, + char const * filesystem) +{ + return factory->create_mountpoint(filesystem, factory->user_data); +} + diff --git a/lib/webfuse/adapter/impl/mountpoint_factory.h b/lib/webfuse/adapter/impl/mountpoint_factory.h new file mode 100644 index 0000000..8675879 --- /dev/null +++ b/lib/webfuse/adapter/impl/mountpoint_factory.h @@ -0,0 +1,55 @@ +#ifndef WF_ADAPTER_IMPL_MOUNTPOINT_FACTORY_H +#define WF_ADAPTER_IMPL_MOUNTPOINT_FACTORY_H + +#include "webfuse/adapter/mountpoint_factory.h" +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_impl_mountpoint_factory +{ + wf_create_mountpoint_fn * create_mountpoint; + void * user_data; +}; + +extern void +wf_impl_mountpoint_factory_init_default( + struct wf_impl_mountpoint_factory * factory); + +extern void +wf_impl_mountpoint_factory_init( + struct wf_impl_mountpoint_factory * factory, + wf_create_mountpoint_fn * create_mountpoint, + void * user_data); + +extern void +wf_impl_mountpoint_factory_clone( + struct wf_impl_mountpoint_factory * factory, + struct wf_impl_mountpoint_factory * other); + +extern bool +wf_impl_mountpoint_factory_isvalid( + struct wf_impl_mountpoint_factory * factory); + +extern void +wf_impl_mountpoint_factory_init_from( + struct wf_impl_mountpoint_factory * factory, + struct wf_impl_mountpoint_factory * other); + +extern void +wf_impl_mountpoint_factory_cleanup( + struct wf_impl_mountpoint_factory * factory); + +extern struct wf_mountpoint * +wf_impl_mountpoint_factory_create_mountpoint( + struct wf_impl_mountpoint_factory * factory, + char const * filesystem); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/adapter/impl/operation/close.c b/lib/webfuse/adapter/impl/operation/close.c index ca63a8e..62d1ec3 100644 --- a/lib/webfuse/adapter/impl/operation/close.c +++ b/lib/webfuse/adapter/impl/operation/close.c @@ -1,10 +1,11 @@ -#include "webfuse/adapter/impl/operations.h" +#include "webfuse/adapter/impl/operation/close.h" +#include "webfuse/adapter/impl/operation/context.h" #include #include #include -#include "webfuse/adapter/impl/jsonrpc/proxy.h" +#include "webfuse/core/jsonrpc/proxy.h" #include "webfuse/core/util.h" void wf_impl_operation_close( @@ -12,13 +13,13 @@ void wf_impl_operation_close( fuse_ino_t inode, struct fuse_file_info * file_info) { - struct wf_impl_operations_context * user_data = fuse_req_userdata(request); - struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data); + struct wf_impl_operation_context * user_data = fuse_req_userdata(request); + struct wf_jsonrpc_proxy * rpc = wf_impl_operation_context_get_proxy(user_data); if (NULL != rpc) { int handle = (int) (file_info->fh & INT_MAX); - wf_impl_jsonrpc_proxy_notify(rpc, "close", "siii", user_data->name, inode, handle, file_info->flags); + wf_jsonrpc_proxy_notify(rpc, "close", "siii", user_data->name, inode, handle, file_info->flags); } fuse_reply_err(request, 0); diff --git a/lib/webfuse/adapter/impl/operation/close.h b/lib/webfuse/adapter/impl/operation/close.h new file mode 100644 index 0000000..d254f67 --- /dev/null +++ b/lib/webfuse/adapter/impl/operation/close.h @@ -0,0 +1,20 @@ +#ifndef WF_ADAPTER_IMPL_OPERATION_CLOSE_H +#define WF_ADAPTER_IMPL_OPERATION_CLOSE_H + +#include "webfuse/adapter/impl/fuse_wrapper.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void wf_impl_operation_close( + fuse_req_t request, + fuse_ino_t inode, + struct fuse_file_info * file_info); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/adapter/impl/operation/context.c b/lib/webfuse/adapter/impl/operation/context.c new file mode 100644 index 0000000..a39978f --- /dev/null +++ b/lib/webfuse/adapter/impl/operation/context.c @@ -0,0 +1,18 @@ +#include "webfuse/adapter/impl/operation/context.h" +#include "webfuse/adapter/impl/session_manager.h" +#include "webfuse/adapter/impl/session.h" +#include + +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; +} diff --git a/lib/webfuse/adapter/impl/operation/context.h b/lib/webfuse/adapter/impl/operation/context.h new file mode 100644 index 0000000..51d886f --- /dev/null +++ b/lib/webfuse/adapter/impl/operation/context.h @@ -0,0 +1,28 @@ +#ifndef WF_ADAPTER_IMPL_OPERATION_CONTEXT_H +#define WF_ADAPTER_IMPL_OPERATION_CONTEXT_H + +#include "webfuse/adapter/impl/fuse_wrapper.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct wf_impl_session; +struct wf_jsonrpc_proxy; + +struct wf_impl_operation_context +{ + struct wf_impl_session * session; + double timeout; + char * name; +}; + +extern struct wf_jsonrpc_proxy * wf_impl_operation_context_get_proxy( + struct wf_impl_operation_context * context); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/lib/webfuse/adapter/impl/operation/getattr.c b/lib/webfuse/adapter/impl/operation/getattr.c index 75736a2..8318dc2 100644 --- a/lib/webfuse/adapter/impl/operation/getattr.c +++ b/lib/webfuse/adapter/impl/operation/getattr.c @@ -1,4 +1,5 @@ -#include "webfuse/adapter/impl/operations.h" +#include "webfuse/adapter/impl/operation/getattr.h" +#include "webfuse/adapter/impl/operation/context.h" #include #include @@ -7,35 +8,28 @@ #include #include -#include "webfuse/adapter/impl/jsonrpc/proxy.h" -#include "webfuse/adapter/impl/jsonrpc/util.h" +#include "webfuse/core/jsonrpc/proxy.h" +#include "webfuse/core/json_util.h" #include "webfuse/core/util.h" -struct wf_impl_operation_getattr_context -{ - fuse_req_t request; - double timeout; - uid_t uid; - gid_t gid; -}; - -static void wf_impl_operation_getattr_finished( +void wf_impl_operation_getattr_finished( void * user_data, - wf_status status, - json_t const * data) + json_t const * result, + json_t const * error) { + wf_status status = wf_impl_jsonrpc_get_status(error); struct wf_impl_operation_getattr_context * context = user_data; struct stat buffer; - if (NULL != data) + if (NULL != result) { - json_t * mode_holder = json_object_get(data, "mode"); - json_t * type_holder = json_object_get(data, "type"); - if ((NULL != mode_holder) && (json_is_integer(mode_holder)) && - (NULL != type_holder) && (json_is_string(type_holder))) + json_t * mode_holder = json_object_get(result, "mode"); + json_t * type_holder = json_object_get(result, "type"); + if ((json_is_integer(mode_holder)) && (json_is_string(type_holder))) { memset(&buffer, 0, sizeof(struct stat)); + buffer.st_ino = context->inode; buffer.st_mode = json_integer_value(mode_holder) & 0555; char const * type = json_string_value(type_holder); if (0 == strcmp("file", type)) @@ -50,11 +44,10 @@ static void wf_impl_operation_getattr_finished( buffer.st_uid = context->uid; buffer.st_gid = context->gid; buffer.st_nlink = 1; - buffer.st_size = wf_impl_json_get_int(data, "size", 0); - buffer.st_atime = wf_impl_json_get_int(data, "atime", 0); - buffer.st_mtime = wf_impl_json_get_int(data, "mtime", 0); - buffer.st_ctime = wf_impl_json_get_int(data, "ctime", 0); - + buffer.st_size = wf_impl_json_get_int(result, "size", 0); + buffer.st_atime = wf_impl_json_get_int(result, "atime", 0); + buffer.st_mtime = wf_impl_json_get_int(result, "mtime", 0); + buffer.st_ctime = wf_impl_json_get_int(result, "ctime", 0); } else { @@ -80,18 +73,19 @@ void wf_impl_operation_getattr ( struct fuse_file_info * WF_UNUSED_PARAM(file_info)) { struct fuse_ctx const * context = fuse_req_ctx(request); - struct wf_impl_operations_context * user_data = fuse_req_userdata(request); - struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data); + struct wf_impl_operation_context * user_data = fuse_req_userdata(request); + struct wf_jsonrpc_proxy * rpc = wf_impl_operation_context_get_proxy(user_data); if (NULL != rpc) { struct wf_impl_operation_getattr_context * getattr_context = malloc(sizeof(struct wf_impl_operation_getattr_context)); getattr_context->request = request; + getattr_context->inode = inode; getattr_context->uid = context->uid; getattr_context->gid = context->gid; getattr_context->timeout = user_data->timeout; - wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_getattr_finished, getattr_context, "getattr", "si", user_data->name, inode); + wf_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_getattr_finished, getattr_context, "getattr", "si", user_data->name, inode); } else { diff --git a/lib/webfuse/adapter/impl/operation/getattr.h b/lib/webfuse/adapter/impl/operation/getattr.h new file mode 100644 index 0000000..3908922 --- /dev/null +++ b/lib/webfuse/adapter/impl/operation/getattr.h @@ -0,0 +1,37 @@ +#ifndef WF_ADAPTER_IMPL_OPERATION_GETATTR_H +#define WF_ADAPTER_IMPL_OPERATION_GETATTR_H + +#include "webfuse/adapter/impl/fuse_wrapper.h" + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_impl_operation_getattr_context +{ + fuse_req_t request; + fuse_ino_t inode; + double timeout; + uid_t uid; + gid_t gid; +}; + +extern void wf_impl_operation_getattr_finished( + void * user_data, + json_t const * result, + json_t const * error); + +extern void wf_impl_operation_getattr ( + fuse_req_t request, + fuse_ino_t inode, + struct fuse_file_info *file_info); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/adapter/impl/operation/lookup.c b/lib/webfuse/adapter/impl/operation/lookup.c index 3749123..4ebbe3c 100644 --- a/lib/webfuse/adapter/impl/operation/lookup.c +++ b/lib/webfuse/adapter/impl/operation/lookup.c @@ -1,4 +1,5 @@ -#include "webfuse/adapter/impl/operations.h" +#include "webfuse/adapter/impl/operation/lookup.h" +#include "webfuse/adapter/impl/operation/context.h" #include #include @@ -10,39 +11,33 @@ #include -#include "webfuse/adapter/impl/jsonrpc/proxy.h" -#include "webfuse/adapter/impl/jsonrpc/util.h" +#include "webfuse/core/jsonrpc/proxy.h" +#include "webfuse/core/json_util.h" #include "webfuse/core/util.h" -struct wf_impl_operation_lookup_context -{ - fuse_req_t request; - double timeout; - uid_t uid; - gid_t gid; -}; - -static void wf_impl_operation_lookup_finished( +void wf_impl_operation_lookup_finished( void * user_data, - wf_status status, - json_t const * data + json_t const * result, + json_t const * error ) { + wf_status status = wf_impl_jsonrpc_get_status(error); struct wf_impl_operation_lookup_context * context = user_data; struct fuse_entry_param buffer; - if (NULL != data) + if (NULL != result) { - json_t * inode_holder = json_object_get(data, "inode"); - json_t * mode_holder = json_object_get(data, "mode"); - json_t * type_holder = json_object_get(data, "type"); - if ((NULL != inode_holder) && (json_is_integer(inode_holder)) && - (NULL != mode_holder) && (json_is_integer(mode_holder)) && - (NULL != type_holder) && (json_is_string(type_holder))) + json_t * inode_holder = json_object_get(result, "inode"); + json_t * mode_holder = json_object_get(result, "mode"); + json_t * type_holder = json_object_get(result, "type"); + if ((json_is_integer(inode_holder)) && + (json_is_integer(mode_holder)) && + (json_is_string(type_holder))) { memset(&buffer, 0, sizeof(struct stat)); buffer.ino = json_integer_value(inode_holder); + buffer.attr.st_ino = buffer.ino; buffer.attr.st_mode = json_integer_value(mode_holder) & 0555; char const * type = json_string_value(type_holder); if (0 == strcmp("file", type)) @@ -60,10 +55,10 @@ static void wf_impl_operation_lookup_finished( buffer.attr.st_uid = context->uid; buffer.attr.st_gid = context->gid; buffer.attr.st_nlink = 1; - buffer.attr.st_size = wf_impl_json_get_int(data, "size", 0); - buffer.attr.st_atime = wf_impl_json_get_int(data, "atime", 0); - buffer.attr.st_mtime = wf_impl_json_get_int(data, "mtime", 0); - buffer.attr.st_ctime = wf_impl_json_get_int(data, "ctime", 0); + buffer.attr.st_size = wf_impl_json_get_int(result, "size", 0); + buffer.attr.st_atime = wf_impl_json_get_int(result, "atime", 0); + buffer.attr.st_mtime = wf_impl_json_get_int(result, "mtime", 0); + buffer.attr.st_ctime = wf_impl_json_get_int(result, "ctime", 0); } else { @@ -89,8 +84,8 @@ void wf_impl_operation_lookup ( char const * name) { struct fuse_ctx const * context = fuse_req_ctx(request); - struct wf_impl_operations_context * user_data = fuse_req_userdata(request); - struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data); + struct wf_impl_operation_context * user_data = fuse_req_userdata(request); + struct wf_jsonrpc_proxy * rpc = wf_impl_operation_context_get_proxy(user_data); if (NULL != rpc) { @@ -100,7 +95,7 @@ void wf_impl_operation_lookup ( lookup_context->gid = context->gid; lookup_context->timeout = user_data->timeout; - wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_lookup_finished, lookup_context, "lookup", "sis", user_data->name, (int) (parent & INT_MAX), name); + wf_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_lookup_finished, lookup_context, "lookup", "sis", user_data->name, (int) (parent & INT_MAX), name); } else { diff --git a/lib/webfuse/adapter/impl/operation/lookup.h b/lib/webfuse/adapter/impl/operation/lookup.h new file mode 100644 index 0000000..c43e864 --- /dev/null +++ b/lib/webfuse/adapter/impl/operation/lookup.h @@ -0,0 +1,36 @@ +#ifndef WF_ADAPTER_IMPL_OPERATION_LOOKUP_H +#define WF_ADAPTER_IMPL_OPERATION_LOOKUP_H + +#include "webfuse/adapter/impl/fuse_wrapper.h" + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_impl_operation_lookup_context +{ + fuse_req_t request; + double timeout; + uid_t uid; + gid_t gid; +}; + +extern void wf_impl_operation_lookup_finished( + void * user_data, + json_t const * result, + json_t const * error); + +extern void wf_impl_operation_lookup ( + fuse_req_t req, + fuse_ino_t parent, + char const * name); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/adapter/impl/operation/open.c b/lib/webfuse/adapter/impl/operation/open.c index e82b865..1b25d99 100644 --- a/lib/webfuse/adapter/impl/operation/open.c +++ b/lib/webfuse/adapter/impl/operation/open.c @@ -1,18 +1,20 @@ -#include "webfuse/adapter/impl/operations.h" +#include "webfuse/adapter/impl/operation/open.h" +#include "webfuse/adapter/impl/operation/context.h" + +#include "webfuse/core/jsonrpc/proxy.h" +#include "webfuse/core/util.h" +#include "webfuse/core/status.h" +#include "webfuse/core/json_util.h" #include #include -#include -#include "webfuse/adapter/impl/jsonrpc/proxy.h" -#include "webfuse/core/util.h" -#include "webfuse/core/status.h" - -static void wf_impl_operation_open_finished( +void wf_impl_operation_open_finished( void * user_data, - wf_status status, - json_t const * result) + json_t const * result, + json_t const * error) { + wf_status status = wf_impl_jsonrpc_get_status(error); fuse_req_t request = user_data; struct fuse_file_info file_info; memset(&file_info, 0, sizeof(struct fuse_file_info)); @@ -20,7 +22,7 @@ static void wf_impl_operation_open_finished( if (NULL != result) { json_t * handle_holder = json_object_get(result, "handle"); - if ((NULL != handle_holder) && (json_is_integer(handle_holder))) + if (json_is_integer(handle_holder)) { file_info.fh = json_integer_value(handle_holder); } @@ -46,12 +48,12 @@ void wf_impl_operation_open( fuse_ino_t inode, struct fuse_file_info * file_info) { - struct wf_impl_operations_context * user_data = fuse_req_userdata(request); - struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data); + struct wf_impl_operation_context * user_data = fuse_req_userdata(request); + struct wf_jsonrpc_proxy * rpc = wf_impl_operation_context_get_proxy(user_data); if (NULL != rpc) { - wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_open_finished, request, "open", "sii", user_data->name, inode, file_info->flags); + wf_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_open_finished, request, "open", "sii", user_data->name, inode, file_info->flags); } else { diff --git a/lib/webfuse/adapter/impl/operation/open.h b/lib/webfuse/adapter/impl/operation/open.h new file mode 100644 index 0000000..eb40490 --- /dev/null +++ b/lib/webfuse/adapter/impl/operation/open.h @@ -0,0 +1,26 @@ +#ifndef WF_ADAPTER_IMPL_OPERATION_OPEN_H +#define WF_ADAPTER_IMPL_OPERATION_OPEN_H + +#include "webfuse/adapter/impl/fuse_wrapper.h" +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void wf_impl_operation_open( + fuse_req_t request, + fuse_ino_t inode, + struct fuse_file_info * file_info); + +extern void wf_impl_operation_open_finished( + void * user_data, + json_t const * result, + json_t const * error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/adapter/impl/operation/read.c b/lib/webfuse/adapter/impl/operation/read.c index 11eb0f2..7e2e0f4 100644 --- a/lib/webfuse/adapter/impl/operation/read.c +++ b/lib/webfuse/adapter/impl/operation/read.c @@ -1,33 +1,47 @@ -#include "webfuse/adapter/impl/operations.h" +#include "webfuse/adapter/impl/operation/read.h" +#include "webfuse/adapter/impl/operation/context.h" #include #include #include #include -#include -#include "webfuse/adapter/impl/jsonrpc/proxy.h" +#include "webfuse/core/jsonrpc/proxy.h" +#include "webfuse/core/base64.h" +#include "webfuse/core/json_util.h" #define WF_MAX_READ_LENGTH 4096 -static char * wf_impl_fill_buffer( +char * wf_impl_fill_buffer( char const * data, + size_t data_size, char const * format, size_t count, wf_status * status) { *status = WF_GOOD; - char * buffer = malloc(count + 1); + char * buffer = malloc(count); - if ((NULL != buffer) && (0 < count)) + if (0 < count) { if (0 == strcmp("identity", format)) { - memcpy(buffer, data, count); + if (count == data_size) + { + memcpy(buffer, data, count); + } + else + { + *status = WF_BAD; + } } else if (0 == strcmp("base64", format)) { - lws_b64_decode_string(data, buffer, count + 1); + size_t result = wf_base64_decode(data, data_size, (uint8_t *) buffer, count); + if (result != count) + { + *status = WF_BAD; + } } else { @@ -35,30 +49,41 @@ static char * wf_impl_fill_buffer( } } + if (WF_GOOD != *status) + { + free(buffer); + buffer = NULL; + } + return buffer; } -static void wf_impl_operation_read_finished(void * user_data, wf_status status, json_t const * data) +void wf_impl_operation_read_finished( + void * user_data, + json_t const * result, + json_t const * error) { + wf_status status = wf_impl_jsonrpc_get_status(error); fuse_req_t request = user_data; char * buffer = NULL; size_t length = 0; - if (NULL != data) + if (NULL != result) { - json_t * data_holder = json_object_get(data, "data"); - json_t * format_holder = json_object_get(data, "format"); - json_t * count_holder = json_object_get(data, "count"); + json_t * data_holder = json_object_get(result, "data"); + json_t * format_holder = json_object_get(result, "format"); + json_t * count_holder = json_object_get(result, "count"); if (json_is_string(data_holder) && json_is_string(format_holder) && json_is_integer(count_holder)) { char const * const data = json_string_value(data_holder); + size_t const data_size = json_string_length(data_holder); char const * const format = json_string_value(format_holder); length = (size_t) json_integer_value(count_holder); - buffer = wf_impl_fill_buffer(data, format, length, &status); + buffer = wf_impl_fill_buffer(data, data_size, format, length, &status); } else { @@ -86,14 +111,14 @@ void wf_impl_operation_read( off_t offset, struct fuse_file_info * file_info) { - struct wf_impl_operations_context * user_data = fuse_req_userdata(request); - struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data); + struct wf_impl_operation_context * user_data = fuse_req_userdata(request); + struct wf_jsonrpc_proxy * rpc = wf_impl_operation_context_get_proxy(user_data); if (NULL != rpc) { int const length = (size <= WF_MAX_READ_LENGTH) ? (int) size : WF_MAX_READ_LENGTH; int handle = (file_info->fh & INT_MAX); - wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_read_finished, request, "read", "siiii", user_data->name, (int) inode, handle, (int) offset, length); + wf_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_read_finished, request, "read", "siiii", user_data->name, (int) inode, handle, (int) offset, length); } else { diff --git a/lib/webfuse/adapter/impl/operation/read.h b/lib/webfuse/adapter/impl/operation/read.h new file mode 100644 index 0000000..89228b0 --- /dev/null +++ b/lib/webfuse/adapter/impl/operation/read.h @@ -0,0 +1,36 @@ +#ifndef WF_ADAPTER_IMPL_OPERATION_READ_H +#define WF_ADAPTER_IMPL_OPERATION_READ_H + +#include "webfuse/adapter/impl/fuse_wrapper.h" +#include "webfuse/core/status.h" + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void wf_impl_operation_read( + fuse_req_t request, + fuse_ino_t ino, size_t size, off_t off, + struct fuse_file_info *fi); + +extern char * wf_impl_fill_buffer( + char const * data, + size_t data_size, + char const * format, + size_t count, + wf_status * status); + +extern void wf_impl_operation_read_finished( + void * user_data, + json_t const * result, + json_t const * error); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/adapter/impl/operation/readdir.c b/lib/webfuse/adapter/impl/operation/readdir.c index 85cfd5b..6d54636 100644 --- a/lib/webfuse/adapter/impl/operation/readdir.c +++ b/lib/webfuse/adapter/impl/operation/readdir.c @@ -1,4 +1,5 @@ -#include "webfuse/adapter/impl/operations.h" +#include "webfuse/adapter/impl/operation/readdir.h" +#include "webfuse/adapter/impl/operation/context.h" #include #include @@ -8,19 +9,13 @@ #include #include -#include "webfuse/adapter/impl/jsonrpc/proxy.h" +#include "webfuse/core/jsonrpc/proxy.h" #include "webfuse/core/util.h" +#include "webfuse/core/json_util.h" #define WF_DIRBUFFER_INITIAL_SIZE 1024 -struct wf_impl_operation_readdir_context -{ - fuse_req_t request; - size_t size; - off_t offset; -}; - struct wf_impl_dirbuffer { char * data; @@ -71,41 +66,51 @@ static size_t wf_impl_min(size_t a, size_t b) return (a < b) ? a : b; } -static void wf_impl_operation_readdir_finished( +void wf_impl_operation_readdir_finished( void * user_data, - wf_status status, - json_t const * result) + json_t const * result, + json_t const * error) { + wf_status status = wf_impl_jsonrpc_get_status(error); struct wf_impl_operation_readdir_context * context = user_data; struct wf_impl_dirbuffer buffer; wf_impl_dirbuffer_init(&buffer); - if (NULL != result) + if (json_is_array(result)) { - if (json_is_array(result)) + size_t const count = json_array_size(result); + for(size_t i = 0; i < count; i++) { - bool buffer_full = false; - size_t const count = json_array_size(result); - for(size_t i = 0; (!buffer_full) && (i < count); i++) + json_t * entry =json_array_get(result, i); + if (json_is_object(entry)) { - json_t * entry =json_array_get(result, i); - if (json_is_object(entry)) - { - json_t * name_holder = json_object_get(entry, "name"); - json_t * inode_holder = json_object_get(entry, "inode"); + json_t * name_holder = json_object_get(entry, "name"); + json_t * inode_holder = json_object_get(entry, "inode"); - if ((NULL != name_holder) && (json_is_string(name_holder)) && - (NULL != inode_holder) && (json_is_integer(inode_holder))) - { - char const * name = json_string_value(name_holder); - fuse_ino_t entry_inode = (fuse_ino_t) json_integer_value(inode_holder); - wf_impl_dirbuffer_add(context->request, &buffer, name, entry_inode); - } + if ((json_is_string(name_holder)) && (json_is_integer(inode_holder))) + { + char const * name = json_string_value(name_holder); + fuse_ino_t entry_inode = (fuse_ino_t) json_integer_value(inode_holder); + wf_impl_dirbuffer_add(context->request, &buffer, name, entry_inode); + } + else + { + status = WF_BAD_FORMAT; + break; } } + else + { + status = WF_BAD_FORMAT; + break; + } } } + else if (WF_GOOD == status) + { + status = WF_BAD_FORMAT; + } if (WF_GOOD == status) { @@ -136,17 +141,17 @@ void wf_impl_operation_readdir ( off_t offset, struct fuse_file_info * WF_UNUSED_PARAM(file_info)) { - struct wf_impl_operations_context * user_data = fuse_req_userdata(request); - struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data); + struct wf_impl_operation_context * user_data = fuse_req_userdata(request); + struct wf_jsonrpc_proxy * rpc = wf_impl_operation_context_get_proxy(user_data); if (NULL != rpc) { - struct wf_impl_operation_readdir_context * readdir_context = malloc(sizeof(struct wf_impl_operation_readdir_context)); - readdir_context->request = request; - readdir_context->size = size; - readdir_context->offset = offset; + struct wf_impl_operation_readdir_context * readdir_context = malloc(sizeof(struct wf_impl_operation_readdir_context)); + readdir_context->request = request; + readdir_context->size = size; + readdir_context->offset = offset; - wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_readdir_finished, readdir_context, "readdir", "si", user_data->name, inode); + wf_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_readdir_finished, readdir_context, "readdir", "si", user_data->name, inode); } else { diff --git a/lib/webfuse/adapter/impl/operation/readdir.h b/lib/webfuse/adapter/impl/operation/readdir.h new file mode 100644 index 0000000..40eb09e --- /dev/null +++ b/lib/webfuse/adapter/impl/operation/readdir.h @@ -0,0 +1,35 @@ +#ifndef WF_ADAPTER_IMPL_OPERATION_READDIR_H +#define WF_ADAPTER_IMPL_OPERATION_READDIR_H + +#include "webfuse/adapter/impl/fuse_wrapper.h" +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_impl_operation_readdir_context +{ + fuse_req_t request; + size_t size; + off_t offset; +}; + +extern void wf_impl_operation_readdir ( + fuse_req_t request, + fuse_ino_t inode, + size_t size, + off_t offset, + struct fuse_file_info *file_info); + +extern void wf_impl_operation_readdir_finished( + void * user_data, + json_t const * result, + json_t const * error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/adapter/impl/operations.c b/lib/webfuse/adapter/impl/operations.c deleted file mode 100644 index 393ab99..0000000 --- a/lib/webfuse/adapter/impl/operations.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "webfuse/adapter/impl/operations.h" -#include "webfuse/adapter/impl/session_manager.h" -#include "webfuse/adapter/impl/session.h" -#include - -struct wf_impl_jsonrpc_proxy * wf_impl_operations_context_get_proxy( - struct wf_impl_operations_context * context) -{ - struct wf_impl_jsonrpc_proxy * proxy = NULL; - - struct wf_impl_session * session = context->session; - if (NULL != session) - { - proxy = &session->rpc; - } - - return proxy; -} diff --git a/lib/webfuse/adapter/impl/operations.h b/lib/webfuse/adapter/impl/operations.h deleted file mode 100644 index debe4fa..0000000 --- a/lib/webfuse/adapter/impl/operations.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef WF_ADAPTER_IMPL_OPERATIONS_H -#define WF_ADAPTER_IMPL_OPERATIONS_H - -#include "webfuse/adapter/impl/fuse_wrapper.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct wf_impl_session; -struct wf_impl_jsonrpc_proxy; - -struct wf_impl_operations_context -{ - struct wf_impl_session * session; - double timeout; - char * name; -}; - -extern void wf_impl_operation_lookup ( - fuse_req_t req, - fuse_ino_t parent, - char const * name); - -extern void wf_impl_operation_getattr ( - fuse_req_t request, - fuse_ino_t inode, - struct fuse_file_info *file_info); - -extern void wf_impl_operation_readdir ( - fuse_req_t request, - fuse_ino_t inode, - size_t size, - off_t offset, - struct fuse_file_info *file_info); - -extern void wf_impl_operation_open( - fuse_req_t request, - fuse_ino_t inode, - struct fuse_file_info * file_info); - -extern void wf_impl_operation_close( - fuse_req_t request, - fuse_ino_t inode, - struct fuse_file_info * file_info); - -extern void wf_impl_operation_read( - fuse_req_t request, - fuse_ino_t ino, size_t size, off_t off, - struct fuse_file_info *fi); - -extern struct wf_impl_jsonrpc_proxy * wf_impl_operations_context_get_proxy( - struct wf_impl_operations_context * context); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/lib/webfuse/adapter/impl/server.c b/lib/webfuse/adapter/impl/server.c index 15e18f4..8f9a01d 100644 --- a/lib/webfuse/adapter/impl/server.c +++ b/lib/webfuse/adapter/impl/server.c @@ -38,7 +38,6 @@ static struct lws_context * wf_impl_server_context_create( memset(server->ws_protocols, 0, sizeof(struct lws_protocols) * WF_SERVER_PROTOCOL_COUNT); server->ws_protocols[0].name = "http"; server->ws_protocols[0].callback = lws_callback_http_dummy; - server->ws_protocols[1].name = "fs"; wf_impl_server_protocol_init_lws(&server->protocol, &server->ws_protocols[1]); memset(&server->mount, 0, sizeof(struct lws_http_mount)); @@ -76,41 +75,18 @@ static struct lws_context * wf_impl_server_context_create( } -static bool wf_impl_server_check_mountpoint( - struct wf_server_config * config) -{ - bool result = false; - - if (NULL != config->mount_point) - { - struct stat info; - int const rc = stat(config->mount_point, &info); - result = ((0 == rc) && (S_ISDIR(info.st_mode))); - - if (!result) - { - result = (0 == mkdir(config->mount_point, 0755)); - } - } - - return result; -} - struct wf_server * wf_impl_server_create( struct wf_server_config * config) { struct wf_server * server = NULL; - if (wf_impl_server_check_mountpoint(config)) + if (wf_impl_mountpoint_factory_isvalid(&config->mountpoint_factory)) { server = malloc(sizeof(struct wf_server)); - if (NULL != server) - { - wf_impl_server_protocol_init(&server->protocol, config->mount_point); - wf_impl_server_config_clone(config, &server->config); - wf_impl_authenticators_move(&server->config.authenticators, &server->protocol.authenticators); - server->context = wf_impl_server_context_create(server); - } + wf_impl_server_protocol_init(&server->protocol, &config->mountpoint_factory); + wf_impl_server_config_clone(config, &server->config); + wf_impl_authenticators_move(&server->config.authenticators, &server->protocol.authenticators); + server->context = wf_impl_server_context_create(server); } return server; @@ -132,8 +108,14 @@ bool wf_impl_server_is_operational( } void wf_impl_server_service( - struct wf_server * server, - int timeout_ms) + struct wf_server * server) { - lws_service(server->context, timeout_ms); + lws_service(server->context, 0); } + +void wf_impl_server_interrupt( + struct wf_server * server) +{ + lws_cancel_service(server->context); +} + diff --git a/lib/webfuse/adapter/impl/server.h b/lib/webfuse/adapter/impl/server.h index 1f04cc7..56dab8d 100644 --- a/lib/webfuse/adapter/impl/server.h +++ b/lib/webfuse/adapter/impl/server.h @@ -23,8 +23,10 @@ extern bool wf_impl_server_is_operational( struct wf_server * server); extern void wf_impl_server_service( - struct wf_server * server, - int timeout_ms); + struct wf_server * server); + +extern void wf_impl_server_interrupt( + struct wf_server * server); #ifdef __cplusplus } diff --git a/lib/webfuse/adapter/impl/server_config.c b/lib/webfuse/adapter/impl/server_config.c index 7098dac..4233094 100644 --- a/lib/webfuse/adapter/impl/server_config.c +++ b/lib/webfuse/adapter/impl/server_config.c @@ -20,14 +20,15 @@ void wf_impl_server_config_init( memset(config, 0, sizeof(struct wf_server_config)); wf_impl_authenticators_init(&config->authenticators); + wf_impl_mountpoint_factory_init_default(&config->mountpoint_factory); } void wf_impl_server_config_cleanup( struct wf_server_config * config) { wf_impl_authenticators_cleanup(&config->authenticators); + wf_impl_mountpoint_factory_cleanup(&config->mountpoint_factory); - free(config->mount_point); free(config->document_root); free(config->key_path); free(config->cert_path); @@ -40,7 +41,6 @@ void wf_impl_server_config_clone( struct wf_server_config * config, struct wf_server_config * clone) { - clone->mount_point = wf_impl_server_config_strdup(config->mount_point); clone->document_root = wf_impl_server_config_strdup(config->document_root); clone->key_path = wf_impl_server_config_strdup(config->key_path); clone->cert_path = wf_impl_server_config_strdup(config->cert_path); @@ -48,15 +48,13 @@ void wf_impl_server_config_clone( clone->port = config->port; wf_impl_authenticators_clone(&config->authenticators, &clone->authenticators); + wf_impl_mountpoint_factory_clone(&config->mountpoint_factory, &clone->mountpoint_factory); } struct wf_server_config * wf_impl_server_config_create(void) { struct wf_server_config * config = malloc(sizeof(struct wf_server_config)); - if (NULL != config) - { - wf_impl_server_config_init(config); - } + wf_impl_server_config_init(config); return config; } @@ -68,14 +66,16 @@ void wf_impl_server_config_dispose( free(config); } -void wf_impl_server_config_set_mountpoint( +void wf_impl_server_config_set_mountpoint_factory( struct wf_server_config * config, - char const * mount_point) + wf_create_mountpoint_fn * create_mountpoint, + void * create_mountpoint_context) { - free(config->mount_point); - config->mount_point = strdup(mount_point); + wf_impl_mountpoint_factory_init(&config->mountpoint_factory, + create_mountpoint, create_mountpoint_context); } + void wf_impl_server_config_set_documentroot( struct wf_server_config * config, char const * document_root) diff --git a/lib/webfuse/adapter/impl/server_config.h b/lib/webfuse/adapter/impl/server_config.h index 734c83a..400b842 100644 --- a/lib/webfuse/adapter/impl/server_config.h +++ b/lib/webfuse/adapter/impl/server_config.h @@ -2,6 +2,7 @@ #define WF_ADAPTER_IMPL_SERVER_CONFIG_H #include "webfuse/adapter/impl/authenticators.h" +#include "webfuse/adapter/impl/mountpoint_factory.h" #ifdef __cplusplus extern "C" { @@ -9,13 +10,13 @@ extern "C" { struct wf_server_config { - char * mount_point; char * document_root; char * key_path; char * cert_path; char * vhost_name; int port; struct wf_impl_authenticators authenticators; + struct wf_impl_mountpoint_factory mountpoint_factory; }; extern struct wf_server_config * wf_impl_server_config_create(void); @@ -33,9 +34,10 @@ extern void wf_impl_server_config_clone( struct wf_server_config * config, struct wf_server_config * clone); -extern void wf_impl_server_config_set_mountpoint( +extern void wf_impl_server_config_set_mountpoint_factory( struct wf_server_config * config, - char const * mount_point); + wf_create_mountpoint_fn * create_mountpoint, + void * create_mountpoint_context); extern void wf_impl_server_config_set_documentroot( struct wf_server_config * config, diff --git a/lib/webfuse/adapter/impl/server_protocol.c b/lib/webfuse/adapter/impl/server_protocol.c index 65f6047..1996c67 100644 --- a/lib/webfuse/adapter/impl/server_protocol.c +++ b/lib/webfuse/adapter/impl/server_protocol.c @@ -6,9 +6,14 @@ #include "webfuse/core/message.h" #include "webfuse/core/util.h" +#include "webfuse/core/protocol_names.h" #include "webfuse/adapter/impl/credentials.h" -#include "webfuse/adapter/impl/jsonrpc/request.h" +#include "webfuse/core/status_intern.h" + +#include "webfuse/core/jsonrpc/request.h" +#include "webfuse/core/timer/manager.h" +#include "webfuse/core/timer/timer.h" static int wf_impl_server_protocol_callback( struct lws * wsi, @@ -18,14 +23,11 @@ static int wf_impl_server_protocol_callback( size_t len) { struct lws_protocols const * ws_protocol = lws_get_protocol(wsi); - if (NULL == ws_protocol) - { - return 0; - } + if (NULL == ws_protocol) { return 0; } + if (ws_protocol->callback != &wf_impl_server_protocol_callback) { return 0; } struct wf_server_protocol * protocol = ws_protocol->user; - - wf_impl_timeout_manager_check(&protocol->timeout_manager); + wf_timer_manager_check(protocol->timer_manager); struct wf_impl_session * session = wf_impl_session_manager_get(&protocol->session_manager, wsi); switch (reason) @@ -38,9 +40,9 @@ static int wf_impl_server_protocol_callback( &protocol->session_manager, wsi, &protocol->authenticators, - &protocol->timeout_manager, - &protocol->server, - protocol->mount_point); + &protocol->mountpoint_factory, + protocol->timer_manager, + protocol->server); if (NULL != session) { @@ -76,17 +78,21 @@ static int wf_impl_server_protocol_callback( } struct wf_server_protocol * wf_impl_server_protocol_create( - char * mount_point) + wf_create_mountpoint_fn * create_mountpoint, + void * create_mountpoint_context) { struct wf_server_protocol * protocol = malloc(sizeof(struct wf_server_protocol)); - if (NULL != protocol) - { - wf_impl_server_protocol_init(protocol, mount_point); - } + struct wf_impl_mountpoint_factory mountpoint_factory; + wf_impl_mountpoint_factory_init(&mountpoint_factory, + create_mountpoint, create_mountpoint_context); + + wf_impl_server_protocol_init(protocol, &mountpoint_factory); return protocol; + } + void wf_impl_server_protocol_dispose( struct wf_server_protocol * protocol) { @@ -98,13 +104,14 @@ void wf_impl_server_protocol_init_lws( struct wf_server_protocol * protocol, struct lws_protocols * lws_protocol) { + lws_protocol->name = WF_PROTOCOL_NAME_ADAPTER_SERVER; lws_protocol->callback = &wf_impl_server_protocol_callback; lws_protocol->per_session_data_size = 0; lws_protocol->user = protocol; } static void wf_impl_server_protocol_authenticate( - struct wf_impl_jsonrpc_request * request, + struct wf_jsonrpc_request * request, char const * WF_UNUSED_PARAM(method_name), json_t * params, void * WF_UNUSED_PARAM(user_data)) @@ -120,7 +127,7 @@ static void wf_impl_server_protocol_authenticate( struct wf_credentials creds; wf_impl_credentials_init(&creds, type, creds_holder); - struct wf_impl_session * session = wf_impl_jsonrpc_request_get_userdata(request); + struct wf_impl_session * session = wf_jsonrpc_request_get_userdata(request); result = wf_impl_session_authenticate(session, &creds); wf_impl_credentials_cleanup(&creds); @@ -130,11 +137,11 @@ static void wf_impl_server_protocol_authenticate( if (result) { json_t * result = json_object(); - wf_impl_jsonrpc_respond(request, result); + wf_jsonrpc_respond(request, result); } else { - wf_impl_jsonrpc_respond_error(request, WF_BAD_ACCESS_DENIED); + wf_jsonrpc_respond_error(request, WF_BAD_ACCESS_DENIED, wf_status_tostring(WF_BAD_ACCESS_DENIED)); } } @@ -154,12 +161,12 @@ static bool wf_impl_server_protocol_check_name(char const * value) } static void wf_impl_server_protocol_add_filesystem( - struct wf_impl_jsonrpc_request * request, + struct wf_jsonrpc_request * request, char const * WF_UNUSED_PARAM(method_name), json_t * params, void * WF_UNUSED_PARAM(user_data)) { - struct wf_impl_session * session = wf_impl_jsonrpc_request_get_userdata(request); + struct wf_impl_session * session = wf_jsonrpc_request_get_userdata(request); wf_status status = (session->is_authenticated) ? WF_GOOD : WF_BAD_ACCESS_DENIED; char const * name = NULL; @@ -193,43 +200,43 @@ static void wf_impl_server_protocol_add_filesystem( { json_t * result = json_object(); json_object_set_new(result, "id", json_string(name)); - wf_impl_jsonrpc_respond(request, result); + wf_jsonrpc_respond(request, result); } else { - wf_impl_jsonrpc_respond_error(request, status); + wf_jsonrpc_respond_error(request, status, wf_status_tostring(status)); } } - void wf_impl_server_protocol_init( struct wf_server_protocol * protocol, - char * mount_point) + struct wf_impl_mountpoint_factory * mountpoint_factory) { - protocol->mount_point = strdup(mount_point); protocol->is_operational = false; - wf_impl_timeout_manager_init(&protocol->timeout_manager); + wf_impl_mountpoint_factory_clone(mountpoint_factory, &protocol->mountpoint_factory); + + protocol->timer_manager = wf_timer_manager_create(); wf_impl_session_manager_init(&protocol->session_manager); wf_impl_authenticators_init(&protocol->authenticators); - wf_impl_jsonrpc_server_init(&protocol->server); - wf_impl_jsonrpc_server_add(&protocol->server, "authenticate", &wf_impl_server_protocol_authenticate, protocol); - wf_impl_jsonrpc_server_add(&protocol->server, "add_filesystem", &wf_impl_server_protocol_add_filesystem, protocol); + protocol->server = wf_jsonrpc_server_create(); + wf_jsonrpc_server_add(protocol->server, "authenticate", &wf_impl_server_protocol_authenticate, protocol); + wf_jsonrpc_server_add(protocol->server, "add_filesystem", &wf_impl_server_protocol_add_filesystem, protocol); } void wf_impl_server_protocol_cleanup( struct wf_server_protocol * protocol) { - free(protocol->mount_point); protocol->is_operational = false; - wf_impl_jsonrpc_server_cleanup(&protocol->server); - wf_impl_timeout_manager_cleanup(&protocol->timeout_manager); + wf_jsonrpc_server_dispose(protocol->server); + wf_timer_manager_dispose(protocol->timer_manager); wf_impl_authenticators_cleanup(&protocol->authenticators); wf_impl_session_manager_cleanup(&protocol->session_manager); + wf_impl_mountpoint_factory_cleanup(&protocol->mountpoint_factory); } void wf_impl_server_protocol_add_authenticator( diff --git a/lib/webfuse/adapter/impl/server_protocol.h b/lib/webfuse/adapter/impl/server_protocol.h index 8e4cd8d..269e3be 100644 --- a/lib/webfuse/adapter/impl/server_protocol.h +++ b/lib/webfuse/adapter/impl/server_protocol.h @@ -1,11 +1,11 @@ #ifndef WF_ADAPTER_IMPL_SERVER_PROTOCOL_H #define WF_ADAPTER_IMPL_SERVER_PROTOCOL_H -#include "webfuse/adapter/impl/jsonrpc/proxy.h" -#include "webfuse/adapter/impl/time/timeout_manager.h" #include "webfuse/adapter/impl/authenticators.h" +#include "webfuse/adapter/impl/mountpoint_factory.h" #include "webfuse/adapter/impl/session_manager.h" -#include "webfuse/adapter/impl/jsonrpc/server.h" +#include "webfuse/core/jsonrpc/proxy.h" +#include "webfuse/core/jsonrpc/server.h" #ifndef __cplusplus #include @@ -17,26 +17,28 @@ extern "C" #endif struct lws_protocols; +struct wf_timer_manager; struct wf_server_protocol { - char * mount_point; - struct wf_impl_timeout_manager timeout_manager; struct wf_impl_authenticators authenticators; + struct wf_impl_mountpoint_factory mountpoint_factory; struct wf_impl_session_manager session_manager; - struct wf_impl_jsonrpc_server server; + struct wf_jsonrpc_server * server; + struct wf_timer_manager * timer_manager; bool is_operational; }; extern void wf_impl_server_protocol_init( struct wf_server_protocol * protocol, - char * mount_point); + struct wf_impl_mountpoint_factory * mountpoint_factory); extern void wf_impl_server_protocol_cleanup( struct wf_server_protocol * protocol); -extern struct wf_server_protocol * wf_impl_server_protocol_create( - char * mount_point); +extern WF_API struct wf_server_protocol * wf_impl_server_protocol_create( + wf_create_mountpoint_fn * create_mountpoint, + void * create_mountpoint_context); extern void wf_impl_server_protocol_dispose( struct wf_server_protocol * protocol); diff --git a/lib/webfuse/adapter/impl/session.c b/lib/webfuse/adapter/impl/session.c index 82f9b80..6bacd10 100644 --- a/lib/webfuse/adapter/impl/session.c +++ b/lib/webfuse/adapter/impl/session.c @@ -2,13 +2,16 @@ #include "webfuse/adapter/impl/authenticators.h" #include "webfuse/core/message_queue.h" #include "webfuse/core/message.h" -#include "webfuse/adapter/impl/jsonrpc/proxy.h" -#include "webfuse/adapter/impl/jsonrpc/request.h" -#include "webfuse/adapter/impl/jsonrpc/response.h" +#include "webfuse/adapter/impl/mountpoint_factory.h" +#include "webfuse/adapter/impl/mountpoint.h" #include "webfuse/core/container_of.h" #include "webfuse/core/util.h" +#include "webfuse/core/jsonrpc/proxy.h" +#include "webfuse/core/jsonrpc/request.h" +#include "webfuse/core/jsonrpc/response.h" + #include #include #include @@ -22,7 +25,7 @@ static bool wf_impl_session_send( struct wf_impl_session * session = user_data; struct wf_message * message = wf_message_create(request); - bool result = (session->is_authenticated || wf_impl_jsonrpc_is_response(request)) && (NULL != session->wsi); + bool result = (session->is_authenticated || wf_jsonrpc_is_response(request)) && (NULL != session->wsi); if (result) { @@ -42,24 +45,21 @@ static bool wf_impl_session_send( struct wf_impl_session * wf_impl_session_create( struct lws * wsi, struct wf_impl_authenticators * authenticators, - struct wf_impl_timeout_manager * timeout_manager, - struct wf_impl_jsonrpc_server * server, - char const * mount_point) + struct wf_timer_manager * timer_manager, + struct wf_jsonrpc_server * server, + struct wf_impl_mountpoint_factory * mountpoint_factory) { struct wf_impl_session * session = malloc(sizeof(struct wf_impl_session)); - if (NULL != session) - { - wf_slist_init(&session->filesystems); - - session->mount_point = strdup(mount_point); - session->wsi = wsi; - session->is_authenticated = false; - session->authenticators = authenticators; - session->server = server; - wf_impl_jsonrpc_proxy_init(&session->rpc, timeout_manager, WF_DEFAULT_TIMEOUT, &wf_impl_session_send, session); - wf_slist_init(&session->messages); - } + wf_slist_init(&session->filesystems); + + session->wsi = wsi; + session->is_authenticated = false; + session->authenticators = authenticators; + session->server = server; + session->mountpoint_factory = mountpoint_factory; + session->rpc = wf_jsonrpc_proxy_create(timer_manager, WF_DEFAULT_TIMEOUT, &wf_impl_session_send, session); + wf_slist_init(&session->messages); return session; } @@ -81,15 +81,10 @@ static void wf_impl_session_dispose_filesystems( void wf_impl_session_dispose( struct wf_impl_session * session) { - wf_impl_jsonrpc_proxy_cleanup(&session->rpc); + wf_jsonrpc_proxy_dispose(session->rpc); wf_message_queue_cleanup(&session->messages); wf_impl_session_dispose_filesystems(&session->filesystems); - session->is_authenticated = false; - session->wsi = NULL; - session->authenticators = NULL; - session->server = NULL; - free(session->mount_point); free(session); } @@ -106,9 +101,28 @@ bool wf_impl_session_add_filesystem( struct wf_impl_session * session, char const * name) { - struct wf_impl_filesystem * filesystem = wf_impl_filesystem_create(session, name); - wf_slist_append(&session->filesystems, &filesystem->item); - return (NULL != filesystem); + bool result; + + struct wf_mountpoint * mountpoint = wf_impl_mountpoint_factory_create_mountpoint(session->mountpoint_factory, name); + result = (NULL != mountpoint); + + if (result) + { + struct wf_impl_filesystem * filesystem = wf_impl_filesystem_create(session, name, mountpoint); + wf_slist_append(&session->filesystems, &filesystem->item); + result = (NULL != filesystem); + } + + // cleanup on error + if (!result) + { + if (NULL != mountpoint) + { + wf_impl_mountpoint_dispose(mountpoint); + } + } + + return result; } @@ -138,13 +152,13 @@ void wf_impl_session_receive( json_t * message = json_loadb(data, length, 0, NULL); if (NULL != message) { - if (wf_impl_jsonrpc_is_response(message)) + if (wf_jsonrpc_is_response(message)) { - wf_impl_jsonrpc_proxy_onresult(&session->rpc, message); + wf_jsonrpc_proxy_onresult(session->rpc, message); } - else if (wf_impl_jsonrpc_is_request(message)) + else if (wf_jsonrpc_is_request(message)) { - wf_impl_jsonrpc_server_process(session->server, message, &wf_impl_session_send, session); + wf_jsonrpc_server_process(session->server, message, &wf_impl_session_send, session); } json_decref(message); diff --git a/lib/webfuse/adapter/impl/session.h b/lib/webfuse/adapter/impl/session.h index cb1a19d..1046a2e 100644 --- a/lib/webfuse/adapter/impl/session.h +++ b/lib/webfuse/adapter/impl/session.h @@ -10,11 +10,12 @@ using std::size_t; #endif #include "webfuse/core/message_queue.h" -#include "webfuse/adapter/impl/jsonrpc/proxy.h" -#include "webfuse/adapter/impl/jsonrpc/server.h" #include "webfuse/adapter/impl/filesystem.h" #include "webfuse/core/slist.h" +#include "webfuse/core/jsonrpc/proxy.h" +#include "webfuse/core/jsonrpc/server.h" + #ifdef __cplusplus extern "C" { @@ -24,27 +25,27 @@ struct lws; struct wf_message; struct wf_credentials; struct wf_impl_authenticators; -struct wf_impl_timeout_manager; +struct wf_impl_mountpoint_factory; struct wf_impl_session { struct wf_slist_item item; - char * mount_point; struct lws * wsi; bool is_authenticated; struct wf_slist messages; struct wf_impl_authenticators * authenticators; - struct wf_impl_jsonrpc_server * server; - struct wf_impl_jsonrpc_proxy rpc; + struct wf_impl_mountpoint_factory * mountpoint_factory; + struct wf_jsonrpc_server * server; + struct wf_jsonrpc_proxy * rpc; struct wf_slist filesystems; }; extern struct wf_impl_session * wf_impl_session_create( struct lws * wsi, struct wf_impl_authenticators * authenticators, - struct wf_impl_timeout_manager * timeout_manager, - struct wf_impl_jsonrpc_server * server, - char const * mount_point); + struct wf_timer_manager * timer_manager, + struct wf_jsonrpc_server * server, + struct wf_impl_mountpoint_factory * mountpoint_factory); extern void wf_impl_session_dispose( struct wf_impl_session * session); diff --git a/lib/webfuse/adapter/impl/session_manager.c b/lib/webfuse/adapter/impl/session_manager.c index a48489b..a872d54 100644 --- a/lib/webfuse/adapter/impl/session_manager.c +++ b/lib/webfuse/adapter/impl/session_manager.c @@ -27,16 +27,13 @@ struct wf_impl_session * wf_impl_session_manager_add( struct wf_impl_session_manager * manager, struct lws * wsi, struct wf_impl_authenticators * authenticators, - struct wf_impl_timeout_manager * timeout_manager, - struct wf_impl_jsonrpc_server * server, - char const * mount_point) + struct wf_impl_mountpoint_factory * mountpoint_factory, + struct wf_timer_manager * timer_manager, + struct wf_jsonrpc_server * server) { struct wf_impl_session * session = wf_impl_session_create( - wsi, authenticators, timeout_manager, server, mount_point); - if (NULL != session) - { - wf_slist_append(&manager->sessions, &session->item); - } + wsi, authenticators, timer_manager, server, mountpoint_factory); + wf_slist_append(&manager->sessions, &session->item); return session; } diff --git a/lib/webfuse/adapter/impl/session_manager.h b/lib/webfuse/adapter/impl/session_manager.h index bfe4aa3..691f133 100644 --- a/lib/webfuse/adapter/impl/session_manager.h +++ b/lib/webfuse/adapter/impl/session_manager.h @@ -15,8 +15,8 @@ extern "C" #endif struct lws; -struct wf_impl_timeout_manager; -struct wf_impl_jsonrpc_server; +struct wf_timer_manager; +struct wf_jsonrpc_server; struct wf_impl_session_manager { @@ -33,9 +33,9 @@ extern struct wf_impl_session * wf_impl_session_manager_add( struct wf_impl_session_manager * manager, struct lws * wsi, struct wf_impl_authenticators * authenticators, - struct wf_impl_timeout_manager * timeout_manager, - struct wf_impl_jsonrpc_server * server, - char const * mount_point); + struct wf_impl_mountpoint_factory * mountpoint_factory, + struct wf_timer_manager * timer_manager, + struct wf_jsonrpc_server * server); extern struct wf_impl_session * wf_impl_session_manager_get( struct wf_impl_session_manager * manager, diff --git a/lib/webfuse/adapter/impl/time/timeout_manager.c b/lib/webfuse/adapter/impl/time/timeout_manager.c deleted file mode 100644 index 155ffe6..0000000 --- a/lib/webfuse/adapter/impl/time/timeout_manager.c +++ /dev/null @@ -1,84 +0,0 @@ -#include "webfuse/adapter/impl/time/timeout_manager_intern.h" - -#include -#include "webfuse/adapter/impl/time/timer_intern.h" -#include "webfuse/adapter/impl/time/timepoint.h" - -void wf_impl_timeout_manager_init( - struct wf_impl_timeout_manager * manager) -{ - manager->timers = NULL; -} - -void wf_impl_timeout_manager_cleanup( - struct wf_impl_timeout_manager * manager) -{ - struct wf_impl_timer * timer = manager->timers; - while (NULL != timer) - { - struct wf_impl_timer * next = timer->next; - - wf_impl_timer_trigger(timer); - - timer = next; - } - - manager->timers = NULL; - -} - -void wf_impl_timeout_manager_check( - struct wf_impl_timeout_manager * manager) -{ - struct wf_impl_timer * timer = manager->timers; - while (NULL != timer) - { - struct wf_impl_timer * next = timer->next; - - if (wf_impl_timer_is_timeout(timer)) - { - wf_impl_timeout_manager_removetimer(manager, timer); - wf_impl_timer_trigger(timer); - } - - timer = next; - } -} - -void wf_impl_timeout_manager_addtimer( - struct wf_impl_timeout_manager * manager, - struct wf_impl_timer * timer) -{ - if (NULL != manager->timers) - { - manager->timers->prev = timer; - } - - timer->next = manager->timers; - timer->prev = NULL; - manager->timers = timer; -} - -void wf_impl_timeout_manager_removetimer( - struct wf_impl_timeout_manager * manager, - struct wf_impl_timer * timer) -{ - struct wf_impl_timer * prev = timer->prev; - struct wf_impl_timer * next = timer->next; - - if (NULL != prev) - { - prev->next = next; - } - - if (NULL != next) - { - next->prev = prev; - } - - if (manager->timers == timer) - { - manager->timers = next; - } -} - diff --git a/lib/webfuse/adapter/impl/time/timeout_manager.h b/lib/webfuse/adapter/impl/time/timeout_manager.h deleted file mode 100644 index 0e33c1b..0000000 --- a/lib/webfuse/adapter/impl/time/timeout_manager.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef WF_ADAPTER_IMPL_TIME_TIMEOUT_MANAGER_H -#define WF_ADAPTER_IMPL_TIME_TIMEOUT_MANAGER_H - -#ifdef __cplusplus -extern "C" -{ -#endif - -struct wf_impl_timer; -struct wf_impl_timeout_manager -{ - struct wf_impl_timer * timers; -}; - -extern void wf_impl_timeout_manager_init( - struct wf_impl_timeout_manager * manager); - -extern void wf_impl_timeout_manager_cleanup( - struct wf_impl_timeout_manager * manager); - -extern void wf_impl_timeout_manager_check( - struct wf_impl_timeout_manager * manager); - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/lib/webfuse/adapter/impl/time/timeout_manager_intern.h b/lib/webfuse/adapter/impl/time/timeout_manager_intern.h deleted file mode 100644 index 67ee0f8..0000000 --- a/lib/webfuse/adapter/impl/time/timeout_manager_intern.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef WF_ADAPTER_IMPL_TIME_TIMEOUT_MANAGER_INTERN_H -#define WF_ADAPTER_IMPL_TIME_TIMEOUT_MANAGER_INTERN_H - -#include "webfuse/adapter/impl/time/timeout_manager.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - -extern void wf_impl_timeout_manager_addtimer( - struct wf_impl_timeout_manager * manager, - struct wf_impl_timer * timer); - -extern void wf_impl_timeout_manager_removetimer( - struct wf_impl_timeout_manager * manager, - struct wf_impl_timer * timer); - -#ifdef __cplusplus -} -#endif - - -#endif diff --git a/lib/webfuse/adapter/impl/time/timepoint.c b/lib/webfuse/adapter/impl/time/timepoint.c deleted file mode 100644 index 8425000..0000000 --- a/lib/webfuse/adapter/impl/time/timepoint.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "webfuse/adapter/impl/time/timepoint.h" - -#include - -#define WF_MSEC_PER_SEC ((wf_impl_timepoint) 1000) -#define WF_NSEC_PER_MSEC ((wf_impl_timepoint) 1000 * 1000) - -wf_impl_timepoint wf_impl_timepoint_now(void) -{ - struct timespec tp; - clock_gettime(CLOCK_MONOTONIC, &tp); - - wf_impl_timepoint const now = (tp.tv_sec * WF_MSEC_PER_SEC) + (tp.tv_nsec / WF_NSEC_PER_MSEC); - return now; -} - -wf_impl_timepoint wf_impl_timepoint_in_msec(wf_impl_timediff value) -{ - wf_impl_timepoint const now = wf_impl_timepoint_now(); - wf_impl_timepoint result = now + ((wf_impl_timepoint) value); - - return result; -} - -bool wf_impl_timepoint_is_elapsed(wf_impl_timepoint tp) -{ - wf_impl_timepoint const now = wf_impl_timepoint_now(); - wf_impl_timediff const diff = (wf_impl_timediff) (tp - now); - - return (0 > diff); -} diff --git a/lib/webfuse/adapter/impl/time/timepoint.h b/lib/webfuse/adapter/impl/time/timepoint.h deleted file mode 100644 index c9f6560..0000000 --- a/lib/webfuse/adapter/impl/time/timepoint.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef WF_ADAPTER_IMPL_TIME_TIMEPOINT_H -#define WF_ADAPTER_IMPL_TIME_TIMEPOINT_H - -#ifndef __cplusplus -#include -#include -#else -#include -#endif - -#ifdef __cplusplus -extern "C" -{ -#endif - -typedef uint64_t wf_impl_timepoint; -typedef int64_t wf_impl_timediff; - -extern wf_impl_timepoint wf_impl_timepoint_now(void); - -extern wf_impl_timepoint wf_impl_timepoint_in_msec( - wf_impl_timediff value); - -extern bool wf_impl_timepoint_is_elapsed( - wf_impl_timepoint timepoint); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/lib/webfuse/adapter/impl/time/timer.c b/lib/webfuse/adapter/impl/time/timer.c deleted file mode 100644 index 9d1da17..0000000 --- a/lib/webfuse/adapter/impl/time/timer.c +++ /dev/null @@ -1,65 +0,0 @@ -#include "webfuse/adapter/impl/time/timer_intern.h" -#include "webfuse/adapter/impl/time/timeout_manager_intern.h" - -#include -#include - -void wf_impl_timer_init( - struct wf_impl_timer * timer, - struct wf_impl_timeout_manager * manager) -{ - timer->manager = manager; - timer->timeout = 0; - timer->timeout_handler = NULL; - timer->user_data = NULL; - timer->prev = NULL; - timer->next = NULL; -} - -void wf_impl_timer_cleanup( - struct wf_impl_timer * timer) -{ - memset(timer, 0, sizeof(struct wf_impl_timer)); -} - -void wf_impl_timer_start( - struct wf_impl_timer * timer, - wf_impl_timepoint absolute_timeout, - wf_impl_timer_timeout_fn * handler, - void * user_data) -{ - timer->timeout = absolute_timeout; - timer->timeout_handler = handler; - timer->user_data = user_data; - - wf_impl_timeout_manager_addtimer(timer->manager, timer); -} - -void wf_impl_timer_cancel( - struct wf_impl_timer * timer) -{ - wf_impl_timeout_manager_removetimer(timer->manager, timer); - - timer->timeout = 0; - timer->timeout_handler = NULL; - timer->user_data = NULL; -} - -bool wf_impl_timer_is_timeout( - struct wf_impl_timer * timer) -{ - return wf_impl_timepoint_is_elapsed(timer->timeout); -} - - -void wf_impl_timer_trigger( - struct wf_impl_timer * timer) -{ - if (NULL != timer->timeout_handler) - { - timer->prev = NULL; - timer->next = NULL; - - timer->timeout_handler(timer); - } -} diff --git a/lib/webfuse/adapter/impl/time/timer.h b/lib/webfuse/adapter/impl/time/timer.h deleted file mode 100644 index bbc840d..0000000 --- a/lib/webfuse/adapter/impl/time/timer.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef WF_ADAPTER_IMPL_TIME_TIMER_H -#define WF_ADAPTER_IMPL_TIME_TIMER_H - -#include "webfuse/adapter/impl/time/timepoint.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - -struct wf_impl_timer; -struct wf_impl_timeout_manager; - -typedef void wf_impl_timer_timeout_fn(struct wf_impl_timer * timer); - -struct wf_impl_timer -{ - struct wf_impl_timeout_manager * manager; - wf_impl_timepoint timeout; - wf_impl_timer_timeout_fn * timeout_handler; - void * user_data; - struct wf_impl_timer * next; - struct wf_impl_timer * prev; -}; - -extern void wf_impl_timer_init( - struct wf_impl_timer * timer, - struct wf_impl_timeout_manager * manager); - -extern void wf_impl_timer_cleanup( - struct wf_impl_timer * timer); - -extern void wf_impl_timer_start( - struct wf_impl_timer * timer, - wf_impl_timepoint absolute_timeout, - wf_impl_timer_timeout_fn * handler, - void * user_data); - -extern void wf_impl_timer_cancel( - struct wf_impl_timer * timer); - -#ifdef __cplusplus -} -#endif - - - -#endif diff --git a/lib/webfuse/adapter/impl/time/timer_intern.h b/lib/webfuse/adapter/impl/time/timer_intern.h deleted file mode 100644 index 6f1f19f..0000000 --- a/lib/webfuse/adapter/impl/time/timer_intern.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef WF_ADAPTER_IMPL_TIME_TIMER_INTERN_H -#define WF_ADAPTER_IMPL_TIME_TIMER_INTERN_H - -#ifndef __cplusplus -#include -#endif - -#include "webfuse/adapter/impl/time/timer.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - -extern bool wf_impl_timer_is_timeout( - struct wf_impl_timer * timer); - -extern void wf_impl_timer_trigger( - struct wf_impl_timer * timer); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/lib/webfuse/core/base64.c b/lib/webfuse/core/base64.c new file mode 100644 index 0000000..2b24688 --- /dev/null +++ b/lib/webfuse/core/base64.c @@ -0,0 +1,183 @@ +#include "webfuse/core/base64.h" + +static const uint8_t wf_base64_decode_table[256] = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 0 + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 1 + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 62, 0x80, 0x80, 0x80, 63, // 2 + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0x80, 0x80, 0x80, 0, 0x80, 0x80, // 3 + 0x80, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 4 + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0x80, 0x80, 0x80, 0x80, 0x80, // 5 + 0x80, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 6 + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0x80, 0x80, 0x80, 0x80, 0x80, // 7 + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 8 + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 9 + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // A + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // B + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // C + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // D + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // E + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // F +}; + + + +size_t wf_base64_encoded_size(size_t length) +{ + return ((length + 2) / 3) * 4; +} + +size_t wf_base64_encode( + uint8_t const * data, + size_t length, + char * buffer, + size_t buffer_size) +{ + // 0 1 2 3 4 5 6 + // 0123456789012345678901234567890123456789012345678901234567890123 + static char const table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + size_t const length_needed = wf_base64_encoded_size(length); + if (buffer_size < length_needed) + { + return 0; + } + + size_t pos = 0; + size_t out_pos = 0; + for(; (length - pos) >= 3; pos += 3) + { + buffer[out_pos++] = table[ data[pos] >> 2 ]; + buffer[out_pos++] = table[ ((data[pos ] & 0x03) << 4) | (data[pos + 1] >> 4) ]; + buffer[out_pos++] = table[ ((data[pos + 1] & 0x0f) << 2) | (data[pos + 2] >> 6) ]; + buffer[out_pos++] = table[ data[pos + 2] & 0x3f ]; + } + + switch((length - pos)) + { + case 1: + buffer[out_pos++] = table[ data[pos] >> 2 ]; + buffer[out_pos++] = table[ ((data[pos] & 0x03) << 4) ]; + buffer[out_pos++] = '='; + buffer[out_pos++] = '='; + break; + case 2: + buffer[out_pos++] = table[ data[pos] >> 2 ]; + buffer[out_pos++] = table[ ((data[pos ] & 0x03) << 4) | (data[pos + 1] >> 4) ]; + buffer[out_pos++] = table[ ((data[pos + 1] & 0x0f) << 2) ]; + buffer[out_pos++] = '='; + break; + default: + break; + } + + if (buffer_size > out_pos) + { + buffer[out_pos] = '\0'; + } + + return out_pos; +} + +size_t wf_base64_decoded_size(char const * data, size_t length) +{ + size_t result = 0; + if ((length > 0) && ((length % 4) == 0)) + { + result = (length / 4) * 3; + + if ('=' == data[length - 1]) + { + result--; + if ('=' == data[length - 2]) + { + result--; + } + } + } + + return result; +} + +size_t wf_base64_decode( + char const * data, + size_t length, + uint8_t * buffer, + size_t buffer_size) +{ + uint8_t const * table = wf_base64_decode_table; + size_t needed_size = wf_base64_decoded_size(data, length); + if ((0 == needed_size) || (buffer_size < needed_size)) + { + return 0; + } + + size_t out_pos = 0; + size_t pos = 0; + for(; pos < length - 4; pos += 4) + { + uint8_t a = table[ (unsigned char) data[pos ] ]; + uint8_t b = table[ (unsigned char) data[pos + 1] ]; + uint8_t c = table[ (unsigned char) data[pos + 2] ]; + uint8_t d = table[ (unsigned char) data[pos + 3] ]; + + buffer[out_pos++] = (a << 2) | (b >> 4); + buffer[out_pos++] = (b << 4) | (c >> 2); + buffer[out_pos++] = (c << 6) | d; + } + + // decode last block + { + uint8_t a = table[ (unsigned char) data[pos ] ]; + uint8_t b = table[ (unsigned char) data[pos + 1] ]; + uint8_t c = table[ (unsigned char) data[pos + 2] ]; + uint8_t d = table[ (unsigned char) data[pos + 3] ]; + + buffer[out_pos++] = (a << 2) | (b >> 4); + if ('=' != data[pos + 2]) + { + buffer[out_pos++] = (b << 4) | (c >> 2); + if ('=' != data[pos + 3]) + { + buffer[out_pos++] = (c << 6) | d; + } + } + } + + return out_pos; +} + +extern bool wf_base64_isvalid(char const * data, size_t length) +{ + uint8_t const * table = wf_base64_decode_table; + + if ((length == 0) || ((length % 4) != 0)) + { + return false; + } + + size_t pos = 0; + for(; pos < (length - 2); pos++) + { + unsigned char c = (unsigned char) data[pos]; + if (('=' == c) || (0x80 == table[c])) + { + return false; + } + } + + if (('=' == data[pos]) && ('=' != data[pos + 1])) + { + return false; + } + + for(;pos < length; pos++) + { + char c = data[pos]; + if (0x80 == table[ (unsigned char) c]) + { + return false; + } + } + + return true; +} diff --git a/lib/webfuse/core/base64.h b/lib/webfuse/core/base64.h new file mode 100644 index 0000000..6803afb --- /dev/null +++ b/lib/webfuse/core/base64.h @@ -0,0 +1,40 @@ +#ifndef WF_BASE64_H +#define WF_BASE64_H + +#ifndef __cplusplus +#include +#include +#include +#else +#include +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern size_t wf_base64_encoded_size(size_t length); + +extern size_t wf_base64_encode( + uint8_t const * data, + size_t length, + char * buffer, + size_t buffer_size); + +extern size_t wf_base64_decoded_size(char const * data, size_t length); + +extern size_t wf_base64_decode( + char const * data, + size_t length, + uint8_t * buffer, + size_t buffer_size); + +extern bool wf_base64_isvalid(char const * data, size_t length); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/adapter/impl/jsonrpc/util.c b/lib/webfuse/core/json_util.c similarity index 53% rename from lib/webfuse/adapter/impl/jsonrpc/util.c rename to lib/webfuse/core/json_util.c index 439eddb..535e07e 100644 --- a/lib/webfuse/adapter/impl/jsonrpc/util.c +++ b/lib/webfuse/core/json_util.c @@ -1,4 +1,4 @@ -#include "webfuse/adapter/impl/jsonrpc/util.h" +#include "webfuse/core/json_util.h" int wf_impl_json_get_int(json_t const * object, char const * key, int default_value) { @@ -11,4 +11,17 @@ int wf_impl_json_get_int(json_t const * object, char const * key, int default_va } return result; -} \ No newline at end of file +} + +wf_status +wf_impl_jsonrpc_get_status( + json_t const * error) +{ + wf_status status = WF_GOOD; + if (NULL != error) + { + status = wf_impl_json_get_int(error, "code", WF_BAD_FORMAT); + } + + return status; +} diff --git a/lib/webfuse/core/json_util.h b/lib/webfuse/core/json_util.h new file mode 100644 index 0000000..db21ed1 --- /dev/null +++ b/lib/webfuse/core/json_util.h @@ -0,0 +1,26 @@ +#ifndef WF_JSON_UTIL_H +#define WF_JSON_UTIL_H + +#include +#include "webfuse/core/status.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern int +wf_impl_json_get_int( + json_t const * object, + char const * key, + int default_value); + +extern wf_status +wf_impl_jsonrpc_get_status( + json_t const * error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/core/jsonrpc/error.c b/lib/webfuse/core/jsonrpc/error.c new file mode 100644 index 0000000..ad74a58 --- /dev/null +++ b/lib/webfuse/core/jsonrpc/error.c @@ -0,0 +1,27 @@ +#include "webfuse/core/jsonrpc/error.h" + +json_t * +wf_jsonrpc_error( + int code, + char const * message) +{ + json_t * error = json_object(); + json_object_set_new(error, "code", json_integer(code)); + json_object_set_new(error, "message", json_string(message)); + + return error; +} + +void +wf_jsonrpc_propate_error( + wf_jsonrpc_proxy_finished_fn * finised, + void * user_data, + int code, + char const * message) +{ + json_t * error = wf_jsonrpc_error(code, message); + finised(user_data, NULL, error); + + json_decref(error); +} + diff --git a/lib/webfuse/core/jsonrpc/error.h b/lib/webfuse/core/jsonrpc/error.h new file mode 100644 index 0000000..2edfd92 --- /dev/null +++ b/lib/webfuse/core/jsonrpc/error.h @@ -0,0 +1,29 @@ +#ifndef WF_JSONRPC_ERROR_H +#define WF_JSONRPC_ERROR_H + +#include +#include "webfuse/core/jsonrpc/proxy_finished_fn.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern json_t * +wf_jsonrpc_error( + int code, + char const * message); + +extern void +wf_jsonrpc_propate_error( + wf_jsonrpc_proxy_finished_fn * finised, + void * user_data, + int code, + char const * message); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/lib/webfuse/core/jsonrpc/method.c b/lib/webfuse/core/jsonrpc/method.c new file mode 100644 index 0000000..a184dca --- /dev/null +++ b/lib/webfuse/core/jsonrpc/method.c @@ -0,0 +1,24 @@ +#include "webfuse/core/jsonrpc/method.h" +#include +#include + +struct wf_jsonrpc_method * wf_jsonrpc_method_create( + char const * method_name, + wf_jsonrpc_method_invoke_fn * invoke, + void * user_data) +{ + struct wf_jsonrpc_method * method = malloc(sizeof(struct wf_jsonrpc_method)); + method->next = NULL; + method->name = strdup(method_name); + method->invoke = invoke; + method->user_data = user_data; + + return method; +} + +void wf_jsonrpc_method_dispose( + struct wf_jsonrpc_method * method) +{ + free(method->name); + free(method); +} diff --git a/lib/webfuse/core/jsonrpc/method.h b/lib/webfuse/core/jsonrpc/method.h new file mode 100644 index 0000000..e196bff --- /dev/null +++ b/lib/webfuse/core/jsonrpc/method.h @@ -0,0 +1,34 @@ +#ifndef WF_JSONRPC_METHOD_H +#define WF_JSONRPC_METHOD_H + +#include "webfuse/core/jsonrpc/method_invoke_fn.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_jsonrpc_method +{ + struct wf_jsonrpc_method * next; + char * name; + wf_jsonrpc_method_invoke_fn * invoke; + void * user_data; +}; + +extern struct wf_jsonrpc_method * +wf_jsonrpc_method_create( + char const * method_name, + wf_jsonrpc_method_invoke_fn * invoke, + void * user_data); + +extern void +wf_jsonrpc_method_dispose( + struct wf_jsonrpc_method * method); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/lib/webfuse/core/jsonrpc/method_invoke_fn.h b/lib/webfuse/core/jsonrpc/method_invoke_fn.h new file mode 100644 index 0000000..046dfb6 --- /dev/null +++ b/lib/webfuse/core/jsonrpc/method_invoke_fn.h @@ -0,0 +1,24 @@ +#ifndef WF_JSONRPC_METHOD_INVOKE_FN_H +#define WF_JSONRPC_METHOD_INVOKE_FN_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_jsonrpc_request; + +typedef void wf_jsonrpc_method_invoke_fn( + struct wf_jsonrpc_request * request, + char const * method_name, + json_t * params, + void * user_data); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/lib/webfuse/core/jsonrpc/proxy.c b/lib/webfuse/core/jsonrpc/proxy.c new file mode 100644 index 0000000..ee5fc0d --- /dev/null +++ b/lib/webfuse/core/jsonrpc/proxy.c @@ -0,0 +1,216 @@ +#include "webfuse/core/jsonrpc/proxy_intern.h" +#include "webfuse/core/jsonrpc/response_intern.h" +#include "webfuse/core/jsonrpc/error.h" +#include "webfuse/core/status.h" + +#include "webfuse/core/timer/timer.h" + +#include +#include + +struct wf_jsonrpc_proxy * +wf_jsonrpc_proxy_create( + struct wf_timer_manager * manager, + int timeout, + wf_jsonrpc_send_fn * send, + void * user_data) +{ + struct wf_jsonrpc_proxy * proxy = malloc(sizeof(struct wf_jsonrpc_proxy)); + wf_jsonrpc_proxy_init(proxy, manager, timeout, send, user_data); + + return proxy; +} + +void wf_jsonrpc_proxy_dispose( + struct wf_jsonrpc_proxy * proxy) +{ + wf_jsonrpc_proxy_cleanup(proxy); + free(proxy); +} + +static void wf_jsonrpc_proxy_on_timeout( + struct wf_timer * timer, void * proxy_ptr) +{ + struct wf_jsonrpc_proxy * proxy = proxy_ptr; + + if (proxy->request.is_pending) + { + wf_jsonrpc_proxy_finished_fn * finished = proxy->request.finished; + void * user_data = proxy->request.user_data; + + proxy->request.is_pending = false; + proxy->request.id = 0; + proxy->request.user_data = NULL; + proxy->request.finished = NULL; + wf_timer_cancel(timer); + + wf_jsonrpc_propate_error(finished, user_data, WF_BAD_TIMEOUT, "Timeout"); + } +} + +static json_t * wf_jsonrpc_request_create( + char const * method, + int id, + char const * param_info, + va_list args) +{ + json_t * request = json_object(); + json_object_set_new(request, "method", json_string(method)); + json_t * params = json_array(); + + for (char const * param_type = param_info; '\0' != *param_type; param_type++) + { + switch(*param_type) + { + case 's': + { + char const * const value = va_arg(args, char const *); + json_array_append_new(params, json_string(value)); + } + break; + case 'i': + { + int const value = va_arg(args, int); + json_array_append_new(params, json_integer(value)); + } + break; + case 'j': + { + json_t * const value = va_arg(args, json_t *); + json_array_append_new(params, value); + } + break; + default: + fprintf(stderr, "fatal: unknown param_type '%c'\n", *param_type); + json_decref(params); + json_decref(request); + return NULL; + } + } + + + json_object_set_new(request, "params", params); + if (0 != id) + { + json_object_set_new(request, "id", json_integer(id)); + } + + return request; +} + +void wf_jsonrpc_proxy_init( + struct wf_jsonrpc_proxy * proxy, + struct wf_timer_manager * timeout_manager, + int timeout, + wf_jsonrpc_send_fn * send, + void * user_data) +{ + proxy->send = send; + proxy->timeout = timeout; + proxy->user_data = user_data; + proxy->request.is_pending = false; + proxy->request.timer = wf_timer_create(timeout_manager, + &wf_jsonrpc_proxy_on_timeout, proxy); +} + +void wf_jsonrpc_proxy_cleanup( + struct wf_jsonrpc_proxy * proxy) +{ + if (proxy->request.is_pending) + { + void * user_data = proxy->request.user_data; + wf_jsonrpc_proxy_finished_fn * finished = proxy->request.finished; + + proxy->request.is_pending = false; + proxy->request.finished = NULL; + proxy->request.user_data = NULL; + proxy->request.id = 0; + wf_timer_cancel(proxy->request.timer); + + wf_jsonrpc_propate_error(finished, user_data, WF_BAD, "Bad: cancelled pending request during shutdown"); + } + + wf_timer_dispose(proxy->request.timer); +} + +void wf_jsonrpc_proxy_vinvoke( + struct wf_jsonrpc_proxy * proxy, + wf_jsonrpc_proxy_finished_fn * finished, + void * user_data, + char const * method_name, + char const * param_info, + va_list args) +{ + if (!proxy->request.is_pending) + { + proxy->request.is_pending = true; + proxy->request.finished = finished; + proxy->request.user_data = user_data; + proxy->request.id = 42; + wf_timer_start(proxy->request.timer, proxy->timeout); + + json_t * request = wf_jsonrpc_request_create(method_name, proxy->request.id, param_info, args); + + bool const is_send = ((NULL != request) && (proxy->send(request, proxy->user_data))); + if (!is_send) + { + proxy->request.is_pending = false; + proxy->request.finished = NULL; + proxy->request.user_data = NULL; + proxy->request.id = 0; + wf_timer_cancel(proxy->request.timer); + + wf_jsonrpc_propate_error(finished, user_data, WF_BAD, "Bad: requenst is not sent"); + } + + if (NULL != request) + { + json_decref(request); + } + } + else + { + wf_jsonrpc_propate_error(finished, user_data, WF_BAD_BUSY, "Busy"); + } +} + +extern void wf_jsonrpc_proxy_vnotify( + struct wf_jsonrpc_proxy * proxy, + char const * method_name, + char const * param_info, + va_list args) +{ + json_t * request = wf_jsonrpc_request_create(method_name, 0, param_info, args); + + if (NULL != request) + { + proxy->send(request, proxy->user_data); + json_decref(request); + } +} + + +void wf_jsonrpc_proxy_onresult( + struct wf_jsonrpc_proxy * proxy, + json_t * message) +{ + struct wf_jsonrpc_response response; + wf_jsonrpc_response_init(&response, message); + + if ((proxy->request.is_pending) && (response.id == proxy->request.id)) + { + wf_jsonrpc_proxy_finished_fn * finished = proxy->request.finished; + void * user_data = proxy->request.user_data; + + proxy->request.is_pending = false; + proxy->request.id = 0; + proxy->request.user_data = NULL; + proxy->request.finished = NULL; + wf_timer_cancel(proxy->request.timer); + + finished(user_data, response.result, response.error); + } + + wf_jsonrpc_response_cleanup(&response); +} + diff --git a/lib/webfuse/core/jsonrpc/proxy.h b/lib/webfuse/core/jsonrpc/proxy.h new file mode 100644 index 0000000..8a69936 --- /dev/null +++ b/lib/webfuse/core/jsonrpc/proxy.h @@ -0,0 +1,74 @@ +#ifndef WF_JSONRPC_PROXY_H +#define WF_JSONRPC_PROXY_H + +#ifndef __cplusplus +#include +#include +#include +#else +#include +#include +using std::size_t; +#endif + +#include +#include "webfuse/core/jsonrpc/send_fn.h" +#include "webfuse/core/jsonrpc/proxy_finished_fn.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct wf_jsonrpc_proxy; +struct wf_timer_manager; + +extern struct wf_jsonrpc_proxy * +wf_jsonrpc_proxy_create( + struct wf_timer_manager * manager, + int timeout, + wf_jsonrpc_send_fn * send, + void * user_data); + +extern void wf_jsonrpc_proxy_dispose( + struct wf_jsonrpc_proxy * proxy); + +//------------------------------------------------------------------------------ +/// \brief Invokes a method. +/// +/// Creates a method an sends it using the send function. +/// Proxy keeps track of method invokation. If no response is returned within +/// timeout, an error is propagated. +/// +/// \param proxy pointer to proxy instance +/// \param finished function which is called exactly once, either on success or +/// on failure. +/// \param method_name name of the method to invoke +/// \param param_info types of the param (s = string, i = integer, j = json) +/// \param ... params +//------------------------------------------------------------------------------ +extern void wf_jsonrpc_proxy_invoke( + struct wf_jsonrpc_proxy * proxy, + wf_jsonrpc_proxy_finished_fn * finished, + void * user_data, + char const * method_name, + char const * param_info, + ... +); + +extern void wf_jsonrpc_proxy_notify( + struct wf_jsonrpc_proxy * proxy, + char const * method_name, + char const * param_info, + ... +); + +extern void wf_jsonrpc_proxy_onresult( + struct wf_jsonrpc_proxy * proxy, + json_t * message); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/lib/webfuse/core/jsonrpc/proxy_finished_fn.h b/lib/webfuse/core/jsonrpc/proxy_finished_fn.h new file mode 100644 index 0000000..cd59c87 --- /dev/null +++ b/lib/webfuse/core/jsonrpc/proxy_finished_fn.h @@ -0,0 +1,20 @@ +#ifndef WF_JSONRPC_PROXY_FINISHED_FN_H +#define WF_JSONRPC_PROXY_FINISHED_FN_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef void wf_jsonrpc_proxy_finished_fn( + void * user_data, + json_t const * result, + json_t const * error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/core/jsonrpc/proxy_intern.h b/lib/webfuse/core/jsonrpc/proxy_intern.h new file mode 100644 index 0000000..fd284a0 --- /dev/null +++ b/lib/webfuse/core/jsonrpc/proxy_intern.h @@ -0,0 +1,62 @@ +#ifndef WF_JSONRPC_PROXY_INTERN_H +#define WF_JSONRPC_PROXY_INTERN_H + +#include "webfuse/core/jsonrpc/proxy.h" +#include "webfuse/core/jsonrpc/proxy_finished_fn.h" +#include "webfuse/core/jsonrpc/send_fn.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_timer; + +struct wf_jsonrpc_request +{ + bool is_pending; + wf_jsonrpc_proxy_finished_fn * finished; + void * user_data; + int id; + struct wf_timer * timer; +}; + +struct wf_jsonrpc_proxy +{ + struct wf_jsonrpc_request request; + int timeout; + wf_jsonrpc_send_fn * send; + void * user_data; +}; + +extern void +wf_jsonrpc_proxy_init( + struct wf_jsonrpc_proxy * proxy, + struct wf_timer_manager * manager, + int timeout, + wf_jsonrpc_send_fn * send, + void * user_data); + +extern void +wf_jsonrpc_proxy_cleanup( + struct wf_jsonrpc_proxy * proxy); + +extern void wf_jsonrpc_proxy_vinvoke( + struct wf_jsonrpc_proxy * proxy, + wf_jsonrpc_proxy_finished_fn * finished, + void * user_data, + char const * method_name, + char const * param_info, + va_list args); + +extern void wf_jsonrpc_proxy_vnotify( + struct wf_jsonrpc_proxy * proxy, + char const * method_name, + char const * param_info, + va_list args); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/core/jsonrpc/proxy_variadic.c b/lib/webfuse/core/jsonrpc/proxy_variadic.c new file mode 100644 index 0000000..2447511 --- /dev/null +++ b/lib/webfuse/core/jsonrpc/proxy_variadic.c @@ -0,0 +1,28 @@ +#include "webfuse/core/jsonrpc/proxy_intern.h" + +void wf_jsonrpc_proxy_invoke( + struct wf_jsonrpc_proxy * proxy, + wf_jsonrpc_proxy_finished_fn * finished, + void * user_data, + char const * method_name, + char const * param_info, + ...) +{ + va_list args; + va_start(args, param_info); + wf_jsonrpc_proxy_vinvoke(proxy, finished, user_data, method_name, param_info, args); + va_end(args); +} + +extern void wf_jsonrpc_proxy_notify( + struct wf_jsonrpc_proxy * proxy, + char const * method_name, + char const * param_info, + ... +) +{ + va_list args; + va_start(args, param_info); + wf_jsonrpc_proxy_vnotify(proxy, method_name, param_info, args); + va_end(args); +} diff --git a/lib/webfuse/core/jsonrpc/request.c b/lib/webfuse/core/jsonrpc/request.c new file mode 100644 index 0000000..30212bd --- /dev/null +++ b/lib/webfuse/core/jsonrpc/request.c @@ -0,0 +1,81 @@ +#include "webfuse/core/jsonrpc/request.h" +#include "webfuse/core/jsonrpc/error.h" +#include + +struct wf_jsonrpc_request +{ + int id; + wf_jsonrpc_send_fn * send; + void * user_data; +}; + +bool +wf_jsonrpc_is_request( + json_t * message) +{ + json_t * id = json_object_get(message, "id"); + json_t * method = json_object_get(message, "method"); + json_t * params = json_object_get(message, "params"); + + return (json_is_integer(id) && json_is_string(method) && + (json_is_array(params) || json_is_object(params))); +} + + +struct wf_jsonrpc_request * +wf_jsonrpc_request_create( + int id, + wf_jsonrpc_send_fn * send, + void * user_data) +{ + struct wf_jsonrpc_request * request = malloc(sizeof(struct wf_jsonrpc_request)); + request->id = id; + request->send = send; + request->user_data = user_data; + + return request; +} + +void +wf_jsonrpc_request_dispose( + struct wf_jsonrpc_request * request) +{ + free(request); +} + +void * +wf_jsonrpc_request_get_userdata( + struct wf_jsonrpc_request * request) +{ + return request->user_data; +} + + +void +wf_jsonrpc_respond( + struct wf_jsonrpc_request * request, + json_t * result) +{ + json_t * response = json_object(); + json_object_set_new(response, "result", result); + json_object_set_new(response, "id", json_integer(request->id)); + + request->send(response, request->user_data); + json_decref(response); + wf_jsonrpc_request_dispose(request); +} + +void wf_jsonrpc_respond_error( + struct wf_jsonrpc_request * request, + int code, + char const * message) +{ + json_t * response = json_object(); + json_object_set_new(response, "error", wf_jsonrpc_error(code, message)); + json_object_set_new(response, "id", json_integer(request->id)); + + request->send(response, request->user_data); + json_decref(response); + wf_jsonrpc_request_dispose(request); +} + diff --git a/lib/webfuse/core/jsonrpc/request.h b/lib/webfuse/core/jsonrpc/request.h new file mode 100644 index 0000000..234ec62 --- /dev/null +++ b/lib/webfuse/core/jsonrpc/request.h @@ -0,0 +1,53 @@ +#ifndef WF_JSONRPC_REQUEST_H +#define WF_JSONRPC_REQUEST_H + +#ifndef __cplusplus +#include +#include +#include +#else +#include +#include +using std::size_t; +#endif + +#include +#include "webfuse/core/jsonrpc/send_fn.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_jsonrpc_request; + +extern bool wf_jsonrpc_is_request( + json_t * message); + +extern struct wf_jsonrpc_request * +wf_jsonrpc_request_create( + int id, + wf_jsonrpc_send_fn * send, + void * user_data); + +extern void wf_jsonrpc_request_dispose( + struct wf_jsonrpc_request * request); + +extern void * wf_jsonrpc_request_get_userdata( + struct wf_jsonrpc_request * request); + +extern void wf_jsonrpc_respond( + struct wf_jsonrpc_request * request, + json_t * result); + +extern void wf_jsonrpc_respond_error( + struct wf_jsonrpc_request * request, + int code, + char const * message); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/core/jsonrpc/response.c b/lib/webfuse/core/jsonrpc/response.c new file mode 100644 index 0000000..69f7033 --- /dev/null +++ b/lib/webfuse/core/jsonrpc/response.c @@ -0,0 +1,68 @@ +#include "webfuse/core/jsonrpc/response_intern.h" +#include "webfuse/core/jsonrpc/error.h" +#include "webfuse/core/status.h" + +bool +wf_jsonrpc_is_response( + json_t * message) +{ + json_t * id = json_object_get(message, "id"); + json_t * err = json_object_get(message, "error"); + json_t * result = json_object_get(message, "result"); + + return (json_is_integer(id) && + (json_is_object(err) || (NULL != result))); +} + + +void +wf_jsonrpc_response_init( + struct wf_jsonrpc_response * result, + json_t * response) +{ + result->id = -1; + result->result = NULL; + result->error = NULL; + + json_t * id_holder = json_object_get(response, "id"); + if (!json_is_integer(id_holder)) + { + result->error = wf_jsonrpc_error(WF_BAD_FORMAT, "invalid format: missing id"); + return; + } + + result->id = json_integer_value(id_holder); + result->result = json_object_get(response, "result"); + if (NULL != result->result) + { + json_incref(result->result); + } + else + { + json_t * error = json_object_get(response, "error"); + if ((json_is_object(error)) && (json_is_integer(json_object_get(error, "code")))) + { + result->error = error; + json_incref(result->error); + } + else + { + result->error = wf_jsonrpc_error(WF_BAD_FORMAT, "invalid format: invalid error object"); + } + } +} + +void +wf_jsonrpc_response_cleanup( + struct wf_jsonrpc_response * response) +{ + if (NULL != response->result) + { + json_decref(response->result); + } + + if (NULL != response->error) + { + json_decref(response->error); + } +} diff --git a/lib/webfuse/core/jsonrpc/response.h b/lib/webfuse/core/jsonrpc/response.h new file mode 100644 index 0000000..0bf0561 --- /dev/null +++ b/lib/webfuse/core/jsonrpc/response.h @@ -0,0 +1,22 @@ +#ifndef WF_JSONRPC_RESPONSE_H +#define WF_JSONRPC_RESPONSE_H + +#ifndef __cplusplus +#include +#endif + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern bool wf_jsonrpc_is_response( + json_t * message); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/core/jsonrpc/response_intern.h b/lib/webfuse/core/jsonrpc/response_intern.h new file mode 100644 index 0000000..137a615 --- /dev/null +++ b/lib/webfuse/core/jsonrpc/response_intern.h @@ -0,0 +1,36 @@ +#ifndef WF_JSONRPC_RESPONSE_INTERN_H +#define WF_JSONRPC_RESPONSE_INTERN_H + +#include "webfuse/core/jsonrpc/response.h" + +#ifndef __cplusplus +#include +#else +#include +using std::size_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct wf_jsonrpc_response +{ + json_t * result; + json_t * error; + int id; +}; + +extern void wf_jsonrpc_response_init( + struct wf_jsonrpc_response * response, + json_t * message); + +extern void wf_jsonrpc_response_cleanup( + struct wf_jsonrpc_response * response); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/lib/webfuse/adapter/impl/jsonrpc/send_fn.h b/lib/webfuse/core/jsonrpc/send_fn.h similarity index 61% rename from lib/webfuse/adapter/impl/jsonrpc/send_fn.h rename to lib/webfuse/core/jsonrpc/send_fn.h index baeedf7..2892687 100644 --- a/lib/webfuse/adapter/impl/jsonrpc/send_fn.h +++ b/lib/webfuse/core/jsonrpc/send_fn.h @@ -1,5 +1,5 @@ -#ifndef WF_ADAPTER_IMPL_JSONRPC_SEND_FN_H -#define WF_ADAPTER_IMPL_JSONRPC_SEND_FN_H +#ifndef WF_JSONRPC_SEND_FN_H +#define WF_JSONRPC_SEND_FN_H #ifndef __cplusplus #include @@ -12,7 +12,7 @@ extern "C" { #endif -typedef bool wf_impl_jsonrpc_send_fn( +typedef bool wf_jsonrpc_send_fn( json_t * request, void * user_data); diff --git a/lib/webfuse/core/jsonrpc/server.c b/lib/webfuse/core/jsonrpc/server.c new file mode 100644 index 0000000..03e30ae --- /dev/null +++ b/lib/webfuse/core/jsonrpc/server.c @@ -0,0 +1,128 @@ +#include "webfuse/core/jsonrpc/server.h" +#include "webfuse/core/jsonrpc/method.h" +#include "webfuse/core/jsonrpc/request.h" +#include "webfuse/core/status.h" +#include "webfuse/core/util.h" + +#include +#include + +struct wf_jsonrpc_server +{ + struct wf_jsonrpc_method * methods; +}; + +static void +wf_jsonrpc_server_init( + struct wf_jsonrpc_server * server); + +static void +wf_jsonrpc_server_cleanup( + struct wf_jsonrpc_server * server); + +struct wf_jsonrpc_server * +wf_jsonrpc_server_create(void) +{ + struct wf_jsonrpc_server * server = malloc(sizeof(struct wf_jsonrpc_server)); + wf_jsonrpc_server_init(server); + + return server; +} + +void +wf_jsonrpc_server_dispose( + struct wf_jsonrpc_server * server) +{ + wf_jsonrpc_server_cleanup(server); + free(server); +} + +static void wf_jsonrpc_server_init( + struct wf_jsonrpc_server * server) +{ + server->methods = NULL; +} + +static void wf_jsonrpc_server_cleanup( + struct wf_jsonrpc_server * server) +{ + struct wf_jsonrpc_method * current = server->methods; + while (NULL != current) + { + struct wf_jsonrpc_method * next = current->next; + wf_jsonrpc_method_dispose(current); + current = next; + } + server->methods = NULL; +} + +void wf_jsonrpc_server_add( + struct wf_jsonrpc_server * server, + char const * method_name, + wf_jsonrpc_method_invoke_fn * invoke, + void * user_data) +{ + struct wf_jsonrpc_method * method = wf_jsonrpc_method_create(method_name, invoke, user_data); + method->next = server->methods; + server->methods = method; +} + +static void wf_jsonrpc_server_invalid_method_invoke( + struct wf_jsonrpc_request * request, + char const * WF_UNUSED_PARAM(method_name), + json_t * WF_UNUSED_PARAM(params), + void * WF_UNUSED_PARAM(user_data)) +{ + wf_jsonrpc_respond_error(request, WF_BAD_NOTIMPLEMENTED, "not implemented"); +} + +static struct wf_jsonrpc_method const wf_jsonrpc_server_invalid_method = +{ + .next = NULL, + .name = "", + .invoke = &wf_jsonrpc_server_invalid_method_invoke, + .user_data = NULL +}; + +static struct wf_jsonrpc_method const * +wf_jsonrpc_server_get_method( + struct wf_jsonrpc_server * server, + char const * method_name) +{ + struct wf_jsonrpc_method const * current = server->methods; + while (NULL != current) + { + if (0 == strcmp(method_name, current->name)) + { + return current; + } + + current = current->next; + } + + return &wf_jsonrpc_server_invalid_method; +} + +void wf_jsonrpc_server_process( + struct wf_jsonrpc_server * server, + json_t * request_data, + wf_jsonrpc_send_fn * send, + void * user_data) +{ + json_t * method_holder = json_object_get(request_data, "method"); + json_t * params = json_object_get(request_data, "params"); + json_t * id_holder = json_object_get(request_data, "id"); + + if (json_is_string(method_holder) && + (json_is_array(params) || (json_is_object(params))) && + json_is_integer(id_holder)) + { + char const * method_name = json_string_value(method_holder); + int id = json_integer_value(id_holder); + struct wf_jsonrpc_request * request = wf_jsonrpc_request_create(id, send, user_data); + struct wf_jsonrpc_method const * method = wf_jsonrpc_server_get_method(server, method_name); + + method->invoke(request, method_name, params, method->user_data); + } +} + diff --git a/lib/webfuse/core/jsonrpc/server.h b/lib/webfuse/core/jsonrpc/server.h new file mode 100644 index 0000000..143f121 --- /dev/null +++ b/lib/webfuse/core/jsonrpc/server.h @@ -0,0 +1,45 @@ +#ifndef WF_JSONRPC_SERVER_H +#define WF_JSONRPC_SERVER_H + +#ifndef __cplusplus +#include +#include +#else +#include +#endif + +#include +#include "webfuse/core/jsonrpc/method_invoke_fn.h" +#include "webfuse/core/jsonrpc/send_fn.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_jsonrpc_server; + +extern struct wf_jsonrpc_server * +wf_jsonrpc_server_create(void); + +extern void +wf_jsonrpc_server_dispose( + struct wf_jsonrpc_server * server); + +extern void wf_jsonrpc_server_add( + struct wf_jsonrpc_server * server, + char const * method_name, + wf_jsonrpc_method_invoke_fn * invoke, + void * user_data); + +extern void wf_jsonrpc_server_process( + struct wf_jsonrpc_server * server, + json_t * request, + wf_jsonrpc_send_fn * send, + void * user_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/core/message.c b/lib/webfuse/core/message.c index e94d1c6..bfb0016 100644 --- a/lib/webfuse/core/message.c +++ b/lib/webfuse/core/message.c @@ -12,13 +12,10 @@ extern struct wf_message * wf_message_create(json_t const * value) { char * data = malloc(sizeof(struct wf_message) + LWS_PRE + length); message = (struct wf_message *) data; - if (NULL != message) - { - message->data = &data[sizeof(struct wf_message) + LWS_PRE]; - message->length = length; + message->data = &data[sizeof(struct wf_message) + LWS_PRE]; + message->length = length; - json_dumpb(value, message->data, length, JSON_COMPACT); - } + json_dumpb(value, message->data, length, JSON_COMPACT); } return message; diff --git a/lib/webfuse/core/string.c b/lib/webfuse/core/string.c index c5117eb..84608cb 100644 --- a/lib/webfuse/core/string.c +++ b/lib/webfuse/core/string.c @@ -16,18 +16,15 @@ char * wf_create_string(char const * format, ...) if (0 <= needed) { result = malloc(needed + 1); - if (NULL != result) - { - va_list args; - va_start(args, format); - int count = vsnprintf(result, needed + 1, format, args); - va_end(args); + 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; - } + if ((count < 0) || (needed < count)) + { + free(result); + result = NULL; } } diff --git a/lib/webfuse/core/timer/manager.c b/lib/webfuse/core/timer/manager.c new file mode 100644 index 0000000..99540e9 --- /dev/null +++ b/lib/webfuse/core/timer/manager.c @@ -0,0 +1,93 @@ +#include "webfuse/core/timer/manager_intern.h" +#include "webfuse/core/timer/timer_intern.h" +#include "webfuse/core/timer/timepoint.h" + +#include +#include + +struct wf_timer_manager +{ + struct wf_timer * timers; +}; + +struct wf_timer_manager * +wf_timer_manager_create(void) +{ + struct wf_timer_manager * manager = malloc(sizeof(struct wf_timer_manager)); + manager->timers = NULL; + + return manager; +} + +void +wf_timer_manager_dispose( + struct wf_timer_manager * manager) +{ + struct wf_timer * timer = manager->timers; + while (NULL != timer) + { + struct wf_timer * next = timer->next; + + wf_timer_trigger(timer); + timer = next; + } + + free(manager); +} + + +void wf_timer_manager_check( + struct wf_timer_manager * manager) +{ + struct wf_timer * timer = manager->timers; + while (NULL != timer) + { + struct wf_timer * next = timer->next; + + if (wf_timer_is_timeout(timer)) + { + wf_timer_manager_removetimer(manager, timer); + wf_timer_trigger(timer); + } + + timer = next; + } +} + +void wf_timer_manager_addtimer( + struct wf_timer_manager * manager, + struct wf_timer * timer) +{ + if (NULL != manager->timers) + { + manager->timers->prev = timer; + } + + timer->next = manager->timers; + timer->prev = NULL; + manager->timers = timer; +} + +void wf_timer_manager_removetimer( + struct wf_timer_manager * manager, + struct wf_timer * timer) +{ + struct wf_timer * prev = timer->prev; + struct wf_timer * next = timer->next; + + if (NULL != prev) + { + prev->next = next; + } + + if (NULL != next) + { + next->prev = prev; + } + + if (manager->timers == timer) + { + manager->timers = next; + } +} + diff --git a/lib/webfuse/core/timer/manager.h b/lib/webfuse/core/timer/manager.h new file mode 100644 index 0000000..870a2d0 --- /dev/null +++ b/lib/webfuse/core/timer/manager.h @@ -0,0 +1,27 @@ +#ifndef WF_TIMER_MANAGER_H +#define WF_TIMER_MANAGER_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_timer_manager; + +extern struct wf_timer_manager * +wf_timer_manager_create(void); + +extern void +wf_timer_manager_dispose( + struct wf_timer_manager * manager); + +extern void +wf_timer_manager_check( + struct wf_timer_manager * manager); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/lib/webfuse/core/timer/manager_intern.h b/lib/webfuse/core/timer/manager_intern.h new file mode 100644 index 0000000..473df2b --- /dev/null +++ b/lib/webfuse/core/timer/manager_intern.h @@ -0,0 +1,26 @@ +#ifndef WF_TIMER_MANAGER_INTERN_H +#define WF_TIMER_MANAGER_INTERN_H + +#include "webfuse/core/timer/manager.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_timer; + +extern void wf_timer_manager_addtimer( + struct wf_timer_manager * manager, + struct wf_timer * timer); + +extern void wf_timer_manager_removetimer( + struct wf_timer_manager * manager, + struct wf_timer * timer); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/lib/webfuse/core/timer/on_timer_fn.h b/lib/webfuse/core/timer/on_timer_fn.h new file mode 100644 index 0000000..d250bae --- /dev/null +++ b/lib/webfuse/core/timer/on_timer_fn.h @@ -0,0 +1,19 @@ +#ifndef WF_TIMER_ON_TIMER_FN_H +#define WF_TIMER_ON_TIMER_FN_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_timer; + +typedef void wf_timer_on_timer_fn( + struct wf_timer * timer, + void * user_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/core/timer/timepoint.c b/lib/webfuse/core/timer/timepoint.c new file mode 100644 index 0000000..09fc025 --- /dev/null +++ b/lib/webfuse/core/timer/timepoint.c @@ -0,0 +1,31 @@ +#include "webfuse/core/timer/timepoint.h" + +#include + +#define WF_TIMER_MSEC_PER_SEC ((wf_timer_timepoint) 1000) +#define WF_TIMER_NSEC_PER_MSEC ((wf_timer_timepoint) 1000 * 1000) + +wf_timer_timepoint wf_timer_timepoint_now(void) +{ + struct timespec tp; + clock_gettime(CLOCK_MONOTONIC, &tp); + + wf_timer_timepoint const now = (tp.tv_sec * WF_TIMER_MSEC_PER_SEC) + (tp.tv_nsec / WF_TIMER_NSEC_PER_MSEC); + return now; +} + +wf_timer_timepoint wf_timer_timepoint_in_msec(wf_timer_timediff value) +{ + wf_timer_timepoint const now = wf_timer_timepoint_now(); + wf_timer_timepoint result = now + ((wf_timer_timepoint) value); + + return result; +} + +bool wf_timer_timepoint_is_elapsed(wf_timer_timepoint tp) +{ + wf_timer_timepoint const now = wf_timer_timepoint_now(); + wf_timer_timediff const diff = (wf_timer_timediff) (tp - now); + + return (0 > diff); +} diff --git a/lib/webfuse/core/timer/timepoint.h b/lib/webfuse/core/timer/timepoint.h new file mode 100644 index 0000000..cb78197 --- /dev/null +++ b/lib/webfuse/core/timer/timepoint.h @@ -0,0 +1,31 @@ +#ifndef WF_TIMER_TIMEPOINT_H +#define WF_TIMER_TIMEPOINT_H + +#ifndef __cplusplus +#include +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef uint64_t wf_timer_timepoint; +typedef int64_t wf_timer_timediff; + +extern wf_timer_timepoint wf_timer_timepoint_now(void); + +extern wf_timer_timepoint wf_timer_timepoint_in_msec( + wf_timer_timediff value); + +extern bool wf_timer_timepoint_is_elapsed( + wf_timer_timepoint timepoint); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/core/timer/timer.c b/lib/webfuse/core/timer/timer.c new file mode 100644 index 0000000..1f4d650 --- /dev/null +++ b/lib/webfuse/core/timer/timer.c @@ -0,0 +1,67 @@ +#include "webfuse/core/timer/timer_intern.h" +#include "webfuse/core/timer/manager_intern.h" +#include "webfuse/core/timer/timepoint.h" + +#include +#include +#include + +struct wf_timer * +wf_timer_create( + struct wf_timer_manager * manager, + wf_timer_on_timer_fn * on_timer, + void * user_data) +{ + struct wf_timer * timer = malloc(sizeof(struct wf_timer)); + timer->manager = manager; + timer->timeout = 0; + timer->on_timer = on_timer; + timer->user_data = user_data; + timer->prev = NULL; + timer->next = NULL; + + return timer; +} + +void +wf_timer_dispose( + struct wf_timer * timer) +{ + free(timer); +} + +void wf_timer_start( + struct wf_timer * timer, + int timeout_ms) +{ + timer->timeout = wf_timer_timepoint_in_msec(timeout_ms); + + wf_timer_manager_addtimer(timer->manager, timer); +} + +void wf_timer_cancel( + struct wf_timer * timer) +{ + wf_timer_manager_removetimer(timer->manager, timer); + + timer->timeout = 0; +} + +bool wf_timer_is_timeout( + struct wf_timer * timer) +{ + return wf_timer_timepoint_is_elapsed(timer->timeout); +} + + +void wf_timer_trigger( + struct wf_timer * timer) +{ + if (0 != timer->on_timer) + { + timer->prev = NULL; + timer->next = NULL; + + timer->on_timer(timer, timer->user_data); + } +} diff --git a/lib/webfuse/core/timer/timer.h b/lib/webfuse/core/timer/timer.h new file mode 100644 index 0000000..48149bd --- /dev/null +++ b/lib/webfuse/core/timer/timer.h @@ -0,0 +1,37 @@ +#ifndef WF_TIMER_TIMER_H +#define WF_TIMER_TIMER_H + +#include "webfuse/core/timer/on_timer_fn.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_timer; +struct wf_timer_manager; + +extern struct wf_timer * +wf_timer_create( + struct wf_timer_manager * manager, + wf_timer_on_timer_fn * on_timer, + void * user_data); + +extern void +wf_timer_dispose( + struct wf_timer * timer); + +extern void +wf_timer_start( + struct wf_timer * timer, + int timeout_ms); + +extern void +wf_timer_cancel( + struct wf_timer * timer); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/core/timer/timer_intern.h b/lib/webfuse/core/timer/timer_intern.h new file mode 100644 index 0000000..c910a3b --- /dev/null +++ b/lib/webfuse/core/timer/timer_intern.h @@ -0,0 +1,40 @@ +#ifndef WF_TIMER_TIMER_H +#define WF_TIMER_TIMER_H + +#include "webfuse/core/timer/timer.h" +#include "webfuse/core/timer/on_timer_fn.h" +#include "webfuse/core/timer/timepoint.h" + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_timer +{ + struct wf_timer_manager * manager; + wf_timer_timepoint timeout; + wf_timer_on_timer_fn * on_timer; + void * user_data; + struct wf_timer * next; + struct wf_timer * prev; +}; + +extern bool wf_timer_is_timeout( + struct wf_timer * timer); + +extern void wf_timer_trigger( + struct wf_timer * timer); + + +#ifdef __cplusplus +} +#endif + + + +#endif diff --git a/lib/webfuse/provider/api.c b/lib/webfuse/provider/api.c index c5caa21..ad288bd 100644 --- a/lib/webfuse/provider/api.c +++ b/lib/webfuse/provider/api.c @@ -11,7 +11,9 @@ #include "webfuse/provider/impl/client_config.h" #include "webfuse/provider/impl/client.h" #include "webfuse/provider/impl/dirbuffer.h" -#include "webfuse/provider/impl/static_filesystem.h" +#include "webfuse/provider/impl/credentials.h" + +#include "webfuse/core/util.h" // respond @@ -93,6 +95,13 @@ void wfp_client_config_set_certpath( wfp_impl_client_config_set_certpath(config, cert_path); } +void wfp_client_config_set_ca_filepath( + struct wfp_client_config * config, + char const * ca_filepath) +{ + wfp_impl_client_config_set_ca_filepath(config, ca_filepath); +} + void wfp_client_config_set_onconnected( struct wfp_client_config * config, wfp_connected_fn * handler) @@ -107,13 +116,6 @@ void wfp_client_config_set_ondisconnected( wfp_impl_client_config_set_ondisconnected(config, handler); } -void wfp_client_config_set_ontimer( - struct wfp_client_config * config, - wfp_ontimer_fn * handler) -{ - wfp_impl_client_config_set_ontimer(config, handler); -} - void wfp_client_config_set_onlookup( struct wfp_client_config * config, wfp_lookup_fn * handler) @@ -156,14 +158,20 @@ void wfp_client_config_set_onread( wfp_impl_client_config_set_onread(config, handler); } +void wfp_client_config_enable_authentication( + struct wfp_client_config * config, + wfp_get_credentials_fn * get_credentials) +{ + wfp_impl_client_config_enable_authentication(config, get_credentials); +} + // protocol struct wfp_client_protocol * wfp_client_protocol_create( - struct wfp_provider const * provider, - void * user_data) + struct wfp_client_config const * config) { - return wfp_impl_client_protocol_create(provider, user_data); + return wfp_impl_client_protocol_create(config); } void wfp_client_protocol_dispose( @@ -179,6 +187,21 @@ void wfp_client_protocol_init_lws( wfp_impl_client_protocol_init_lws(protocol, lws_protocol); } +void wfp_client_protocol_connect( + struct wfp_client_protocol * protocol, + struct lws_context * context, + char const * url) +{ + wfp_impl_client_protocol_connect(protocol, context, url); +} + +void wfp_client_protocol_disconnect( + struct wfp_client_protocol * protocol) +{ + wfp_impl_client_protocol_disconnect(protocol); +} + + // client struct wfp_client * wfp_client_create( @@ -207,12 +230,18 @@ void wfp_client_dispose( } void wfp_client_service( - struct wfp_client * client, - int timeout_ms) + struct wfp_client * client) { - wfp_impl_client_service(client, timeout_ms); + wfp_impl_client_service(client); } +void wfp_client_interrupt( + struct wfp_client * client) +{ + wfp_impl_client_interrupt(client); +} + + // dirbuffer struct wfp_dirbuffer * wfp_dirbuffer_create(void) @@ -234,59 +263,19 @@ void wfp_dirbuffer_add( wfp_impl_dirbuffer_add(buffer, name, inode); } -// static_filesystem +// credentials -struct wfp_static_filesystem * -wfp_static_filesystem_create( - struct wfp_client_config * config) +void wfp_credentials_set_type( + struct wfp_credentials * credentials, + char const * type) { - return wfp_impl_static_filesystem_create(config); + wfp_impl_credentials_set_type(credentials, type); } -void -wfp_static_filesystem_dispose( - struct wfp_static_filesystem * filesystem) +void wfp_credentials_add( + struct wfp_credentials * credentials, + char const * key, + char const * value) { - wfp_impl_static_filesystem_dispose(filesystem); -} - -void -wfp_static_filesystem_add( - struct wfp_static_filesystem * filesystem, - char const * path, - int mode, - char const * content, - size_t length) -{ - wfp_impl_static_filesystem_add(filesystem, path, mode, content, length); -} - -void -wfp_static_filesystem_add_text( - struct wfp_static_filesystem * filesystem, - char const * path, - int mode, - char const * content) -{ - wfp_impl_static_filesystem_add_text(filesystem, path, mode, content); -} - -void -wfp_static_filesystem_add_file( - struct wfp_static_filesystem * filesystem, - char const * path, - char const * filename) -{ - wfp_impl_static_filesystem_add_file(filesystem, path, filename); -} - -void -wfp_static_filesystem_add_generic( - struct wfp_static_filesystem * filesystem, - char const * path, - wfp_static_filesystem_read_fn * read, - wfp_static_filesystem_get_info_fn * get_info, - void * user_data) -{ - wfp_impl_static_filesystem_add_generic(filesystem, path, read, get_info, user_data); + wfp_impl_credentials_add(credentials, key, value); } diff --git a/lib/webfuse/provider/impl/client.c b/lib/webfuse/provider/impl/client.c index b88207a..e7c6e81 100644 --- a/lib/webfuse/provider/impl/client.c +++ b/lib/webfuse/provider/impl/client.c @@ -9,10 +9,8 @@ #include "webfuse/provider/impl/provider.h" #include "webfuse/provider/impl/client_protocol.h" #include "webfuse/provider/impl/client_config.h" -#include "webfuse/provider/impl/url.h" #include "webfuse/core/lws_log.h" -#define WFP_PROTOCOL ("fs") #define WFP_CLIENT_PROTOCOL_COUNT 2 struct wfp_client @@ -32,26 +30,32 @@ struct wfp_client * wfp_impl_client_create( wf_lwslog_disable(); struct wfp_client * client = malloc(sizeof(struct wfp_client)); - if (NULL != client) + wfp_impl_client_protocol_init(&client->protocol, &config->provider, config->user_data); + + memset(client->protocols, 0, sizeof(struct lws_protocols) * WFP_CLIENT_PROTOCOL_COUNT); + wfp_impl_client_protocol_init_lws(&client->protocol, &client->protocols[0]); + + memset(&client->info, 0, sizeof(struct lws_context_creation_info)); + client->info.port = CONTEXT_PORT_NO_LISTEN; + client->info.protocols = client->protocols; + client->info.uid = -1; + client->info.gid = -1; + + if ((NULL != config->cert_path) && (NULL != config->key_path)) { - wfp_impl_client_protocol_init(&client->protocol, &config->provider, config->user_data); + client->info.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS; + } - memset(client->protocols, 0, sizeof(struct lws_protocols) * WFP_CLIENT_PROTOCOL_COUNT); - client->protocols[0].name = "fs"; - wfp_impl_client_protocol_init_lws(&client->protocol, &client->protocols[0]); + client->context = lws_create_context(&client->info); - memset(&client->info, 0, sizeof(struct lws_context_creation_info)); - client->info.port = CONTEXT_PORT_NO_LISTEN; - client->info.protocols = client->protocols; - client->info.uid = -1; - client->info.gid = -1; - - if ((NULL != config->cert_path) && (NULL != config->key_path)) - { - - } - - client->context = lws_create_context(&client->info); + if ((NULL != config->cert_path) && (NULL != config->key_path)) + { + struct lws_vhost * vhost = lws_create_vhost(client->context, &client->info); + client->info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + client->info.client_ssl_cert_filepath = config->cert_path; + client->info.client_ssl_private_key_filepath = config->key_path; + client->info.client_ssl_ca_filepath = config->ca_filepath; + lws_init_vhost_client_ssl(&client->info, vhost); } return client; @@ -69,34 +73,13 @@ void wfp_impl_client_connect( struct wfp_client * client, char const * url) { - struct wfp_impl_url url_data; - bool const success = wfp_impl_url_init(&url_data, url); - if (success) - { - struct lws_client_connect_info info; - memset(&info, 0, sizeof(struct lws_client_connect_info)); - info.context = client->context; - info.port = url_data.port; - info.address = url_data.host; - info.path = url_data.path; - info.host = info.address; - info.origin = info.address; - info.ssl_connection = (url_data.use_tls) ? LCCSCF_USE_SSL : 0; - info.protocol = WFP_PROTOCOL; - info.pwsi = &client->protocol.wsi; - - lws_client_connect_via_info(&info); - - wfp_impl_url_cleanup(&url_data); - } + wfp_impl_client_protocol_connect(&client->protocol, client->context, url); } void wfp_impl_client_disconnect( struct wfp_client * client) { - (void) client; - - // ToDo: implement me + wfp_impl_client_protocol_disconnect(&client->protocol); } bool wfp_impl_client_is_connected( @@ -106,9 +89,13 @@ bool wfp_impl_client_is_connected( } void wfp_impl_client_service( - struct wfp_client * client, - int timeout_ms) + struct wfp_client * client) { - lws_service(client->context, timeout_ms); + lws_service(client->context, 0); } +void wfp_impl_client_interrupt( + struct wfp_client * client) +{ + lws_cancel_service(client->context); +} diff --git a/lib/webfuse/provider/impl/client.h b/lib/webfuse/provider/impl/client.h index 4ade5f2..46e41f9 100644 --- a/lib/webfuse/provider/impl/client.h +++ b/lib/webfuse/provider/impl/client.h @@ -38,8 +38,10 @@ extern bool wfp_impl_client_is_connected( struct wfp_client * client); extern void wfp_impl_client_service( - struct wfp_client * client, - int timeout_ms); + struct wfp_client * client); + +extern void wfp_impl_client_interrupt( + struct wfp_client * client); #ifdef __cplusplus } diff --git a/lib/webfuse/provider/impl/client_config.c b/lib/webfuse/provider/impl/client_config.c index 7d121da..cd7b015 100644 --- a/lib/webfuse/provider/impl/client_config.c +++ b/lib/webfuse/provider/impl/client_config.c @@ -6,13 +6,11 @@ struct wfp_client_config * wfp_impl_client_config_create(void) { struct wfp_client_config * config = malloc(sizeof(struct wfp_client_config)); - if (NULL != config) - { - wfp_impl_provider_init(&config->provider); - config->user_data = NULL; - config->key_path = NULL; - config->cert_path = NULL; - } + wfp_impl_provider_init(&config->provider); + config->user_data = NULL; + config->key_path = NULL; + config->cert_path = NULL; + config->ca_filepath = NULL; return config; } @@ -22,6 +20,7 @@ void wfp_impl_client_config_dispose( { free(config->key_path); free(config->cert_path); + free(config->ca_filepath); free(config); } @@ -48,6 +47,14 @@ void wfp_impl_client_config_set_certpath( config->cert_path = strdup(cert_path); } +void wfp_impl_client_config_set_ca_filepath( + struct wfp_client_config * config, + char const * ca_filepath) +{ + free(config->ca_filepath); + config->ca_filepath = strdup(ca_filepath); +} + void wfp_impl_client_config_set_onconnected( struct wfp_client_config * config, wfp_connected_fn * handler) @@ -62,13 +69,6 @@ void wfp_impl_client_config_set_ondisconnected( config->provider.disconnected = handler; } -void wfp_impl_client_config_set_ontimer( - struct wfp_client_config * config, - wfp_ontimer_fn * handler) -{ - config->provider.ontimer = handler; -} - void wfp_impl_client_config_set_onlookup( struct wfp_client_config * config, wfp_lookup_fn * handler) @@ -110,3 +110,10 @@ void wfp_impl_client_config_set_onread( { config->provider.read = handler; } + +void wfp_impl_client_config_enable_authentication( + struct wfp_client_config * config, + wfp_get_credentials_fn * get_credentials) +{ + config->provider.get_credentials = get_credentials; +} diff --git a/lib/webfuse/provider/impl/client_config.h b/lib/webfuse/provider/impl/client_config.h index 190a3cb..9e588b0 100644 --- a/lib/webfuse/provider/impl/client_config.h +++ b/lib/webfuse/provider/impl/client_config.h @@ -15,6 +15,7 @@ struct wfp_client_config void * user_data; char * key_path; char * cert_path; + char * ca_filepath; }; extern struct wfp_client_config * wfp_impl_client_config_create(void); @@ -34,6 +35,10 @@ extern void wfp_impl_client_config_set_certpath( struct wfp_client_config * config, char const * cert_path); +extern void wfp_impl_client_config_set_ca_filepath( + struct wfp_client_config * config, + char const * ca_filepath); + extern void wfp_impl_client_config_set_onconnected( struct wfp_client_config * config, wfp_connected_fn * handler); @@ -42,10 +47,6 @@ extern void wfp_impl_client_config_set_ondisconnected( struct wfp_client_config * config, wfp_disconnected_fn * handler); -extern void wfp_impl_client_config_set_ontimer( - struct wfp_client_config * config, - wfp_ontimer_fn * handler); - extern void wfp_impl_client_config_set_onlookup( struct wfp_client_config * config, wfp_lookup_fn * handler); @@ -70,6 +71,10 @@ extern void wfp_impl_client_config_set_onread( struct wfp_client_config * config, wfp_read_fn * handler); +extern void wfp_impl_client_config_enable_authentication( + struct wfp_client_config * config, + wfp_get_credentials_fn * get_credentials); + #ifdef __cplusplus } #endif diff --git a/lib/webfuse/provider/impl/client_protocol.c b/lib/webfuse/provider/impl/client_protocol.c index cf31a98..db78137 100644 --- a/lib/webfuse/provider/impl/client_protocol.c +++ b/lib/webfuse/provider/impl/client_protocol.c @@ -6,12 +6,23 @@ #include #include - +#include "webfuse/provider/impl/client_config.h" #include "webfuse/provider/impl/provider.h" +#include "webfuse/provider/impl/credentials.h" #include "webfuse/core/util.h" #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/protocol_names.h" + +#include "webfuse/core/timer/manager.h" + +#include "webfuse/core/jsonrpc/response.h" +#include "webfuse/core/jsonrpc/request.h" +#include "webfuse/core/jsonrpc/proxy.h" + +#define WF_DEFAULT_TIMEOUT (10 * 1000) static void wfp_impl_client_protocol_respond( json_t * response, @@ -27,54 +38,122 @@ static void wfp_impl_client_protocol_respond( } } -static void wfp_impl_client_protocol_process_request( +static void wfp_impl_client_protocol_process( struct wfp_client_protocol * protocol, - char const * message, + char const * data, size_t length) { - json_t * request = json_loadb(message, length, 0, NULL); - if (NULL != request) + json_t * message = json_loadb(data, length, 0, NULL); + if (NULL != message) { - // FIXME: is_connected should be invoked, when filesystem added - if ((!protocol->is_connected) && (NULL != json_object_get(request, "result"))) + if (wf_jsonrpc_is_response(message)) { - protocol->is_connected = true; - protocol->provider.connected(protocol->user_data); + wf_jsonrpc_proxy_onresult(protocol->proxy, message); } - - - struct wfp_impl_invokation_context context = + if (wf_jsonrpc_is_request(message)) { - .provider = &protocol->provider, - .user_data = protocol->user_data, - .request = &protocol->request - }; + struct wfp_impl_invokation_context context = + { + .provider = &protocol->provider, + .user_data = protocol->user_data, + .request = &protocol->request + }; - wfp_impl_provider_invoke(&context, request); - json_decref(request); + wfp_impl_provider_invoke(&context, message); + } + + json_decref(message); } } +static void +wfp_impl_client_protocol_on_add_filesystem_finished( + void * user_data, + json_t const * result, + json_t const * WF_UNUSED_PARAM(error)) +{ + struct wfp_client_protocol * protocol = user_data; + if (NULL == protocol->wsi) { return; } + + if (NULL != result) + { + protocol->is_connected = true; + protocol->provider.connected(protocol->user_data); + } + else + { + protocol->is_shutdown_requested = true; + lws_callback_on_writable(protocol->wsi); + } +} + static void wfp_impl_client_protocol_add_filesystem( struct wfp_client_protocol * protocol) { - json_t * params = json_array(); - json_array_append_new(params, json_string("cprovider")); + wf_jsonrpc_proxy_invoke( + protocol->proxy, + &wfp_impl_client_protocol_on_add_filesystem_finished, + protocol, + "add_filesystem", + "s", + "cprovider"); +} - json_t * request = json_object(); - json_object_set_new(request, "method", json_string("add_filesystem")); - json_object_set_new(request, "params", params); - json_object_set_new(request, "id", json_integer(42)); +static void +wfp_impl_client_protocol_on_authenticate_finished( + void * user_data, + json_t const * result, + json_t const * WF_UNUSED_PARAM(error)) +{ + struct wfp_client_protocol * protocol = user_data; + if (NULL == protocol->wsi) { return; } - struct wf_message * message = wf_message_create(request); - if (NULL != message) + if (NULL != result) { - wf_slist_append(&protocol->messages, &message->item); - lws_callback_on_writable(protocol->wsi); + wfp_impl_client_protocol_add_filesystem(protocol); } + else + { + protocol->is_shutdown_requested = true; + lws_callback_on_writable(protocol->wsi); + } +} - json_decref(request); +static void wfp_impl_client_protocol_authenticate( + struct wfp_client_protocol * protocol) +{ + struct wfp_credentials credentials; + wfp_impl_credentials_init(&credentials); + + protocol->provider.get_credentials(&credentials, protocol->user_data); + + char const * cred_type = wfp_impl_credentials_get_type(&credentials); + json_t * creds = wfp_impl_credentials_get(&credentials); + json_incref(creds); + + wf_jsonrpc_proxy_invoke( + protocol->proxy, + &wfp_impl_client_protocol_on_authenticate_finished, + protocol, + "authenticate", + "sj", + cred_type, creds); + + wfp_impl_credentials_cleanup(&credentials); +} + +static void wfp_impl_client_protocol_handshake( + struct wfp_client_protocol * protocol) +{ + if (wfp_impl_provider_is_authentication_enabled(&protocol->provider)) + { + wfp_impl_client_protocol_authenticate(protocol); + } + else + { + wfp_impl_client_protocol_add_filesystem(protocol); + } } static int wfp_impl_client_protocol_callback( @@ -84,16 +163,18 @@ static int wfp_impl_client_protocol_callback( void * in, size_t len) { + int result = 0; struct lws_protocols const * ws_protocol = lws_get_protocol(wsi); struct wfp_client_protocol * protocol = (NULL != ws_protocol) ? ws_protocol->user: NULL; if (NULL != protocol) { + wf_timer_manager_check(protocol->timer_manager); + switch (reason) { case LWS_CALLBACK_CLIENT_ESTABLISHED: - wfp_impl_client_protocol_add_filesystem(protocol); - // Defer is_connected until response received + wfp_impl_client_protocol_handshake(protocol); break; case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: protocol->is_connected = false; @@ -101,36 +182,60 @@ static int wfp_impl_client_protocol_callback( break; case LWS_CALLBACK_CLIENT_CLOSED: protocol->is_connected = false; - protocol->provider.disconnected(protocol->user_data); + protocol->provider.disconnected(protocol->user_data); + protocol->wsi = NULL; break; case LWS_CALLBACK_CLIENT_RECEIVE: - wfp_impl_client_protocol_process_request(protocol, in, len); + wfp_impl_client_protocol_process(protocol, in, len); break; case LWS_CALLBACK_SERVER_WRITEABLE: // fall-through case LWS_CALLBACK_CLIENT_WRITEABLE: - if ((wsi == protocol->wsi) && (!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)) + if (wsi == protocol->wsi) + { + if (protocol->is_shutdown_requested) { - lws_callback_on_writable(wsi); - + 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; default: break; } } - return 0; + return result; } +static bool wfp_impl_client_protocol_send( + json_t * request, + void * user_data) +{ + bool result = false; + struct wfp_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; +} void wfp_impl_client_protocol_init( struct wfp_client_protocol * protocol, @@ -138,6 +243,7 @@ void wfp_impl_client_protocol_init( void * user_data) { protocol->is_connected = false; + protocol->is_shutdown_requested = false; wf_slist_init(&protocol->messages); protocol->wsi = NULL; @@ -145,6 +251,9 @@ void wfp_impl_client_protocol_init( protocol->request.respond = &wfp_impl_client_protocol_respond; protocol->request.user_data = protocol; + protocol->timer_manager = wf_timer_manager_create(); + protocol->proxy = wf_jsonrpc_proxy_create(protocol->timer_manager, WF_DEFAULT_TIMEOUT, &wfp_impl_client_protocol_send, protocol); + protocol->user_data = user_data; wfp_impl_provider_init_from_prototype(&protocol->provider, provider); } @@ -152,18 +261,16 @@ void wfp_impl_client_protocol_init( void wfp_impl_client_protocol_cleanup( struct wfp_client_protocol * protocol) { + wf_jsonrpc_proxy_dispose(protocol->proxy); + wf_timer_manager_dispose(protocol->timer_manager); wf_message_queue_cleanup(&protocol->messages); } struct wfp_client_protocol * wfp_impl_client_protocol_create( - struct wfp_provider const * provider, - void * user_data) + struct wfp_client_config const * config) { struct wfp_client_protocol * protocol = malloc(sizeof(struct wfp_client_protocol)); - if (NULL != protocol) - { - wfp_impl_client_protocol_init(protocol, provider, user_data); - } + wfp_impl_client_protocol_init(protocol, &config->provider, config->user_data); return protocol; } @@ -179,7 +286,55 @@ void wfp_impl_client_protocol_init_lws( struct wfp_client_protocol * protocol, struct lws_protocols * lws_protocol) { + lws_protocol->name = WF_PROTOCOL_NAME_PROVIDER_CLIENT; lws_protocol->callback = &wfp_impl_client_protocol_callback; lws_protocol->per_session_data_size = 0; lws_protocol->user = protocol; } + +void wfp_impl_client_protocol_connect( + struct wfp_client_protocol * protocol, + struct lws_context * context, + char const * url) +{ + struct wfp_impl_url url_data; + bool const success = wfp_impl_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_ADAPTER_SERVER; + info.local_protocol_name = WF_PROTOCOL_NAME_PROVIDER_CLIENT; + info.pwsi = &protocol->wsi; + + lws_client_connect_via_info(&info); + + wfp_impl_url_cleanup(&url_data); + } + else + { + protocol->provider.disconnected(protocol->user_data); + } + +} + +void wfp_impl_client_protocol_disconnect( + struct wfp_client_protocol * protocol) +{ + if (protocol->is_connected) + { + protocol->is_shutdown_requested = true; + lws_callback_on_writable(protocol->wsi); + } + else + { + protocol->provider.disconnected(protocol->user_data); + } +} diff --git a/lib/webfuse/provider/impl/client_protocol.h b/lib/webfuse/provider/impl/client_protocol.h index 9087d94..d6dd8ab 100644 --- a/lib/webfuse/provider/impl/client_protocol.h +++ b/lib/webfuse/provider/impl/client_protocol.h @@ -11,16 +11,22 @@ extern "C" { #endif -struct wfp_provider; +struct wfp_client_config; struct lws_protocols; +struct lws_context; +struct wf_jsonrpc_proxy; +struct wf_timer_manager; struct wfp_client_protocol { bool is_connected; + bool is_shutdown_requested; struct wfp_request request; struct wfp_provider provider; void * user_data; struct lws * wsi; + struct wf_timer_manager * timer_manager; + struct wf_jsonrpc_proxy * proxy; struct wf_slist messages; }; @@ -33,8 +39,7 @@ extern void wfp_impl_client_protocol_cleanup( struct wfp_client_protocol * protocol); extern struct wfp_client_protocol * wfp_impl_client_protocol_create( - struct wfp_provider const * provider, - void * user_data); + struct wfp_client_config const * config); extern void wfp_impl_client_protocol_dispose( struct wfp_client_protocol * protocol); @@ -43,6 +48,14 @@ extern void wfp_impl_client_protocol_init_lws( struct wfp_client_protocol * protocol, struct lws_protocols * lws_protocol); +extern void wfp_impl_client_protocol_connect( + struct wfp_client_protocol * protocol, + struct lws_context * context, + char const * url); + +extern void wfp_impl_client_protocol_disconnect( + struct wfp_client_protocol * protocol); + #ifdef __cplusplus } #endif diff --git a/lib/webfuse/provider/impl/credentials.c b/lib/webfuse/provider/impl/credentials.c new file mode 100644 index 0000000..f2d254f --- /dev/null +++ b/lib/webfuse/provider/impl/credentials.c @@ -0,0 +1,46 @@ +#include "webfuse/provider/impl/credentials.h" + +#include +#include + +void wfp_impl_credentials_init( + struct wfp_credentials * credentials) +{ + credentials->type = NULL; + credentials->contents = json_object(); +} + +void wfp_impl_credentials_cleanup( + struct wfp_credentials * credentials) +{ + free(credentials->type); + json_decref(credentials->contents); +} + +void wfp_impl_credentials_set_type( + struct wfp_credentials * credentials, + char const * type) +{ + free(credentials->type); + credentials->type = strdup(type); +} + +void wfp_impl_credentials_add( + struct wfp_credentials * credentials, + char const * key, + char const * value) +{ + json_object_set_new(credentials->contents, key, json_string(value)); +} + +char const * wfp_impl_credentials_get_type( + struct wfp_credentials * credentials) +{ + return credentials->type; +} + +json_t * wfp_impl_credentials_get( + struct wfp_credentials * credentials) +{ + return credentials->contents; +} diff --git a/lib/webfuse/provider/impl/credentials.h b/lib/webfuse/provider/impl/credentials.h new file mode 100644 index 0000000..304b067 --- /dev/null +++ b/lib/webfuse/provider/impl/credentials.h @@ -0,0 +1,43 @@ +#ifndef WF_PROVIDER_IMPL_CREDENTIALS_H +#define WF_PROVIDER_IMPL_CREDENTIALS_H + +#include "webfuse/provider/credentials.h" +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_credentials +{ + char * type; + json_t * contents; +}; + +extern void wfp_impl_credentials_init( + struct wfp_credentials * credentials); + +extern void wfp_impl_credentials_cleanup( + struct wfp_credentials * credentials); + +extern void wfp_impl_credentials_set_type( + struct wfp_credentials * credentials, + char const * type); + +extern void wfp_impl_credentials_add( + struct wfp_credentials * credentials, + char const * key, + char const * value); + +extern char const * wfp_impl_credentials_get_type( + struct wfp_credentials * credentials); + +extern json_t * wfp_impl_credentials_get( + struct wfp_credentials * credentials); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/provider/impl/dirbuffer.c b/lib/webfuse/provider/impl/dirbuffer.c index db401ef..9bc5f22 100644 --- a/lib/webfuse/provider/impl/dirbuffer.c +++ b/lib/webfuse/provider/impl/dirbuffer.c @@ -4,10 +4,7 @@ struct wfp_dirbuffer * wfp_impl_dirbuffer_create(void) { struct wfp_dirbuffer * buffer = malloc(sizeof(struct wfp_dirbuffer)); - if (NULL != buffer) - { - buffer->entries = json_array(); - } + buffer->entries = json_array(); return buffer; } diff --git a/lib/webfuse/provider/impl/operation/read.c b/lib/webfuse/provider/impl/operation/read.c index 64188b4..5c2b639 100644 --- a/lib/webfuse/provider/impl/operation/read.c +++ b/lib/webfuse/provider/impl/operation/read.c @@ -1,11 +1,11 @@ #include "webfuse/provider/impl/operation/read.h" #include -#include #include "webfuse/provider/impl/operation/error.h" #include "webfuse/provider/impl/request.h" #include "webfuse/core/util.h" +#include "webfuse/core/base64.h" void wfp_impl_read( struct wfp_impl_invokation_context * context, @@ -54,30 +54,23 @@ void wfp_impl_respond_read( { if (0 < length) { - size_t const size = 4 * ((length / 3) + 2); + size_t const size = wf_base64_encoded_size(length) + 1; char * buffer = malloc(size); - if (NULL != buffer) - { - lws_b64_encode_string(data, length, buffer, size); + wf_base64_encode((uint8_t const *) data, length, buffer, size); - json_t * result = json_object(); - json_object_set_new(result, "data", json_string(buffer)); - json_object_set_new(result, "format", json_string("base64")); - json_object_set_new(result, "count", json_integer((int) length)); + json_t * result = json_object(); + json_object_set_new(result, "data", json_string(buffer)); + json_object_set_new(result, "format", json_string("base64")); + json_object_set_new(result, "count", json_integer((int) length)); - wfp_impl_respond(request, result); - free(buffer); - } - else - { - wfp_impl_respond_error(request, WF_BAD); - } + wfp_impl_respond(request, result); + free(buffer); } else { json_t * result = json_object(); json_object_set_new(result, "data", json_string("")); - json_object_set_new(result, "format", json_string("identitiy")); + json_object_set_new(result, "format", json_string("identity")); json_object_set_new(result, "count", json_integer(0)); wfp_impl_respond(request, result); diff --git a/lib/webfuse/provider/impl/operation/readdir.c b/lib/webfuse/provider/impl/operation/readdir.c index 4fc3db7..63d59c1 100644 --- a/lib/webfuse/provider/impl/operation/readdir.c +++ b/lib/webfuse/provider/impl/operation/readdir.c @@ -14,7 +14,7 @@ void wfp_impl_readdir( { json_t * inode_holder = json_array_get(params, 1); - if ((NULL != inode_holder) && (json_is_integer(inode_holder))) + if (json_is_integer(inode_holder)) { ino_t inode = (ino_t) json_integer_value(inode_holder); struct wfp_request * request = wfp_impl_request_create(context->request, id); diff --git a/lib/webfuse/provider/impl/provider.c b/lib/webfuse/provider/impl/provider.c index def9e4a..397a563 100644 --- a/lib/webfuse/provider/impl/provider.c +++ b/lib/webfuse/provider/impl/provider.c @@ -66,7 +66,7 @@ void wfp_impl_provider_init( provider->read = &wfp_impl_read_default; provider->connected = &wfp_impl_connected_default; provider->disconnected = &wfp_impl_disconnected_default; - provider->ontimer = &wfp_impl_ontimer_default; + provider->get_credentials = NULL; } void wfp_impl_provider_init_from_prototype( @@ -81,7 +81,7 @@ void wfp_impl_provider_init_from_prototype( provider->read = prototype->read; provider->connected = prototype->connected; provider->disconnected = prototype->disconnected; - provider->ontimer = prototype->ontimer; + provider->get_credentials = prototype->get_credentials; } void wfp_impl_provider_invoke( @@ -92,8 +92,7 @@ void wfp_impl_provider_invoke( json_t * params = json_object_get(request, "params"); json_t * id_holder = json_object_get(request, "id"); - if ((NULL != method_holder) && (json_is_string(method_holder)) && - (NULL != params) && (json_is_array(params))) + if ((json_is_string(method_holder)) && (json_is_array(params))) { char const * method = json_string_value(method_holder); int id = json_is_integer(id_holder) ? json_integer_value(id_holder) : 0; @@ -118,10 +117,9 @@ void wfp_impl_disconnected_default( // empty } -void wfp_impl_ontimer_default( - void * user_data) + bool wfp_impl_provider_is_authentication_enabled( + struct wfp_provider * provider) { - (void) user_data; + return (NULL != provider->get_credentials); +} - // empty -} \ No newline at end of file diff --git a/lib/webfuse/provider/impl/provider.h b/lib/webfuse/provider/impl/provider.h index ddee453..aff4420 100644 --- a/lib/webfuse/provider/impl/provider.h +++ b/lib/webfuse/provider/impl/provider.h @@ -1,9 +1,14 @@ #ifndef WF_PROVIDER_IMPL_PROVIDER_H #define WF_PROVIDER_IMPL_PROVIDER_H +#ifndef __cplusplus +#include +#endif + #include #include "webfuse/provider/client_config.h" + #ifdef __cplusplus extern "C" { @@ -13,18 +18,18 @@ struct wfp_provider { wfp_connected_fn * connected; wfp_disconnected_fn * disconnected; - wfp_ontimer_fn * ontimer; wfp_lookup_fn * lookup; wfp_getattr_fn * getattr; wfp_readdir_fn * readdir; wfp_open_fn * open; wfp_close_fn * close; wfp_read_fn * read; + wfp_get_credentials_fn * get_credentials; }; struct wfp_impl_invokation_context { - struct wfp_provider * provider; + struct wfp_provider const * provider; void * user_data; struct wfp_request * request; }; @@ -41,15 +46,15 @@ extern void wfp_impl_provider_invoke( struct wfp_impl_invokation_context * context, json_t * request); +extern bool wfp_impl_provider_is_authentication_enabled( + struct wfp_provider * provider); + extern void wfp_impl_connected_default( void * user_data); extern void wfp_impl_disconnected_default( void * user_data); -extern void wfp_impl_ontimer_default( - void * user_data); - #ifdef __cplusplus } #endif diff --git a/lib/webfuse/provider/impl/request.c b/lib/webfuse/provider/impl/request.c index 42ea835..7f8b05f 100644 --- a/lib/webfuse/provider/impl/request.c +++ b/lib/webfuse/provider/impl/request.c @@ -8,12 +8,9 @@ struct wfp_request * wfp_impl_request_create( int id) { struct wfp_request * request = malloc(sizeof(struct wfp_request)); - if (NULL != request) - { - request->respond = prototype->respond; - request->user_data = prototype->user_data; - request->id = id; - } + request->respond = prototype->respond; + request->user_data = prototype->user_data; + request->id = id; return request; } diff --git a/lib/webfuse/provider/impl/static_filesystem.h b/lib/webfuse/provider/impl/static_filesystem.h deleted file mode 100644 index a18835d..0000000 --- a/lib/webfuse/provider/impl/static_filesystem.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef WFP_IMPL_STATIC_FILESYSTEM_H -#define WFP_IMPL_STATIC_FILESYSTEM_H - -#include "webfuse/provider/static_filesystem.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - -extern struct wfp_static_filesystem * -wfp_impl_static_filesystem_create( - struct wfp_client_config * config); - -extern void -wfp_impl_static_filesystem_dispose( - struct wfp_static_filesystem * filesystem); - -extern void -wfp_impl_static_filesystem_add( - struct wfp_static_filesystem * filesystem, - char const * path, - int mode, - char const * content, - size_t length); - -extern void -wfp_impl_static_filesystem_add_text( - struct wfp_static_filesystem * filesystem, - char const * path, - int mode, - char const * content); - -extern void -wfp_impl_static_filesystem_add_file( - struct wfp_static_filesystem * filesystem, - char const * path, - char const * filename); - -extern void -wfp_impl_static_filesystem_add_generic( - struct wfp_static_filesystem * filesystem, - char const * path, - wfp_static_filesystem_read_fn * read, - wfp_static_filesystem_get_info_fn * get_info, - void * user_data); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..f2b8c01 --- /dev/null +++ b/meson.build @@ -0,0 +1,260 @@ +project('webfuse', 'c', 'cpp', version: '0.3.0', license: 'LGPL-3.0+') + +libwebsockets_dep = dependency('libwebsockets', version: '>=4.0.1') +jansson_dep = dependency('jansson', version: '>=2.11', fallback: ['jansson', 'jansson_dep']) +libfuse_dep = dependency('fuse3', version: '>=3.8.0', fallback: ['fuse3', 'libfuse_dep']) +gtest_dep = dependency('gtest', version: '>=1.10.0', fallback: ['gtest', 'gtest_dep']) +gmock_main_dep = dependency('gmock_main', version: '>=1.10.0', fallback: ['gtest', 'gmock_main_main_dep']) + +pkg_config = import('pkgconfig') + +# Webfuse core + +inc_dir = include_directories('include') +private_inc_dir = include_directories('include', 'lib') + +webfuse_core = static_library('webfuse_core', + 'lib/webfuse/core/slist.c', + '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/timer/manager.c', + 'lib/webfuse/core/timer/timepoint.c', + 'lib/webfuse/core/timer/timer.c', + 'lib/webfuse/core/jsonrpc/proxy.c', + 'lib/webfuse/core/jsonrpc/proxy_variadic.c', + 'lib/webfuse/core/jsonrpc/server.c', + 'lib/webfuse/core/jsonrpc/method.c', + 'lib/webfuse/core/jsonrpc/request.c', + 'lib/webfuse/core/jsonrpc/response.c', + 'lib/webfuse/core/jsonrpc/error.c', + c_args: ['-fvisibility=hidden'], + include_directories: private_inc_dir, + dependencies: [jansson_dep, libwebsockets_dep]) + +webfuse_core_dep = declare_dependency( + include_directories: inc_dir, + link_with: webfuse_core, + dependencies: [jansson_dep, libwebsockets_dep]) + +install_subdir('include/webfuse/core', install_dir: 'include/webfuse') + +# Webfuse 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', + 'lib/webfuse/provider/impl/provider.c', + 'lib/webfuse/provider/impl/request.c', + 'lib/webfuse/provider/impl/dirbuffer.c', + 'lib/webfuse/provider/impl/credentials.c', + 'lib/webfuse/provider/impl/operation/lookup.c', + 'lib/webfuse/provider/impl/operation/getattr.c', + 'lib/webfuse/provider/impl/operation/readdir.c', + 'lib/webfuse/provider/impl/operation/open.c', + 'lib/webfuse/provider/impl/operation/close.c', + 'lib/webfuse/provider/impl/operation/read.c', + c_args: ['-fvisibility=hidden'], + include_directories: private_inc_dir, + dependencies: [webfuse_core_dep]) + +webfuse_provider_static_dep = declare_dependency( + include_directories: inc_dir, + link_with: webfuse_provider_static) + +webfuse_provider = shared_library('webfuse_provider', + 'lib/webfuse/provider/api.c', + version: meson.project_version(), + c_args: ['-fvisibility=hidden', '-DWFP_API=WFP_EXPORT'], + include_directories: private_inc_dir, + dependencies: [webfuse_provider_static_dep], + install: true) + +webfuse_provider_dep = declare_dependency( + include_directories: inc_dir, + link_with: [webfuse_provider], + dependencies: [libwebsockets_dep, jansson_dep]) + +install_headers('include/webfuse_provider.h', subdir: 'webfuse') +install_subdir('include/webfuse/provider', install_dir: 'include/webfuse') + +pkg_config.generate( + libraries: [webfuse_provider, jansson_dep, libwebsockets_dep], + subdirs: '.', + version: meson.project_version(), + name: 'libwebfuse_provider', + filebase: 'webfuse_provider', + description: 'Provider library for websockets filesystem') + +# Webfuse adapter + +webfuse_adapter_static = static_library('webfuse_adapter', + 'lib/webfuse/adapter/api.c', + 'lib/webfuse/adapter/impl/filesystem.c', + 'lib/webfuse/adapter/impl/server.c', + 'lib/webfuse/adapter/impl/server_config.c', + 'lib/webfuse/adapter/impl/server_protocol.c', + 'lib/webfuse/adapter/impl/session.c', + 'lib/webfuse/adapter/impl/session_manager.c', + 'lib/webfuse/adapter/impl/authenticator.c', + 'lib/webfuse/adapter/impl/authenticators.c', + 'lib/webfuse/adapter/impl/credentials.c', + 'lib/webfuse/adapter/impl/mountpoint.c', + 'lib/webfuse/adapter/impl/mountpoint_factory.c', + 'lib/webfuse/adapter/impl/operation/context.c', + 'lib/webfuse/adapter/impl/operation/lookup.c', + 'lib/webfuse/adapter/impl/operation/getattr.c', + 'lib/webfuse/adapter/impl/operation/readdir.c', + 'lib/webfuse/adapter/impl/operation/open.c', + 'lib/webfuse/adapter/impl/operation/close.c', + 'lib/webfuse/adapter/impl/operation/read.c', + c_args: ['-fvisibility=hidden'], + include_directories: private_inc_dir, + dependencies: [webfuse_core_dep, libfuse_dep]) + +webfuse_adapter_static_dep = declare_dependency( + include_directories: inc_dir, + link_with: [webfuse_adapter_static], + dependencies: [libfuse_dep]) + +webfuse_adapter = shared_library('webfuse_adapter', + 'lib/webfuse/adapter/api.c', + version: meson.project_version(), + c_args: ['-fvisibility=hidden', '-DWF_API=WF_EXPORT'], + include_directories: private_inc_dir, + dependencies: [webfuse_adapter_static_dep, libfuse_dep], + install: true) + +webfuse_adapter_dep = declare_dependency( + include_directories: inc_dir, + link_with: [webfuse_adapter], + dependencies: [libfuse_dep, libwebsockets_dep, jansson_dep]) + +install_headers('include/webfuse_adapter.h', subdir: 'webfuse') +install_subdir('include/webfuse/adapter', install_dir: 'include/webfuse') + +pkg_config.generate( + libraries: [webfuse_adapter, jansson_dep, libwebsockets_dep, libfuse_dep], + subdirs: '.', + version: meson.project_version(), + name: 'libwebfuse_adapter', + filebase: 'webfuse_adapter', + description: 'Websockets filesystem server library') + +# Unit Tests + +fscheck = executable('fs_check', + 'test/webfuse/tests/integration/fs_check.c') + +openssl = find_program('openssl') +test_server_certs = custom_target('test_server_certs', + output: ['server-key.pem', 'server-cert.pem'], + command: [openssl, 'req', '-x509', '-newkey', 'rsa:4096', '-keyout', 'server-key.pem', '-out', 'server-cert.pem', '-days', '365', '-nodes', '-batch', '-subj', '/CN=localhost']) +test_client_certs = custom_target('test_client_certs', + output: ['client-key.pem', 'client-cert.pem'], + command: [openssl, 'req', '-x509', '-newkey', 'rsa:4096', '-keyout', 'client-key.pem', '-out', 'client-cert.pem', '-days', '365', '-nodes', '-batch', '-subj', '/CN=localhost']) + +test_certs_dep = declare_dependency( + sources: [test_server_certs, test_client_certs]) + +alltests = executable('alltests', + 'test/webfuse/tests/core/jsonrpc/mock_timer_callback.cc', + 'test/webfuse/tests/core/jsonrpc/mock_timer.cc', + 'test/webfuse/tests/core/jsonrpc/test_is_request.cc', + 'test/webfuse/tests/core/jsonrpc/test_request.cc', + 'test/webfuse/tests/core/jsonrpc/test_is_response.cc', + 'test/webfuse/tests/core/jsonrpc/test_response.cc', + 'test/webfuse/tests/core/jsonrpc/test_server.cc', + 'test/webfuse/tests/core/jsonrpc/test_proxy.cc', + 'test/webfuse/tests/core/jsonrpc/test_response_parser.cc', + 'test/webfuse/tests/core/timer/test_timepoint.cc', + 'test/webfuse/tests/core/timer/test_timer.cc', + 'test/webfuse/utils/tempdir.cc', + 'test/webfuse/utils/file_utils.cc', + 'test/webfuse/utils/timeout_watcher.cc', + 'test/webfuse/utils/path.c', + 'test/webfuse/utils/static_filesystem.c', + 'test/webfuse/utils/ws_server.cc', + 'test/webfuse/mocks/fake_invokation_context.cc', + 'test/webfuse/mocks/mock_authenticator.cc', + 'test/webfuse/mocks/mock_request.cc', + 'test/webfuse/mocks/mock_provider_client.cc', + 'test/webfuse/mocks/mock_provider.cc', + 'test/webfuse/mocks/mock_fuse.cc', + 'test/webfuse/mocks/mock_operation_context.cc', + 'test/webfuse/mocks/mock_jsonrpc_proxy.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/adapter/test_server.cc', + 'test/webfuse/tests/adapter/test_server_config.cc', + 'test/webfuse/tests/adapter/test_credentials.cc', + 'test/webfuse/tests/adapter/test_authenticator.cc', + 'test/webfuse/tests/adapter/test_authenticators.cc', + 'test/webfuse/tests/adapter/test_mountpoint.cc', + 'test/webfuse/tests/adapter/test_fuse_req.cc', + 'test/webfuse/tests/adapter/operation/test_context.cc', + 'test/webfuse/tests/adapter/operation/test_open.cc', + 'test/webfuse/tests/adapter/operation/test_close.cc', + 'test/webfuse/tests/adapter/operation/test_read.cc', + 'test/webfuse/tests/adapter/operation/test_readdir.cc', + 'test/webfuse/tests/adapter/operation/test_getattr.cc', + 'test/webfuse/tests/adapter/operation/test_lookup.cc', + 'test/webfuse/tests/provider/test_url.cc', + 'test/webfuse/tests/provider/test_client_protocol.cc', + 'test/webfuse/tests/provider/operation/test_close.cc', + 'test/webfuse/tests/provider/operation/test_getattr.cc', + 'test/webfuse/tests/provider/operation/test_lookup.cc', + 'test/webfuse/tests/provider/operation/test_open.cc', + 'test/webfuse/tests/provider/operation/test_read.cc', + 'test/webfuse/tests/provider/operation/test_readdir.cc', + 'test/webfuse/tests/integration/test_lowlevel.cc', + 'test/webfuse/tests/integration/test_integration.cc', + 'test/webfuse/tests/integration/file.cc', + 'test/webfuse/tests/integration/server.cc', + 'test/webfuse/tests/integration/provider.cc', + link_args: [ + '-Wl,--wrap=wf_timer_manager_create', + '-Wl,--wrap=wf_timer_manager_dispose', + '-Wl,--wrap=wf_timer_manager_check', + '-Wl,--wrap=wf_timer_create', + '-Wl,--wrap=wf_timer_dispose', + '-Wl,--wrap=wf_timer_start', + '-Wl,--wrap=wf_timer_cancel', + '-Wl,--wrap=wf_impl_operation_context_get_proxy', + '-Wl,--wrap=wf_jsonrpc_proxy_vinvoke', + '-Wl,--wrap=wf_jsonrpc_proxy_vnotify', + '-Wl,--wrap=fuse_req_userdata', + '-Wl,--wrap=fuse_reply_open', + '-Wl,--wrap=fuse_reply_err', + '-Wl,--wrap=fuse_reply_buf', + '-Wl,--wrap=fuse_reply_attr', + '-Wl,--wrap=fuse_reply_entry', + '-Wl,--wrap=fuse_req_ctx' + ], + include_directories: [private_inc_dir, 'test'], + dependencies: [ + webfuse_adapter_static_dep, + webfuse_provider_static_dep, + webfuse_core_dep, + libwebsockets_dep, + libfuse_dep, + jansson_dep, + gtest_dep, + gmock_main_dep, + test_certs_dep + ]) + +test('alltests', alltests) diff --git a/run-clang-tidy.sh b/run-clang-tidy.sh deleted file mode 100755 index f4e98d4..0000000 --- a/run-clang-tidy.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -# -# Helper to run clang-tidy to perform some additional checks. - -FILES=$(find lib -name '*.c') -INCLUDES="-Iinclude -Ilib $(pkg-config fuse3 --cflags) $(pkg-config libwebsockets --cflags)" - -clang-tidy -header-filter='.*' ${FILES} -- ${INCLUDES} diff --git a/subprojects/fuse3.wrap b/subprojects/fuse3.wrap new file mode 100644 index 0000000..ccb34f5 --- /dev/null +++ b/subprojects/fuse3.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = libfuse-fuse-3.8.0 + +source_url = https://github.com/libfuse/libfuse/archive/fuse-3.8.0.tar.gz +source_filename = fuse-3.8.0.tar.gz +source_hash = 1781225ba4d11d76eb105e02e54976939974547eb40bab4b4e91167854224024 + diff --git a/subprojects/gtest.wrap b/subprojects/gtest.wrap new file mode 100644 index 0000000..d52b0b3 --- /dev/null +++ b/subprojects/gtest.wrap @@ -0,0 +1,10 @@ +[wrap-file] +directory = googletest-release-1.10.0 + +source_url = https://github.com/google/googletest/archive/release-1.10.0.zip +source_filename = gtest-1.10.0.zip +source_hash = 94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91 + +patch_url = https://wrapdb.mesonbuild.com/v1/projects/gtest/1.10.0/1/get_zip +patch_filename = gtest-1.10.0-1-wrap.zip +patch_hash = 04ff14e8880e4e465f6260221e9dfd56fea6bc7cce4c4aff0dc528e4a2c8f514 \ No newline at end of file diff --git a/subprojects/jansson.wrap b/subprojects/jansson.wrap new file mode 100644 index 0000000..6282afd --- /dev/null +++ b/subprojects/jansson.wrap @@ -0,0 +1,10 @@ +[wrap-file] +directory = jansson-2.11 + +source_url = http://www.digip.org/jansson/releases/jansson-2.11.tar.bz2 +source_filename = jansson-2.11.tar.bz2 +source_hash = 783132e2fc970feefc2fa54199ef65ee020bd8e0e991a78ea44b8586353a0947 + +patch_url = https://wrapdb.mesonbuild.com/v1/projects/jansson/2.11/3/get_zip +patch_filename = jansson-2.11-3-wrap.zip +patch_hash = 0bcac510994890048d42658c674e33dd7d88715fc1e3bf49d10012f57b0e0020 \ No newline at end of file diff --git a/test/adapter/jsonrpc/test_proxy.cc b/test/adapter/jsonrpc/test_proxy.cc deleted file mode 100644 index 1355c4e..0000000 --- a/test/adapter/jsonrpc/test_proxy.cc +++ /dev/null @@ -1,393 +0,0 @@ -#include -#include "webfuse/adapter/impl/jsonrpc/proxy.h" -#include "webfuse/adapter/impl/time/timeout_manager.h" -#include "msleep.hpp" - -using webfuse_test::msleep; - -#define WF_DEFAULT_TIMEOUT (10 * 1000) - -namespace -{ - - struct SendContext - { - json_t * response; - bool result; - bool is_called; - - explicit SendContext(bool result_ = true) - : response(nullptr) - , result(result_) - , is_called(false) - { - } - - ~SendContext() - { - if (nullptr != response) - { - json_decref(response); - } - } - }; - - bool jsonrpc_send( - json_t * request, - void * user_data) - { - SendContext * context = reinterpret_cast(user_data); - context->is_called = true; - context->response = request; - json_incref(request); - - return context->result; - } - - struct FinishedContext - { - bool is_called; - wf_status status; - json_t * result; - - FinishedContext() - : is_called(false) - , status(WF_BAD) - , result(nullptr) - { - - } - - ~FinishedContext() - { - if (nullptr != result) - { - json_decref(result); - } - } - }; - - void jsonrpc_finished( - void * user_data, - wf_status status, - struct json_t const * result) - { - FinishedContext * context = reinterpret_cast(user_data); - context->is_called = true; - context->status = status; - context->result = json_deep_copy(result); - } -} - -TEST(jsonrpc_proxy, init) -{ - struct wf_impl_timeout_manager timeout_manager; - wf_impl_timeout_manager_init(&timeout_manager); - - SendContext context; - void * user_data = reinterpret_cast(&context); - struct wf_impl_jsonrpc_proxy proxy; - wf_impl_jsonrpc_proxy_init(&proxy, &timeout_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, user_data); - - wf_impl_jsonrpc_proxy_cleanup(&proxy); - wf_impl_timeout_manager_cleanup(&timeout_manager); - - ASSERT_FALSE(context.is_called); -} - -TEST(jsonrpc_proxy, invoke) -{ - struct wf_impl_timeout_manager timeout_manager; - wf_impl_timeout_manager_init(&timeout_manager); - - SendContext send_context; - void * send_data = reinterpret_cast(&send_context); - struct wf_impl_jsonrpc_proxy proxy; - wf_impl_jsonrpc_proxy_init(&proxy, &timeout_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); - - FinishedContext finished_context; - void * finished_data = reinterpret_cast(&finished_context); - wf_impl_jsonrpc_proxy_invoke(&proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); - - ASSERT_TRUE(send_context.is_called); - ASSERT_TRUE(json_is_object(send_context.response)); - - json_t * method = json_object_get(send_context.response, "method"); - ASSERT_TRUE(json_is_string(method)); - ASSERT_STREQ("foo", json_string_value(method)); - - json_t * params = json_object_get(send_context.response, "params"); - ASSERT_TRUE(json_is_array(params)); - ASSERT_EQ(2, json_array_size(params)); - ASSERT_TRUE(json_is_string(json_array_get(params, 0))); - ASSERT_STREQ("bar", json_string_value(json_array_get(params, 0))); - ASSERT_TRUE(json_is_integer(json_array_get(params, 1))); - ASSERT_EQ(42, json_integer_value(json_array_get(params, 1))); - - json_t * id = json_object_get(send_context.response, "id"); - ASSERT_TRUE(json_is_integer(id)); - - ASSERT_FALSE(finished_context.is_called); - - wf_impl_jsonrpc_proxy_cleanup(&proxy); - wf_impl_timeout_manager_cleanup(&timeout_manager); - - ASSERT_TRUE(finished_context.is_called); - ASSERT_FALSE(WF_GOOD == finished_context.status); -} - -TEST(jsonrpc_proxy, invoke_calls_finish_if_send_fails) -{ - struct wf_impl_timeout_manager timeout_manager; - wf_impl_timeout_manager_init(&timeout_manager); - - SendContext send_context(false); - void * send_data = reinterpret_cast(&send_context); - struct wf_impl_jsonrpc_proxy proxy; - wf_impl_jsonrpc_proxy_init(&proxy, &timeout_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); - - FinishedContext finished_context; - void * finished_data = reinterpret_cast(&finished_context); - wf_impl_jsonrpc_proxy_invoke(&proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); - - ASSERT_TRUE(send_context.is_called); - ASSERT_TRUE(json_is_object(send_context.response)); - - ASSERT_TRUE(finished_context.is_called); - ASSERT_FALSE(WF_GOOD == finished_context.status); - - wf_impl_jsonrpc_proxy_cleanup(&proxy); - wf_impl_timeout_manager_cleanup(&timeout_manager); -} - -TEST(jsonrpc_proxy, invoke_fails_if_another_request_is_pending) -{ - struct wf_impl_timeout_manager timeout_manager; - wf_impl_timeout_manager_init(&timeout_manager); - - SendContext send_context; - void * send_data = reinterpret_cast(&send_context); - struct wf_impl_jsonrpc_proxy proxy; - wf_impl_jsonrpc_proxy_init(&proxy, &timeout_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); - - FinishedContext finished_context; - void * finished_data = reinterpret_cast(&finished_context); - wf_impl_jsonrpc_proxy_invoke(&proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); - - FinishedContext finished_context2; - void * finished_data2 = reinterpret_cast(&finished_context2); - wf_impl_jsonrpc_proxy_invoke(&proxy, &jsonrpc_finished, finished_data2, "foo", ""); - - ASSERT_TRUE(send_context.is_called); - ASSERT_TRUE(json_is_object(send_context.response)); - - ASSERT_FALSE(finished_context.is_called); - - ASSERT_TRUE(finished_context2.is_called); - ASSERT_EQ(WF_BAD_BUSY, finished_context2.status); - - wf_impl_jsonrpc_proxy_cleanup(&proxy); - wf_impl_timeout_manager_cleanup(&timeout_manager); -} - -TEST(jsonrpc_proxy, invoke_fails_if_request_is_invalid) -{ - struct wf_impl_timeout_manager timeout_manager; - wf_impl_timeout_manager_init(&timeout_manager); - - SendContext send_context; - void * send_data = reinterpret_cast(&send_context); - struct wf_impl_jsonrpc_proxy proxy; - wf_impl_jsonrpc_proxy_init(&proxy, &timeout_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); - - FinishedContext finished_context; - void * finished_data = reinterpret_cast(&finished_context); - wf_impl_jsonrpc_proxy_invoke(&proxy, &jsonrpc_finished, finished_data, "foo", "?", "error"); - - ASSERT_FALSE(send_context.is_called); - - ASSERT_TRUE(finished_context.is_called); - ASSERT_EQ(WF_BAD, finished_context.status); - - wf_impl_jsonrpc_proxy_cleanup(&proxy); - wf_impl_timeout_manager_cleanup(&timeout_manager); -} - -TEST(jsonrpc_proxy, on_result) -{ - struct wf_impl_timeout_manager timeout_manager; - wf_impl_timeout_manager_init(&timeout_manager); - - SendContext send_context; - void * send_data = reinterpret_cast(&send_context); - struct wf_impl_jsonrpc_proxy proxy; - wf_impl_jsonrpc_proxy_init(&proxy, &timeout_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); - - FinishedContext finished_context; - void * finished_data = reinterpret_cast(&finished_context); - wf_impl_jsonrpc_proxy_invoke(&proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); - - ASSERT_TRUE(send_context.is_called); - ASSERT_TRUE(json_is_object(send_context.response)); - - json_t * id = json_object_get(send_context.response, "id"); - ASSERT_TRUE(json_is_number(id)); - - json_t * response = json_object(); - json_object_set_new(response, "result", json_string("okay")); - json_object_set(response, "id", id); - - wf_impl_jsonrpc_proxy_onresult(&proxy, response); - json_decref(response); - - ASSERT_TRUE(finished_context.is_called); - ASSERT_EQ(WF_GOOD, finished_context.status); - ASSERT_TRUE(json_is_string(finished_context.result)); - ASSERT_STREQ("okay", json_string_value(finished_context.result)); - - wf_impl_jsonrpc_proxy_cleanup(&proxy); - wf_impl_timeout_manager_cleanup(&timeout_manager); -} - -TEST(jsonrpc_proxy, on_result_reject_response_with_unknown_id) -{ - struct wf_impl_timeout_manager timeout_manager; - wf_impl_timeout_manager_init(&timeout_manager); - - SendContext send_context; - void * send_data = reinterpret_cast(&send_context); - struct wf_impl_jsonrpc_proxy proxy; - wf_impl_jsonrpc_proxy_init(&proxy, &timeout_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); - - FinishedContext finished_context; - void * finished_data = reinterpret_cast(&finished_context); - wf_impl_jsonrpc_proxy_invoke(&proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); - - ASSERT_TRUE(send_context.is_called); - ASSERT_TRUE(json_is_object(send_context.response)); - - json_t * id = json_object_get(send_context.response, "id"); - ASSERT_TRUE(json_is_number(id)); - - json_t * response = json_object(); - json_object_set_new(response, "result", json_string("okay")); - json_object_set_new(response, "id", json_integer(1 + json_integer_value(id))); - - wf_impl_jsonrpc_proxy_onresult(&proxy, response); - json_decref(response); - - ASSERT_FALSE(finished_context.is_called); - - wf_impl_jsonrpc_proxy_cleanup(&proxy); - wf_impl_timeout_manager_cleanup(&timeout_manager); -} - -TEST(jsonrpc_proxy, timeout) -{ - struct wf_impl_timeout_manager timeout_manager; - wf_impl_timeout_manager_init(&timeout_manager); - - SendContext send_context; - void * send_data = reinterpret_cast(&send_context); - struct wf_impl_jsonrpc_proxy proxy; - wf_impl_jsonrpc_proxy_init(&proxy, &timeout_manager, 0, &jsonrpc_send, send_data); - - FinishedContext finished_context; - void * finished_data = reinterpret_cast(&finished_context); - wf_impl_jsonrpc_proxy_invoke(&proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); - - ASSERT_TRUE(send_context.is_called); - ASSERT_TRUE(json_is_object(send_context.response)); - - msleep(10); - wf_impl_timeout_manager_check(&timeout_manager); - - ASSERT_TRUE(finished_context.is_called); - ASSERT_EQ(WF_BAD_TIMEOUT, finished_context.status); - - wf_impl_jsonrpc_proxy_cleanup(&proxy); - wf_impl_timeout_manager_cleanup(&timeout_manager); -} - -TEST(jsonrpc_proxy, cleanup_pending_request) -{ - struct wf_impl_timeout_manager timeout_manager; - wf_impl_timeout_manager_init(&timeout_manager); - - SendContext send_context; - void * send_data = reinterpret_cast(&send_context); - struct wf_impl_jsonrpc_proxy proxy; - wf_impl_jsonrpc_proxy_init(&proxy, &timeout_manager, 10, &jsonrpc_send, send_data); - - FinishedContext finished_context; - void * finished_data = reinterpret_cast(&finished_context); - wf_impl_jsonrpc_proxy_invoke(&proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); - - ASSERT_TRUE(send_context.is_called); - ASSERT_TRUE(json_is_object(send_context.response)); - - ASSERT_FALSE(finished_context.is_called); - ASSERT_NE(nullptr, timeout_manager.timers); - - wf_impl_jsonrpc_proxy_cleanup(&proxy); - - ASSERT_TRUE(finished_context.is_called); - ASSERT_EQ(nullptr, timeout_manager.timers); - - wf_impl_timeout_manager_cleanup(&timeout_manager); -} - - - -TEST(jsonrpc_proxy, notify) -{ - struct wf_impl_timeout_manager timeout_manager; - wf_impl_timeout_manager_init(&timeout_manager); - - SendContext send_context; - void * send_data = reinterpret_cast(&send_context); - struct wf_impl_jsonrpc_proxy proxy; - wf_impl_jsonrpc_proxy_init(&proxy, &timeout_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); - - wf_impl_jsonrpc_proxy_notify(&proxy, "foo", "si", "bar", 42); - - ASSERT_TRUE(send_context.is_called); - ASSERT_TRUE(json_is_object(send_context.response)); - - json_t * method = json_object_get(send_context.response, "method"); - ASSERT_TRUE(json_is_string(method)); - ASSERT_STREQ("foo", json_string_value(method)); - - json_t * params = json_object_get(send_context.response, "params"); - ASSERT_TRUE(json_is_array(params)); - ASSERT_EQ(2, json_array_size(params)); - ASSERT_TRUE(json_is_string(json_array_get(params, 0))); - ASSERT_STREQ("bar", json_string_value(json_array_get(params, 0))); - ASSERT_TRUE(json_is_integer(json_array_get(params, 1))); - ASSERT_EQ(42, json_integer_value(json_array_get(params, 1))); - - json_t * id = json_object_get(send_context.response, "id"); - ASSERT_EQ(nullptr, id); - - - wf_impl_jsonrpc_proxy_cleanup(&proxy); - wf_impl_timeout_manager_cleanup(&timeout_manager); -} - -TEST(jsonrpc_proxy, notify_dont_send_invalid_request) -{ - struct wf_impl_timeout_manager timeout_manager; - wf_impl_timeout_manager_init(&timeout_manager); - - SendContext send_context; - void * send_data = reinterpret_cast(&send_context); - struct wf_impl_jsonrpc_proxy proxy; - wf_impl_jsonrpc_proxy_init(&proxy, &timeout_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); - - wf_impl_jsonrpc_proxy_notify(&proxy, "foo", "?"); - - ASSERT_FALSE(send_context.is_called); - - wf_impl_jsonrpc_proxy_cleanup(&proxy); - wf_impl_timeout_manager_cleanup(&timeout_manager); -} diff --git a/test/adapter/jsonrpc/test_response.cc b/test/adapter/jsonrpc/test_response.cc deleted file mode 100644 index 3a63d0f..0000000 --- a/test/adapter/jsonrpc/test_response.cc +++ /dev/null @@ -1,56 +0,0 @@ -#include -#include "webfuse/adapter/impl/jsonrpc/response.h" - -TEST(json_response, init_result) -{ - json_t * message = json_object(); - json_object_set_new(message, "result", json_integer(47)); - json_object_set_new(message, "id", json_integer(11)); - - struct wf_impl_jsonrpc_response response; - wf_impl_jsonrpc_response_init(&response, message); - - ASSERT_EQ(WF_GOOD, response.status); - ASSERT_TRUE(json_is_integer(response.result)); - ASSERT_EQ(47, json_integer_value(response.result)); - ASSERT_EQ(11, response.id); - - wf_impl_jsonrpc_response_cleanup(&response); - json_decref(message); -} - -TEST(json_response, init_error) -{ - json_t * message = json_object(); - json_t * err = json_object(); - json_object_set_new(err, "code", json_integer(WF_BAD_ACCESS_DENIED)); - json_object_set_new(err, "message", json_string("access denied")); - json_object_set_new(message, "error", err); - json_object_set_new(message, "id", json_integer(23)); - - struct wf_impl_jsonrpc_response response; - wf_impl_jsonrpc_response_init(&response, message); - - ASSERT_EQ(WF_BAD_ACCESS_DENIED, response.status); - ASSERT_EQ(nullptr, response.result); - ASSERT_EQ(23, response.id); - - wf_impl_jsonrpc_response_cleanup(&response); - json_decref(message); -} - -TEST(json_response, init_format_error) -{ - json_t * message = json_object(); - json_object_set_new(message, "id", json_integer(12)); - - struct wf_impl_jsonrpc_response response; - wf_impl_jsonrpc_response_init(&response, message); - - ASSERT_EQ(WF_BAD_FORMAT, response.status); - ASSERT_EQ(nullptr, response.result); - ASSERT_EQ(12, response.id); - - wf_impl_jsonrpc_response_cleanup(&response); - json_decref(message); -} diff --git a/test/adapter/jsonrpc/test_server.cc b/test/adapter/jsonrpc/test_server.cc deleted file mode 100644 index 20d5d0f..0000000 --- a/test/adapter/jsonrpc/test_server.cc +++ /dev/null @@ -1,125 +0,0 @@ -#include -#include "webfuse/adapter/impl/jsonrpc/server.h" -#include "webfuse/adapter/impl/jsonrpc/request.h" - -namespace -{ - struct Context - { - json_t * response; - bool is_called; - }; - - bool jsonrpc_send( - json_t * request, - void * user_data) - { - Context * context = reinterpret_cast(user_data); - context->is_called = true; - context->response = request; - json_incref(request); - - return true; - } - - void sayHello( - struct wf_impl_jsonrpc_request * request, - char const * method_name, - json_t * params, - void * user_data) - { - (void) method_name; - (void) params; - (void) user_data; - - json_t * result = json_string("Hello"); - wf_impl_jsonrpc_respond(request, result); - } - -} - -TEST(jsonrpc_server, process_request) -{ - struct wf_impl_jsonrpc_server server; - wf_impl_jsonrpc_server_init(&server); - wf_impl_jsonrpc_server_add(&server, "sayHello", &sayHello, nullptr); - - Context context{nullptr, false}; - void * user_data = reinterpret_cast(&context); - json_t * request = json_object(); - json_object_set_new(request, "method", json_string("sayHello")); - json_object_set_new(request, "params", json_array()); - json_object_set_new(request, "id", json_integer(23)); - wf_impl_jsonrpc_server_process(&server, request, &jsonrpc_send, user_data); - - ASSERT_TRUE(context.is_called); - ASSERT_NE(nullptr, context.response); - ASSERT_TRUE(json_is_object(context.response)); - - json_t * id = json_object_get(context.response, "id"); - ASSERT_TRUE(json_is_integer(id)); - ASSERT_EQ(23, json_integer_value(id)); - - json_t * result = json_object_get(context.response, "result"); - ASSERT_TRUE(json_is_string(result)); - ASSERT_STREQ("Hello", json_string_value(result)); - - json_decref(context.response); - json_decref(request); - wf_impl_jsonrpc_server_cleanup(&server); -} - -TEST(jsonrpc_server, invoke_unknown_method) -{ - struct wf_impl_jsonrpc_server server; - wf_impl_jsonrpc_server_init(&server); - wf_impl_jsonrpc_server_add(&server, "sayHello", &sayHello, nullptr); - - Context context{nullptr, false}; - void * user_data = reinterpret_cast(&context); - json_t * request = json_object(); - json_object_set_new(request, "method", json_string("greet")); - json_object_set_new(request, "params", json_array()); - json_object_set_new(request, "id", json_integer(42)); - wf_impl_jsonrpc_server_process(&server, request, &jsonrpc_send, user_data); - - ASSERT_TRUE(context.is_called); - ASSERT_NE(nullptr, context.response); - ASSERT_TRUE(json_is_object(context.response)); - - json_t * id = json_object_get(context.response, "id"); - ASSERT_TRUE(json_is_integer(id)); - ASSERT_EQ(42, json_integer_value(id)); - - json_t * err = json_object_get(context.response, "error"); - ASSERT_TRUE(json_is_object(err)); - - json_t * err_code = json_object_get(err, "code"); - ASSERT_TRUE(json_is_integer(err_code)); - ASSERT_EQ(WF_BAD_NOTIMPLEMENTED, json_integer_value(err_code)); - - json_t * err_message = json_object_get(err, "message"); - ASSERT_TRUE(json_is_string(err_message)); - - json_decref(context.response); - json_decref(request); - wf_impl_jsonrpc_server_cleanup(&server); -} - -TEST(jsonrpc_server, skip_invalid_request) -{ - struct wf_impl_jsonrpc_server server; - wf_impl_jsonrpc_server_init(&server); - - Context context{nullptr, false}; - void * user_data = reinterpret_cast(&context); - json_t * request = json_object(); - json_object_set_new(request, "method", json_string("sayHello")); - json_object_set_new(request, "params", json_array()); - wf_impl_jsonrpc_server_process(&server, request, &jsonrpc_send, user_data); - - ASSERT_FALSE(context.is_called); - - json_decref(request); - wf_impl_jsonrpc_server_cleanup(&server); -} diff --git a/test/adapter/test_credentials.cc b/test/adapter/test_credentials.cc deleted file mode 100644 index 72350d4..0000000 --- a/test/adapter/test_credentials.cc +++ /dev/null @@ -1,70 +0,0 @@ -#include - -#include "webfuse/adapter/impl/credentials.h" -#include - -TEST(Credentials, Type) -{ - struct wf_credentials creds; - - wf_impl_credentials_init(&creds, "test", nullptr); - ASSERT_STREQ("test", wf_impl_credentials_type(&creds)); - wf_impl_credentials_cleanup(&creds); -} - -TEST(Credentials, Get) -{ - struct wf_credentials creds; - json_t * data = json_object(); - json_object_set_new(data, "username", json_string("bob")); - json_object_set_new(data, "password", json_string("")); - - wf_impl_credentials_init(&creds, "username", data); - ASSERT_STREQ("username", wf_impl_credentials_type(&creds)); - ASSERT_STREQ("bob", wf_impl_credentials_get(&creds, "username")); - ASSERT_STREQ("", wf_impl_credentials_get(&creds, "password")); - - wf_impl_credentials_cleanup(&creds); - json_decref(data); -} - -TEST(Credentials, FailedToGetNonexistingValue) -{ - struct wf_credentials creds; - json_t * data = json_object(); - - wf_impl_credentials_init(&creds, "username", data); - ASSERT_STREQ("username", wf_impl_credentials_type(&creds)); - ASSERT_STREQ(nullptr, wf_impl_credentials_get(&creds, "username")); - ASSERT_STREQ(nullptr, wf_impl_credentials_get(&creds, "password")); - - wf_impl_credentials_cleanup(&creds); - json_decref(data); -} - -TEST(Credentials, FailedToGetWithoutData) -{ - struct wf_credentials creds; - - wf_impl_credentials_init(&creds, "username", nullptr); - ASSERT_STREQ("username", wf_impl_credentials_type(&creds)); - ASSERT_STREQ(nullptr, wf_impl_credentials_get(&creds, "username")); - ASSERT_STREQ(nullptr, wf_impl_credentials_get(&creds, "password")); - - wf_impl_credentials_cleanup(&creds); -} - -TEST(Credentials, FailedToGetWrongDataType) -{ - struct wf_credentials creds; - json_t * data = json_string("invalid_creds"); - - wf_impl_credentials_init(&creds, "username", data); - ASSERT_STREQ("username", wf_impl_credentials_type(&creds)); - ASSERT_STREQ(nullptr, wf_impl_credentials_get(&creds, "username")); - ASSERT_STREQ(nullptr, wf_impl_credentials_get(&creds, "password")); - - wf_impl_credentials_cleanup(&creds); - json_decref(data); -} - diff --git a/test/adapter/test_timepoint.cc b/test/adapter/test_timepoint.cc deleted file mode 100644 index 85de35d..0000000 --- a/test/adapter/test_timepoint.cc +++ /dev/null @@ -1,36 +0,0 @@ -#include - -#include "msleep.hpp" -#include "webfuse/adapter/impl/time/timepoint.h" - -using webfuse_test::msleep; - -TEST(timepoint, now) -{ - wf_impl_timepoint start = wf_impl_timepoint_now(); - msleep(42); - wf_impl_timepoint end = wf_impl_timepoint_now(); - - ASSERT_LT(start, end); - ASSERT_LT(end, start + 500); -} - -TEST(timepoint, in_msec) -{ - wf_impl_timepoint now = wf_impl_timepoint_now(); - wf_impl_timepoint later = wf_impl_timepoint_in_msec(42); - - ASSERT_LT(now, later); - ASSERT_LT(later, now + 500); -} - -TEST(wf_impl_timepoint, elapsed) -{ - wf_impl_timepoint now; - - now = wf_impl_timepoint_now(); - ASSERT_TRUE(wf_impl_timepoint_is_elapsed(now - 1)); - - now =wf_impl_timepoint_now(); - ASSERT_FALSE(wf_impl_timepoint_is_elapsed(now + 500)); -} diff --git a/test/adapter/test_timer.cc b/test/adapter/test_timer.cc deleted file mode 100644 index b838147..0000000 --- a/test/adapter/test_timer.cc +++ /dev/null @@ -1,149 +0,0 @@ -#include - -#include - -#include "msleep.hpp" -#include "webfuse/adapter/impl/time/timer.h" -#include "webfuse/adapter/impl/time/timeout_manager.h" - -using std::size_t; -using webfuse_test::msleep; - -namespace -{ - void on_timeout(struct wf_impl_timer * timer) - { - bool * triggered = reinterpret_cast(timer->user_data); - *triggered = true; - } -} - -TEST(timer, init) -{ - struct wf_impl_timeout_manager manager; - struct wf_impl_timer timer; - - wf_impl_timeout_manager_init(&manager); - wf_impl_timer_init(&timer, &manager); - - wf_impl_timer_cleanup(&timer); - wf_impl_timeout_manager_cleanup(&manager); -} - -TEST(timer, trigger) -{ - struct wf_impl_timeout_manager manager; - struct wf_impl_timer timer; - - wf_impl_timeout_manager_init(&manager); - wf_impl_timer_init(&timer, &manager); - - bool triggered = false; - wf_impl_timer_start(&timer, wf_impl_timepoint_in_msec(250), &on_timeout, reinterpret_cast(&triggered)); - msleep(500); - wf_impl_timeout_manager_check(&manager); - - ASSERT_TRUE(triggered); - - wf_impl_timer_cleanup(&timer); - wf_impl_timeout_manager_cleanup(&manager); -} - -TEST(timer, trigger_on_cleanup) -{ - struct wf_impl_timeout_manager manager; - struct wf_impl_timer timer; - - wf_impl_timeout_manager_init(&manager); - wf_impl_timer_init(&timer, &manager); - - bool triggered = false; - wf_impl_timer_start(&timer, wf_impl_timepoint_in_msec(5 * 60 * 1000), &on_timeout, reinterpret_cast(&triggered)); - - wf_impl_timeout_manager_cleanup(&manager); - ASSERT_TRUE(triggered); - - wf_impl_timer_cleanup(&timer); -} - -TEST(timer, cancel) -{ - struct wf_impl_timeout_manager manager; - struct wf_impl_timer timer; - - wf_impl_timeout_manager_init(&manager); - wf_impl_timer_init(&timer, &manager); - - bool triggered = false; - wf_impl_timer_start(&timer, wf_impl_timepoint_in_msec(250), &on_timeout, &triggered); - msleep(500); - wf_impl_timer_cancel(&timer); - wf_impl_timeout_manager_check(&manager); - - ASSERT_FALSE(triggered); - - wf_impl_timer_cleanup(&timer); - wf_impl_timeout_manager_cleanup(&manager); -} - -TEST(timer, cancel_multiple_timers) -{ - static size_t const count = 5; - struct wf_impl_timeout_manager manager; - struct wf_impl_timer timer[count]; - - wf_impl_timeout_manager_init(&manager); - - bool triggered = false; - for(size_t i = 0; i < count; i++) - { - wf_impl_timer_init(&timer[i], &manager); - wf_impl_timer_start(&timer[i], wf_impl_timepoint_in_msec(0), &on_timeout, &triggered); - } - - msleep(10); - for(size_t i = 0; i < count; i++) - { - wf_impl_timer_cancel(&timer[i]); - } - - wf_impl_timeout_manager_check(&manager); - ASSERT_FALSE(triggered); - - for(size_t i = 0; i < count; i++) - { - wf_impl_timer_cleanup(&timer[0]); - } - wf_impl_timeout_manager_cleanup(&manager); -} - -TEST(timer, multiple_timers) -{ - static size_t const count = 5; - struct wf_impl_timeout_manager manager; - struct wf_impl_timer timer[count]; - bool triggered[count]; - - wf_impl_timeout_manager_init(&manager); - - for(size_t i = 0; i < count; i++) - { - wf_impl_timer_init(&timer[i], &manager); - triggered[i] = false; - wf_impl_timer_start(&timer[i], wf_impl_timepoint_in_msec(300 - (50 * i)), &on_timeout, &triggered[i]); - } - - for(size_t i = 0; i < count; i++) - { - msleep(100); - wf_impl_timeout_manager_check(&manager); - } - - for(size_t i = 0; i < count; i++) - { - ASSERT_TRUE(triggered[i]); - wf_impl_timer_cleanup(&timer[i]); - } - - wf_impl_timeout_manager_cleanup(&manager); -} diff --git a/test/core/test_path.cc b/test/core/test_path.cc deleted file mode 100644 index 93783ec..0000000 --- a/test/core/test_path.cc +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include "webfuse/core/path.h" - -TEST(wf_path, empty) -{ - struct wf_path * path = wf_path_create(""); - ASSERT_EQ(0, wf_path_element_count(path)); - ASSERT_EQ(nullptr, wf_path_get_element(path, 0)); - - wf_path_dispose(path); -} - -TEST(wf_path, relative_file) -{ - struct wf_path * path = wf_path_create("some.file"); - ASSERT_EQ(1, wf_path_element_count(path)); - ASSERT_STREQ("some.file", wf_path_get_element(path, 0)); - - wf_path_dispose(path); -} - -TEST(wf_path, absolute_file) -{ - struct wf_path * path = wf_path_create("/absolute.file"); - ASSERT_EQ(1, wf_path_element_count(path)); - ASSERT_STREQ("absolute.file", wf_path_get_element(path, 0)); - - wf_path_dispose(path); -} - -TEST(wf_path, nested_path) -{ - struct wf_path * path = wf_path_create("/a/nested/path"); - ASSERT_EQ(3, wf_path_element_count(path)); - ASSERT_STREQ("a", wf_path_get_element(path, 0)); - ASSERT_STREQ("nested", wf_path_get_element(path, 1)); - ASSERT_STREQ("path", wf_path_get_element(path, 2)); - - wf_path_dispose(path); -} - -TEST(wf_path, deep_nested_path) -{ - struct wf_path * path = wf_path_create("/this/is/a/very/deep/nested/path/to/some/file"); - ASSERT_EQ(10, wf_path_element_count(path)); - ASSERT_STREQ("this", wf_path_get_element(path, 0)); - ASSERT_STREQ("is", wf_path_get_element(path, 1)); - ASSERT_STREQ("a", wf_path_get_element(path, 2)); - ASSERT_STREQ("very", wf_path_get_element(path, 3)); - ASSERT_STREQ("deep", wf_path_get_element(path, 4)); - ASSERT_STREQ("nested", wf_path_get_element(path, 5)); - ASSERT_STREQ("path", wf_path_get_element(path, 6)); - ASSERT_STREQ("to", wf_path_get_element(path, 7)); - ASSERT_STREQ("some", wf_path_get_element(path, 8)); - ASSERT_STREQ("file", wf_path_get_element(path, 9)); - - wf_path_dispose(path); -} \ No newline at end of file diff --git a/test/die_if.cc b/test/die_if.cc deleted file mode 100644 index 3eff5ae..0000000 --- a/test/die_if.cc +++ /dev/null @@ -1,15 +0,0 @@ -#include "die_if.hpp" -#include - -namespace webfuse_test -{ - -void die_if(bool expression) -{ - if (expression) - { - exit(EXIT_FAILURE); - } -} - -} \ No newline at end of file diff --git a/test/die_if.hpp b/test/die_if.hpp deleted file mode 100644 index 95d3ff2..0000000 --- a/test/die_if.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef WF_TEST_DIE_IF_HPP -#define WF_TEST_DIE_IF_HPP - -namespace webfuse_test -{ - -extern void die_if(bool expression); - -} - -#endif diff --git a/test/integration/test_integration.cc b/test/integration/test_integration.cc deleted file mode 100644 index 8bd21c6..0000000 --- a/test/integration/test_integration.cc +++ /dev/null @@ -1,156 +0,0 @@ -#include -#include "integration/server.hpp" -#include "integration/provider.hpp" - -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include "webfuse/core/lws_log.h" -#include "die_if.hpp" - -using webfuse_test::Server; -using webfuse_test::Provider; -using webfuse_test::die_if; - -namespace -{ - class IntegrationTest: public ::testing::Test - { - public: - IntegrationTest() - : server(nullptr) - , provider(nullptr) - { - json_object_seed(0); - wf_lwslog_disable(); - } - - protected: - void SetUp() - { - server = new Server(); - provider = new Provider("ws://localhost:8080/"); - } - - void TearDown() - { - delete provider; - delete server; - } - - char const * GetBaseDir() const - { - return server->GetBaseDir(); - } - private: - Server * server; - Provider * provider; - }; -} - -TEST_F(IntegrationTest, HasMountpoint) -{ - struct stat buffer; - int rc = stat(GetBaseDir(), &buffer); - - ASSERT_EQ(0, rc); - ASSERT_TRUE(S_ISDIR(buffer.st_mode)); -} - -TEST_F(IntegrationTest, ProvidesTextFile) -{ - std::string file_name = std::string(GetBaseDir()) + "/cprovider/default/hello.txt"; - - ASSERT_EXIT({ - struct stat buffer; - int rc = stat(file_name.c_str(), &buffer); - - die_if(0 != rc); - die_if(!S_ISREG(buffer.st_mode)); - die_if(0444 != (buffer.st_mode & 0777)); - die_if(12 != buffer.st_size); - - exit(0); - }, ::testing::ExitedWithCode(0), ".*"); -} - -TEST_F(IntegrationTest, ReadTextFile) -{ - std::string file_name = std::string(GetBaseDir()) + "/cprovider/default/hello.txt"; - - ASSERT_EXIT({ - FILE * file = fopen(file_name.c_str(), "rb"); - die_if(nullptr == file); - - char buffer[13]; - ssize_t count = fread(buffer, 1, 12, file); - int rc = fclose(file); - - die_if(12 != count); - die_if(0 != strncmp("Hello, World", buffer, 12)); - die_if(0 != rc); - - exit(0); - }, ::testing::ExitedWithCode(0), ".*"); - -} - -TEST_F(IntegrationTest, ReadDir) -{ - std::string dir_name = std::string(GetBaseDir()) + "/cprovider/default"; - - ASSERT_EXIT({ - - DIR * dir = opendir(dir_name.c_str()); - die_if(nullptr == dir); - - bool found_self = false; - bool found_parent = false; - bool found_hello_txt = false; - bool found_other = false; - - dirent * entry = readdir(dir); - while (NULL != entry) - { - if (0 == strcmp(".", entry->d_name)) - { - found_self = true; - } - else if (0 == strcmp("..", entry->d_name)) - { - found_parent = true; - } - else if (0 == strcmp("hello.txt", entry->d_name)) - { - found_hello_txt = true; - } - else - { - found_other = true; - } - - - entry = readdir(dir); - } - - closedir(dir); - - die_if(!found_self); - die_if(!found_parent); - die_if(!found_hello_txt); - - die_if(found_other); - - exit(0); - }, ::testing::ExitedWithCode(0), ".*"); - -} \ No newline at end of file diff --git a/test/msleep.cc b/test/msleep.cc deleted file mode 100644 index 5f76e49..0000000 --- a/test/msleep.cc +++ /dev/null @@ -1,19 +0,0 @@ -#include "msleep.hpp" -#include - -namespace webfuse_test -{ - -void msleep(long millis) -{ - long const secs_per_msec = 1000; - long const msecs_per_nsec = (1000 * 1000); - - long const seconds = millis / secs_per_msec; - long const nanos = (millis % secs_per_msec) * msecs_per_nsec; - - struct timespec timeout = { seconds, nanos }; - while (0 != nanosleep(&timeout, &timeout)); -} - -} \ No newline at end of file diff --git a/test/msleep.hpp b/test/msleep.hpp deleted file mode 100644 index 47cb1e6..0000000 --- a/test/msleep.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef WF_TEST_MSLEEP_HPP -#define WF_TEST_MSLEEP_HPP - -namespace webfuse_test -{ - -extern void msleep(long millis); - -} - - -#endif diff --git a/test/provider/test_static_filesystem.cc b/test/provider/test_static_filesystem.cc deleted file mode 100644 index e7db28e..0000000 --- a/test/provider/test_static_filesystem.cc +++ /dev/null @@ -1,61 +0,0 @@ -#include - -#include "webfuse/provider/impl/static_filesystem.h" -#include "webfuse/provider/client_config.h" -#include "webfuse/provider/impl/client_config.h" - -#include "mock_request.hpp" - -using webfuse_test::request_create; -using webfuse_test::MockRequest; -using webfuse_test::GetAttrMatcher; -using webfuse_test::ReaddirMatcher; -using testing::_; - -TEST(wfp_static_filesystem, has_root_dir) -{ - struct wfp_client_config * config = wfp_client_config_create(); - struct wfp_static_filesystem * filesystem = wfp_impl_static_filesystem_create(config); - - MockRequest mock; - struct wfp_request * request = request_create(&mock, 42); - EXPECT_CALL(mock, respond(GetAttrMatcher(1, 0555, "dir"), 42)).Times(1); - - config->provider.getattr(request, 1, config->user_data); - - wfp_impl_static_filesystem_dispose(filesystem); - wfp_client_config_dispose(config); -} - -TEST(wfp_static_filesystem, contains_default_dirs) -{ - struct wfp_client_config * config = wfp_client_config_create(); - struct wfp_static_filesystem * filesystem = wfp_impl_static_filesystem_create(config); - - MockRequest mock; - struct wfp_request * request = request_create(&mock, 23); - char const * default_dirs[] = {".", "..", nullptr}; - EXPECT_CALL(mock, respond(ReaddirMatcher(default_dirs), 23)).Times(1); - - config->provider.readdir(request, 1, config->user_data); - - wfp_impl_static_filesystem_dispose(filesystem); - wfp_client_config_dispose(config); -} - -TEST(wfp_static_filesystem, add_text) -{ - struct wfp_client_config * config = wfp_client_config_create(); - struct wfp_static_filesystem * filesystem = wfp_impl_static_filesystem_create(config); - wfp_impl_static_filesystem_add_text(filesystem, "text.file", 666, "some text"); - - MockRequest mock; - struct wfp_request * request = request_create(&mock, 23); - char const * contained_elements[] = {"text.file", nullptr}; - EXPECT_CALL(mock, respond(ReaddirMatcher(contained_elements), 23)).Times(1); - - config->provider.readdir(request, 1, config->user_data); - - wfp_impl_static_filesystem_dispose(filesystem); - wfp_client_config_dispose(config); -} diff --git a/test/webfuse/mocks/fake_invokation_context.cc b/test/webfuse/mocks/fake_invokation_context.cc new file mode 100644 index 0000000..4aea3ee --- /dev/null +++ b/test/webfuse/mocks/fake_invokation_context.cc @@ -0,0 +1,17 @@ +#include "webfuse/mocks/fake_invokation_context.hpp" + +namespace webfuse_test +{ + +wfp_impl_invokation_context create_context(MockProvider& provider, wfp_request * request) +{ + wfp_impl_invokation_context context = + { + provider.get_provider(), + provider.get_userdata(), + request + }; + return context; +} + +} \ No newline at end of file diff --git a/test/webfuse/mocks/fake_invokation_context.hpp b/test/webfuse/mocks/fake_invokation_context.hpp new file mode 100644 index 0000000..07eb971 --- /dev/null +++ b/test/webfuse/mocks/fake_invokation_context.hpp @@ -0,0 +1,16 @@ +#ifndef FAKE_INVOCATION_CONTEXT_HPP +#define FAKE_INVOCATION_CONTEXT_HPP + +#include "webfuse/provider/impl/provider.h" +#include "webfuse/mocks/mock_provider.hpp" + +namespace webfuse_test +{ + +wfp_impl_invokation_context create_context( + MockProvider& provider, + wfp_request * request = nullptr); + +} + +#endif \ No newline at end of file diff --git a/test/mock_authenticator.cc b/test/webfuse/mocks/mock_authenticator.cc similarity index 94% rename from test/mock_authenticator.cc rename to test/webfuse/mocks/mock_authenticator.cc index f2decab..35648ed 100644 --- a/test/mock_authenticator.cc +++ b/test/webfuse/mocks/mock_authenticator.cc @@ -1,4 +1,4 @@ -#include "mock_authenticator.hpp" +#include "webfuse/mocks/mock_authenticator.hpp" #define WF_AUTHENTICATOR_COUNT 3 diff --git a/test/mock_authenticator.hpp b/test/webfuse/mocks/mock_authenticator.hpp similarity index 100% rename from test/mock_authenticator.hpp rename to test/webfuse/mocks/mock_authenticator.hpp diff --git a/test/webfuse/mocks/mock_fuse.cc b/test/webfuse/mocks/mock_fuse.cc new file mode 100644 index 0000000..486c8ed --- /dev/null +++ b/test/webfuse/mocks/mock_fuse.cc @@ -0,0 +1,31 @@ +#include "webfuse/mocks/mock_fuse.hpp" +#include "webfuse/utils/wrap.hpp" + +extern "C" +{ +static webfuse_test::FuseMock * webfuse_test_FuseMock = nullptr; + +WF_WRAP_FUNC1(webfuse_test_FuseMock, void*, fuse_req_userdata, fuse_req_t); +WF_WRAP_FUNC2(webfuse_test_FuseMock, int, fuse_reply_open, fuse_req_t, const struct fuse_file_info *); +WF_WRAP_FUNC2(webfuse_test_FuseMock, int, fuse_reply_err, fuse_req_t, int); +WF_WRAP_FUNC3(webfuse_test_FuseMock, int, fuse_reply_buf, fuse_req_t, const char *, size_t); +WF_WRAP_FUNC3(webfuse_test_FuseMock, int, fuse_reply_attr, fuse_req_t, const struct stat *, double); +WF_WRAP_FUNC1(webfuse_test_FuseMock, const struct fuse_ctx *, fuse_req_ctx, fuse_req_t); +WF_WRAP_FUNC2(webfuse_test_FuseMock, int, fuse_reply_entry, fuse_req_t, const struct fuse_entry_param *); +} + +namespace webfuse_test +{ + +FuseMock::FuseMock() +{ + webfuse_test_FuseMock = this; +} + +FuseMock::~FuseMock() +{ + webfuse_test_FuseMock = nullptr; +} + + +} \ No newline at end of file diff --git a/test/webfuse/mocks/mock_fuse.hpp b/test/webfuse/mocks/mock_fuse.hpp new file mode 100644 index 0000000..cc3fb71 --- /dev/null +++ b/test/webfuse/mocks/mock_fuse.hpp @@ -0,0 +1,27 @@ +#ifndef MOCK_FUSE_HPP +#define MOCK_FUSE_HPP + +#include "webfuse/adapter/impl/fuse_wrapper.h" +#include + +namespace webfuse_test +{ + +class FuseMock +{ +public: + FuseMock(); + virtual ~FuseMock(); + + MOCK_METHOD1(fuse_req_userdata, void *(fuse_req_t req)); + MOCK_METHOD2(fuse_reply_open, int (fuse_req_t req, const struct fuse_file_info *fi)); + MOCK_METHOD2(fuse_reply_err, int (fuse_req_t req, int err)); + MOCK_METHOD3(fuse_reply_buf, int (fuse_req_t req, const char *buf, size_t size)); + MOCK_METHOD3(fuse_reply_attr, int (fuse_req_t req, const struct stat *attr, double attr_timeout)); + MOCK_METHOD1(fuse_req_ctx, const struct fuse_ctx *(fuse_req_t req)); + MOCK_METHOD2(fuse_reply_entry, int (fuse_req_t req, const struct fuse_entry_param *e)); +}; + +} + +#endif diff --git a/test/webfuse/mocks/mock_jsonrpc_proxy.cc b/test/webfuse/mocks/mock_jsonrpc_proxy.cc new file mode 100644 index 0000000..3fb243e --- /dev/null +++ b/test/webfuse/mocks/mock_jsonrpc_proxy.cc @@ -0,0 +1,34 @@ +#include "webfuse/mocks/mock_jsonrpc_proxy.hpp" +#include "webfuse/utils/wrap.hpp" + +extern "C" +{ +static webfuse_test::MockJsonRpcProxy * webfuse_test_MockJsonRpcProxy = nullptr; + +WF_WRAP_VFUNC5(webfuse_test_MockJsonRpcProxy, void, wf_jsonrpc_proxy_vinvoke, + struct wf_jsonrpc_proxy *, + wf_jsonrpc_proxy_finished_fn *, + void *, + char const *, + char const *); + +WF_WRAP_VFUNC3(webfuse_test_MockJsonRpcProxy, void, wf_jsonrpc_proxy_vnotify, + struct wf_jsonrpc_proxy *, + char const *, + char const *); +} + +namespace webfuse_test +{ + +MockJsonRpcProxy::MockJsonRpcProxy() +{ + webfuse_test_MockJsonRpcProxy = this; +} + +MockJsonRpcProxy::~MockJsonRpcProxy() +{ + webfuse_test_MockJsonRpcProxy = nullptr; +} + +} \ No newline at end of file diff --git a/test/webfuse/mocks/mock_jsonrpc_proxy.hpp b/test/webfuse/mocks/mock_jsonrpc_proxy.hpp new file mode 100644 index 0000000..74d61af --- /dev/null +++ b/test/webfuse/mocks/mock_jsonrpc_proxy.hpp @@ -0,0 +1,30 @@ +#ifndef MOCK_JSONRPC_PROXY_HPP +#define MOCK_JSONRPC_PROXY_HPP + +#include "webfuse/core/jsonrpc/proxy_intern.h" +#include + +namespace webfuse_test +{ + +class MockJsonRpcProxy +{ +public: + MockJsonRpcProxy(); + virtual ~MockJsonRpcProxy(); + MOCK_METHOD5(wf_jsonrpc_proxy_vinvoke, void ( + struct wf_jsonrpc_proxy * proxy, + wf_jsonrpc_proxy_finished_fn * finished, + void * user_data, + char const * method_name, + char const * param_info)); + MOCK_METHOD3(wf_jsonrpc_proxy_vnotify, void ( + struct wf_jsonrpc_proxy * proxy, + char const * method_name, + char const * param_info)); + +}; + +} + +#endif diff --git a/test/webfuse/mocks/mock_operation_context.cc b/test/webfuse/mocks/mock_operation_context.cc new file mode 100644 index 0000000..9537533 --- /dev/null +++ b/test/webfuse/mocks/mock_operation_context.cc @@ -0,0 +1,27 @@ +#include "webfuse/mocks/mock_operation_context.hpp" +#include "webfuse/utils/wrap.hpp" + +extern "C" +{ +static webfuse_test::MockOperationContext * webfuse_test_MockOperationContext = nullptr; + +WF_WRAP_FUNC1(webfuse_test_MockOperationContext, + struct wf_jsonrpc_proxy *, wf_impl_operation_context_get_proxy, + struct wf_impl_operation_context *); + +} + +namespace webfuse_test +{ + +MockOperationContext::MockOperationContext() +{ + webfuse_test_MockOperationContext = this; +} + +MockOperationContext::~MockOperationContext() +{ + webfuse_test_MockOperationContext = nullptr; +} + +} \ No newline at end of file diff --git a/test/webfuse/mocks/mock_operation_context.hpp b/test/webfuse/mocks/mock_operation_context.hpp new file mode 100644 index 0000000..e692e76 --- /dev/null +++ b/test/webfuse/mocks/mock_operation_context.hpp @@ -0,0 +1,22 @@ +#ifndef MOCK_OPERATIONS_CONTEXT_HPP +#define MOCK_OPERATIONS_CONTEXT_HPP + +#include "webfuse/adapter/impl/operation/context.h" +#include + +namespace webfuse_test +{ + +class MockOperationContext +{ +public: + MockOperationContext(); + virtual ~MockOperationContext(); + MOCK_METHOD1(wf_impl_operation_context_get_proxy, wf_jsonrpc_proxy * ( + struct wf_impl_operation_context * context)); + +}; + +} + +#endif diff --git a/test/webfuse/mocks/mock_provider.cc b/test/webfuse/mocks/mock_provider.cc new file mode 100644 index 0000000..1aab4cf --- /dev/null +++ b/test/webfuse/mocks/mock_provider.cc @@ -0,0 +1,130 @@ +#include "webfuse/mocks/mock_provider.hpp" + +extern "C" +{ +using webfuse_test::MockProvider; + +static void webfuse_test_MockProvider_connected( + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->connected(); +} + +static void webfuse_test_MockProvider_disconnected( + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->disconnected(); +} + +static void webfuse_test_MockProvider_lookup( + wfp_request * request, + ino_t parent, + char const * name, + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->lookup(request, parent, name); +} + +static void webfuse_test_MockProvider_getattr( + wfp_request * request, + ino_t inode, + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->getattr(request, inode); +} + +static void webfuse_test_MockProvider_readdir( + wfp_request * request, + ino_t directory, + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->readdir(request, directory); +} + +static void webfuse_test_MockProvider_open( + wfp_request * request, + ino_t inode, + int flags, + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->open(request, inode, flags); + +} + +static void webfuse_test_MockProvider_close( + ino_t inode, + uint32_t handle, + int flags, + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->close(inode, handle, flags); + +} + +static void webfuse_test_MockProvider_read( + wfp_request * request, + ino_t inode, + uint32_t handle, + size_t offset, + size_t length, + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->read(request, inode, handle, offset, length); +} + +static void webfuse_test_MockProvider_get_credentials( + wfp_credentials * credentials, + void * user_data) +{ + auto * provider = reinterpret_cast(user_data); + provider->get_credentials(credentials); +} + + +static wfp_provider const webfuse_test_MockProvider = +{ + &webfuse_test_MockProvider_connected, + &webfuse_test_MockProvider_disconnected, + &webfuse_test_MockProvider_lookup, + &webfuse_test_MockProvider_getattr, + &webfuse_test_MockProvider_readdir, + &webfuse_test_MockProvider_open, + &webfuse_test_MockProvider_close, + &webfuse_test_MockProvider_read, + &webfuse_test_MockProvider_get_credentials, +}; + +} + +namespace webfuse_test +{ + +MockProvider::MockProvider() +{ + +} + +MockProvider::~MockProvider() +{ + +} + +wfp_provider const * MockProvider::get_provider() +{ + return &webfuse_test_MockProvider; +} + +void * MockProvider::get_userdata() +{ + return reinterpret_cast(this); +} + +} \ No newline at end of file diff --git a/test/webfuse/mocks/mock_provider.hpp b/test/webfuse/mocks/mock_provider.hpp new file mode 100644 index 0000000..1f03b08 --- /dev/null +++ b/test/webfuse/mocks/mock_provider.hpp @@ -0,0 +1,31 @@ +#ifndef WF_MOCK_PROVIDER_HPP +#define WF_MOCK_PROVIDER_HPP + +#include "webfuse/provider/impl/provider.h" +#include + +namespace webfuse_test +{ + +class MockProvider +{ +public: + MockProvider(); + ~MockProvider(); + wfp_provider const * get_provider(); + void * get_userdata(); + MOCK_METHOD0(connected, void ()); + MOCK_METHOD0(disconnected, void ()); + MOCK_METHOD0(on_timer, void()); + MOCK_METHOD3(lookup, void(wfp_request * request, ino_t parent, char const * name)); + MOCK_METHOD2(getattr, void(wfp_request * request, ino_t inode)); + MOCK_METHOD2(readdir, void(wfp_request * request, ino_t directory)); + MOCK_METHOD3(open, void(wfp_request * request, ino_t inode, int flags)); + MOCK_METHOD3(close, void(ino_t inode, uint32_t handle, int flags)); + MOCK_METHOD5(read, void(wfp_request * request, ino_t inode, uint32_t handle, size_t offset, size_t length)); + MOCK_METHOD1(get_credentials, void(wfp_credentials * credentials)); +}; + +} + +#endif diff --git a/test/webfuse/mocks/mock_provider_client.cc b/test/webfuse/mocks/mock_provider_client.cc new file mode 100644 index 0000000..779c0ef --- /dev/null +++ b/test/webfuse/mocks/mock_provider_client.cc @@ -0,0 +1,217 @@ +#include "webfuse/mocks/mock_provider_client.hpp" +#include "webfuse/provider/operation/error.h" +#include "webfuse/provider/dirbuffer.h" + +extern "C" +{ +using webfuse_test::IProviderClient; +using webfuse_test::ProviderClientException; + +static void webfuse_test_iproviderclient_onconnected( + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + self->OnConnected(); +} + +static void webfuse_test_iproviderclient_ondisconnected( + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + self->OnDisconnected(); +} + +static void webfuse_test_iproviderclient_onlookup( + struct wfp_request * request, + ino_t parent, + char const * name, + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + + try + { + struct stat buffer; + self->Lookup(parent, name, &buffer); + wfp_respond_lookup(request, &buffer); + } + catch (ProviderClientException& ex) + { + wfp_respond_error(request, ex.GetErrorCode()); + } + catch (...) + { + wfp_respond_error(request, WF_BAD); + } +} + +static void webfuse_test_iproviderclient_ongetattr( + struct wfp_request * request, + ino_t inode, + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + + try + { + struct stat buffer; + memset(&buffer, 0, sizeof(struct stat)); + self->GetAttr(inode, &buffer); + wfp_respond_getattr(request,&buffer); + } + catch (ProviderClientException& ex) + { + wfp_respond_error(request, ex.GetErrorCode()); + } + catch (...) + { + wfp_respond_error(request, WF_BAD); + } + +} + +static void webfuse_test_iproviderclient_onreaddir( + struct wfp_request * request, + ino_t directory, + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + wfp_dirbuffer * buffer = wfp_dirbuffer_create(); + + try + { + self->ReadDir(directory, buffer); + wfp_respond_readdir(request, buffer); + } + catch (ProviderClientException& ex) + { + wfp_respond_error(request, ex.GetErrorCode()); + } + catch (...) + { + wfp_respond_error(request, WF_BAD); + } + + wfp_dirbuffer_dispose(buffer); + +} + +static void webfuse_test_iproviderclient_onopen( + struct wfp_request * request, + ino_t inode, + int flags, + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + + try + { + uint32_t handle = 0; + self->Open(inode, flags, &handle); + wfp_respond_open(request, handle); + } + catch (ProviderClientException& ex) + { + wfp_respond_error(request, ex.GetErrorCode()); + } + catch (...) + { + wfp_respond_error(request, WF_BAD); + } + +} + +static void webfuse_test_iproviderclient_onclose( + ino_t inode, + uint32_t handle, + int flags, + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + self->Close(inode, handle, flags); +} + +static void webfuse_test_iproviderclient_onread( + struct wfp_request * request, + ino_t inode, + uint32_t handle, + size_t offset, + size_t length, + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + char * data = new char[length]; + + try + { + size_t bytes_read = 0; + self->Read(inode, handle, offset, length, data, &bytes_read); + wfp_respond_read(request, data, bytes_read); + } + catch (ProviderClientException& ex) + { + wfp_respond_error(request, ex.GetErrorCode()); + } + catch (...) + { + wfp_respond_error(request, WF_BAD); + } + + delete[] data; +} + +static void webfuse_test_iproviderclient_get_credentials( + wfp_credentials * credentials, + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + + try + { + self->GetCredentials(credentials); + } + catch (...) + { + // swallow + } +} + +} + +namespace webfuse_test +{ + +ProviderClientException::ProviderClientException(wf_status error_code) +: runtime_error("ProviderClientException") +, error_code_(error_code) +{ + +} + +wf_status ProviderClientException::GetErrorCode() +{ + return error_code_; +} + + +void IProviderClient::AttachTo(wfp_client_config * config, bool enableAuthentication) +{ + void * self = reinterpret_cast(this); + wfp_client_config_set_userdata(config, self); + wfp_client_config_set_onconnected(config, &webfuse_test_iproviderclient_onconnected); + wfp_client_config_set_ondisconnected(config, &webfuse_test_iproviderclient_ondisconnected); + + wfp_client_config_set_onlookup(config, &webfuse_test_iproviderclient_onlookup); + wfp_client_config_set_ongetattr(config, &webfuse_test_iproviderclient_ongetattr); + wfp_client_config_set_onreaddir(config, &webfuse_test_iproviderclient_onreaddir); + wfp_client_config_set_onopen(config, &webfuse_test_iproviderclient_onopen); + wfp_client_config_set_onclose(config, &webfuse_test_iproviderclient_onclose); + wfp_client_config_set_onread(config, &webfuse_test_iproviderclient_onread); + + if (enableAuthentication) + { + wfp_client_config_enable_authentication(config, &webfuse_test_iproviderclient_get_credentials); + } +} + +} \ No newline at end of file diff --git a/test/webfuse/mocks/mock_provider_client.hpp b/test/webfuse/mocks/mock_provider_client.hpp new file mode 100644 index 0000000..b60e743 --- /dev/null +++ b/test/webfuse/mocks/mock_provider_client.hpp @@ -0,0 +1,53 @@ +#ifndef WF_MOCK_PROVIDER_CLIENT_HPP +#define WF_MOCK_PROVIDER_CLIENT_HPP + +#include +#include "webfuse/provider/client_config.h" +#include "webfuse/core/status.h" +#include + +namespace webfuse_test +{ + class ProviderClientException: public std::runtime_error + { + public: + explicit ProviderClientException(wf_status error_code); + wf_status GetErrorCode(); + private: + wf_status error_code_; + }; + + class IProviderClient + { + public: + virtual ~IProviderClient() = default; + virtual void OnConnected() = 0; + virtual void OnDisconnected() = 0; + virtual void Lookup(ino_t parent, char const * name, struct stat * result) = 0; + virtual void GetAttr(ino_t inode, struct stat * buffer) = 0; + virtual void ReadDir(ino_t directory, wfp_dirbuffer * buffer) = 0; + virtual void Open(ino_t inode, int flags, uint32_t * handle) = 0; + virtual void Close(ino_t inode, uint32_t handle, int flags) = 0; + virtual void Read(ino_t inode, uint32_t handle, size_t offset, size_t length, char * buffer, size_t * bytes_read) = 0; + virtual void GetCredentials(wfp_credentials * credentials) = 0; + + void AttachTo(wfp_client_config * config, bool enableAuthentication = false); + }; + + class MockProviderClient: public IProviderClient + { + public: + ~MockProviderClient() override = default; + MOCK_METHOD0( OnConnected, void()); + MOCK_METHOD0( OnDisconnected, void()); + MOCK_METHOD3( Lookup, void(ino_t parent, char const * name, struct stat * result)); + MOCK_METHOD2( GetAttr, void(ino_t inode, struct stat * buffer)); + MOCK_METHOD2( ReadDir, void(ino_t directory, wfp_dirbuffer * buffer)); + MOCK_METHOD3( Open, void(ino_t inode, int flags, uint32_t * handle)); + MOCK_METHOD3( Close, void(ino_t inode, uint32_t handle, int flags)); + MOCK_METHOD6( Read, void(ino_t inode, uint32_t handle, size_t offset, size_t length, char * buffer, size_t * bytes_read)); + MOCK_METHOD1( GetCredentials, void (wfp_credentials * credentials)); + }; +} + +#endif diff --git a/test/mock_request.cc b/test/webfuse/mocks/mock_request.cc similarity index 57% rename from test/mock_request.cc rename to test/webfuse/mocks/mock_request.cc index efd8334..5b31c63 100644 --- a/test/mock_request.cc +++ b/test/webfuse/mocks/mock_request.cc @@ -1,15 +1,14 @@ -#include "mock_request.hpp" +#include "webfuse/mocks/mock_request.hpp" #include -namespace +extern "C" { -extern "C" void -respond( +static void webfuse_test_MockRequest_respond( json_t * response, void * user_data) { - webfuse_test::Request * request = reinterpret_cast(user_data); + auto * request = reinterpret_cast(user_data); json_t * result = json_object_get(response, "result"); json_t * error = json_object_get(response, "error"); @@ -31,21 +30,17 @@ respond( } } - } namespace webfuse_test { -struct wfp_request * -request_create( - Request * req, - int id) +struct wfp_request * MockRequest::create_request(int id) { - struct wfp_request * request = reinterpret_cast(malloc(sizeof(struct wfp_request))); + auto * request = reinterpret_cast(malloc(sizeof(wfp_request))); + request->respond = &webfuse_test_MockRequest_respond; + request->user_data = reinterpret_cast(this); request->id = id; - request->user_data = reinterpret_cast(req); - request->respond = &respond; return request; } diff --git a/test/mock_request.hpp b/test/webfuse/mocks/mock_request.hpp similarity index 64% rename from test/mock_request.hpp rename to test/webfuse/mocks/mock_request.hpp index 51884f4..0582d6a 100644 --- a/test/mock_request.hpp +++ b/test/webfuse/mocks/mock_request.hpp @@ -10,27 +10,15 @@ namespace webfuse_test { -class Request -{ -public: - virtual ~Request() { } - virtual void respond(json_t * result, int id) = 0; - virtual void respond_error(json_t * error, int id) = 0; -}; - -class MockRequest: public Request +class MockRequest { public: + struct wfp_request * create_request(int id); MOCK_METHOD2(respond, void(json_t * result, int id)); MOCK_METHOD2(respond_error, void(json_t * error, int id)); }; -extern struct wfp_request * -request_create( - Request * req, - int id); - -MATCHER_P3(GetAttrMatcher, inode, mode, file_type, "") +MATCHER_P3(StatMatcher, inode, mode, file_type, "") { json_t * inode_holder = json_object_get(arg, "inode"); if ((!json_is_integer(inode_holder)) || (inode != json_integer_value(inode_holder))) @@ -56,6 +44,43 @@ MATCHER_P3(GetAttrMatcher, inode, mode, file_type, "") return true; } +MATCHER_P(OpenMatcher, handle, "") +{ + json_t * handle_holder = json_object_get(arg, "handle"); + if ((!json_is_integer(handle_holder)) || (handle != json_integer_value(handle_holder))) + { + *result_listener << "missing handle"; + return false; + } + + return true; +} + +MATCHER_P3(ReadResultMatcher, data, format, count, "") +{ + json_t * format_holder = json_object_get(arg, "format"); + if ((!json_is_string(format_holder)) || (0 != strcmp(format, json_string_value(format_holder)))) + { + *result_listener << "invalid or missing format: " << json_string_value(format_holder); + return false; + } + + json_t * count_holder = json_object_get(arg, "count"); + if ((!json_is_integer(count_holder)) || (count != json_integer_value(count_holder))) + { + *result_listener << "invalid or missing count: " << json_integer_value(count_holder); + return false; + } + + json_t * data_holder = json_object_get(arg, "data"); + if ((!json_is_string(data_holder)) || (0 != strcmp(data, json_string_value(data_holder)))) + { + *result_listener << "invalid or missing data: " << json_string_value(data_holder); + return false; + } + + return true; +} MATCHER_P(ReaddirMatcher, contained_elements , "") { diff --git a/test/webfuse/tests/adapter/operation/test_close.cc b/test/webfuse/tests/adapter/operation/test_close.cc new file mode 100644 index 0000000..00e4763 --- /dev/null +++ b/test/webfuse/tests/adapter/operation/test_close.cc @@ -0,0 +1,56 @@ +#include "webfuse/adapter/impl/operation/close.h" +#include + +#include "webfuse/mocks/mock_fuse.hpp" +#include "webfuse/mocks/mock_operation_context.hpp" +#include "webfuse/mocks/mock_jsonrpc_proxy.hpp" + +#include + +using webfuse_test::MockJsonRpcProxy; +using webfuse_test::MockOperationContext; +using webfuse_test::FuseMock; +using testing::_; +using testing::Return; +using testing::StrEq; + +TEST(wf_impl_operation_close, notify_proxy) +{ + MockJsonRpcProxy proxy; + EXPECT_CALL(proxy, wf_jsonrpc_proxy_vnotify(_,StrEq("close"),StrEq("siii"))).Times(1); + + MockOperationContext context; + EXPECT_CALL(context, wf_impl_operation_context_get_proxy(_)).Times(1) + .WillOnce(Return(reinterpret_cast(&proxy))); + + wf_impl_operation_context op_context; + op_context.name = nullptr; + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(&op_context)); + EXPECT_CALL(fuse, fuse_reply_err(_, 0)).Times(1).WillOnce(Return(0)); + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + fuse_file_info file_info; + file_info.flags = 0; + wf_impl_operation_close(request, inode, &file_info); +} + +TEST(wf_impl_operation_close, fail_rpc_null) +{ + MockJsonRpcProxy proxy; + EXPECT_CALL(proxy, wf_jsonrpc_proxy_vnotify(_,_,_)).Times(0); + + MockOperationContext context; + EXPECT_CALL(context, wf_impl_operation_context_get_proxy(_)).Times(1) + .WillOnce(Return(nullptr)); + + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(nullptr)); + EXPECT_CALL(fuse, fuse_reply_err(_, 0)).Times(1).WillOnce(Return(0)); + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + fuse_file_info * file_info = nullptr; + wf_impl_operation_close(request, inode, file_info); +} diff --git a/test/webfuse/tests/adapter/operation/test_context.cc b/test/webfuse/tests/adapter/operation/test_context.cc new file mode 100644 index 0000000..77aa287 --- /dev/null +++ b/test/webfuse/tests/adapter/operation/test_context.cc @@ -0,0 +1,23 @@ +#include "webfuse/adapter/impl/operation/context.h" +#include "webfuse/adapter/impl/session.h" +#include + +TEST(wf_impl_operation_context, get_proxy) +{ + wf_jsonrpc_proxy * proxy = reinterpret_cast(42); + wf_impl_session session; + session.rpc = proxy; + wf_impl_operation_context context; + context.session = &session; + + ASSERT_EQ(proxy, wf_impl_operation_context_get_proxy(&context)); +} + +TEST(wf_impl_operation_context, get_proxy_fail_no_session) +{ + wf_impl_operation_context context; + context.session = nullptr; + + ASSERT_EQ(nullptr, wf_impl_operation_context_get_proxy(&context)); + +} \ No newline at end of file diff --git a/test/webfuse/tests/adapter/operation/test_getattr.cc b/test/webfuse/tests/adapter/operation/test_getattr.cc new file mode 100644 index 0000000..8249692 --- /dev/null +++ b/test/webfuse/tests/adapter/operation/test_getattr.cc @@ -0,0 +1,213 @@ +#include "webfuse/adapter/impl/operation/getattr.h" + +#include "webfuse/core/status.h" + +#include "webfuse/mocks/mock_fuse.hpp" +#include "webfuse/mocks/mock_operation_context.hpp" +#include "webfuse/mocks/mock_jsonrpc_proxy.hpp" + +#include + +using webfuse_test::MockJsonRpcProxy; +using webfuse_test::MockOperationContext; +using webfuse_test::FuseMock; +using testing::_; +using testing::Return; +using testing::Invoke; +using testing::StrEq; + +namespace +{ + +void free_context( + struct wf_jsonrpc_proxy * , + wf_jsonrpc_proxy_finished_fn * , + void * user_data, + char const * , + char const *) +{ + free(user_data); +} + +} + +TEST(wf_impl_operation_getattr, invoke_proxy) +{ + MockJsonRpcProxy proxy; + EXPECT_CALL(proxy, wf_jsonrpc_proxy_vinvoke(_,_,_,StrEq("getattr"),StrEq("si"))).Times(1) + .WillOnce(Invoke(free_context)); + + MockOperationContext context; + EXPECT_CALL(context, wf_impl_operation_context_get_proxy(_)).Times(1) + .WillOnce(Return(reinterpret_cast(&proxy))); + + wf_impl_operation_context op_context; + op_context.name = nullptr; + fuse_ctx fuse_context; + fuse_context.gid = 0; + fuse_context.uid = 0; + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_ctx(_)).Times(1).WillOnce(Return(&fuse_context)); + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(&op_context)); + + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + fuse_file_info * file_info = nullptr; + wf_impl_operation_getattr(request, inode, file_info); +} + +TEST(wf_impl_operation_getattr, fail_rpc_null) +{ + MockOperationContext context; + EXPECT_CALL(context, wf_impl_operation_context_get_proxy(_)).Times(1) + .WillOnce(Return(nullptr)); + + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_ctx(_)).Times(1).WillOnce(Return(nullptr)); + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(nullptr)); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + fuse_file_info * file_info = nullptr; + wf_impl_operation_getattr(request, inode, file_info); +} + +TEST(wf_impl_operation_getattr, finished_file) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_attr(_,_,_)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "mode", json_integer(0755)); + json_object_set_new(result, "type", json_string("file")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_getattr_context))); + context->inode = 1; + context->gid = 0; + context->uid = 0; + wf_impl_operation_getattr_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_getattr, finished_dir) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_attr(_,_,_)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "mode", json_integer(0755)); + json_object_set_new(result, "type", json_string("dir")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_getattr_context))); + context->inode = 1; + context->gid = 0; + context->uid = 0; + wf_impl_operation_getattr_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_getattr, finished_unknown_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_attr(_,_,_)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "mode", json_integer(0755)); + json_object_set_new(result, "type", json_string("unknown")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_getattr_context))); + context->inode = 1; + context->gid = 0; + context->uid = 0; + wf_impl_operation_getattr_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_getattr, finished_fail_missing_mode) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "type", json_string("file")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_getattr_context))); + context->inode = 1; + context->gid = 0; + context->uid = 0; + wf_impl_operation_getattr_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_getattr, finished_fail_invalid_mode_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "mode", json_string("0755")); + json_object_set_new(result, "type", json_string("file")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_getattr_context))); + context->inode = 1; + context->gid = 0; + context->uid = 0; + wf_impl_operation_getattr_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_getattr, finished_fail_type_mode) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "mode", json_integer(0755)); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_getattr_context))); + context->inode = 1; + context->gid = 0; + context->uid = 0; + wf_impl_operation_getattr_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_getattr, finished_fail_invalid_type_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "mode", json_integer(0755)); + json_object_set_new(result, "type", json_integer(42)); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_getattr_context))); + context->inode = 1; + context->gid = 0; + context->uid = 0; + wf_impl_operation_getattr_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_getattr, finished_error) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * error = json_object(); + json_object_set_new(error, "code", json_integer(WF_BAD)); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_getattr_context))); + context->inode = 1; + context->gid = 0; + context->uid = 0; + wf_impl_operation_getattr_finished(context, nullptr, error); + json_decref(error); +} diff --git a/test/webfuse/tests/adapter/operation/test_lookup.cc b/test/webfuse/tests/adapter/operation/test_lookup.cc new file mode 100644 index 0000000..002fc30 --- /dev/null +++ b/test/webfuse/tests/adapter/operation/test_lookup.cc @@ -0,0 +1,255 @@ +#include "webfuse/adapter/impl/operation/lookup.h" + +#include "webfuse/core/status.h" + +#include "webfuse/mocks/mock_fuse.hpp" +#include "webfuse/mocks/mock_operation_context.hpp" +#include "webfuse/mocks/mock_jsonrpc_proxy.hpp" + +#include + +using webfuse_test::MockJsonRpcProxy; +using webfuse_test::MockOperationContext; +using webfuse_test::FuseMock; +using testing::_; +using testing::Return; +using testing::Invoke; +using testing::StrEq; + +namespace +{ + +void free_context( + struct wf_jsonrpc_proxy * , + wf_jsonrpc_proxy_finished_fn * , + void * user_data, + char const * , + char const *) +{ + free(user_data); +} + +} + +TEST(wf_impl_operation_lookup, invoke_proxy) +{ + MockJsonRpcProxy proxy; + EXPECT_CALL(proxy, wf_jsonrpc_proxy_vinvoke(_,_,_,StrEq("lookup"),StrEq("sis"))).Times(1) + .WillOnce(Invoke(free_context)); + + MockOperationContext context; + EXPECT_CALL(context, wf_impl_operation_context_get_proxy(_)).Times(1) + .WillOnce(Return(reinterpret_cast(&proxy))); + + wf_impl_operation_context op_context; + op_context.name = nullptr; + fuse_ctx fuse_context; + fuse_context.gid = 0; + fuse_context.uid = 0; + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_ctx(_)).Times(1).WillOnce(Return(&fuse_context)); + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(&op_context)); + + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + wf_impl_operation_lookup(request, inode, "some.file"); +} + +TEST(wf_impl_operation_lookup, fail_rpc_null) +{ + MockOperationContext context; + EXPECT_CALL(context, wf_impl_operation_context_get_proxy(_)).Times(1) + .WillOnce(Return(nullptr)); + + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_ctx(_)).Times(1).WillOnce(Return(nullptr)); + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(nullptr)); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + wf_impl_operation_lookup(request, inode, "some.file"); +} + +TEST(wf_impl_operation_lookup, finished_file) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_entry(_,_)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "inode", json_integer(42)); + json_object_set_new(result, "mode", json_integer(0755)); + json_object_set_new(result, "type", json_string("file")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_lookup_context))); + context->timeout = 1.0; + context->gid = 0; + context->uid = 0; + wf_impl_operation_lookup_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_lookup, finished_dir) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_entry(_,_)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "inode", json_integer(42)); + json_object_set_new(result, "mode", json_integer(0755)); + json_object_set_new(result, "type", json_string("dir")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_lookup_context))); + context->timeout = 1.0; + context->gid = 0; + context->uid = 0; + wf_impl_operation_lookup_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_lookup, finished_unknown_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_entry(_,_)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "inode", json_integer(42)); + json_object_set_new(result, "mode", json_integer(0755)); + json_object_set_new(result, "type", json_string("unknown")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_lookup_context))); + context->timeout = 1.0; + context->gid = 0; + context->uid = 0; + wf_impl_operation_lookup_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_lookup, finished_fail_missing_inode) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "mode", json_integer(0755)); + json_object_set_new(result, "type", json_string("file")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_lookup_context))); + context->timeout = 1.0; + context->gid = 0; + context->uid = 0; + wf_impl_operation_lookup_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_lookup, finished_fail_invalid_inode_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "inode", json_string("42")); + json_object_set_new(result, "mode", json_string("0755")); + json_object_set_new(result, "type", json_string("file")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_lookup_context))); + context->timeout = 1.0; + context->gid = 0; + context->uid = 0; + wf_impl_operation_lookup_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_lookup, finished_fail_missing_mode) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "inode", json_integer(42)); + json_object_set_new(result, "type", json_string("file")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_lookup_context))); + context->timeout = 1.0; + context->gid = 0; + context->uid = 0; + wf_impl_operation_lookup_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_lookup, finished_fail_invalid_mode_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "inode", json_integer(42)); + json_object_set_new(result, "mode", json_string("0755")); + json_object_set_new(result, "type", json_string("file")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_lookup_context))); + context->timeout = 1.0; + context->gid = 0; + context->uid = 0; + wf_impl_operation_lookup_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_lookup, finished_fail_type_mode) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "inode", json_integer(42)); + json_object_set_new(result, "mode", json_integer(0755)); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_lookup_context))); + context->timeout = 1.0; + context->gid = 0; + context->uid = 0; + wf_impl_operation_lookup_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_lookup, finished_fail_invalid_type_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "inode", json_integer(42)); + json_object_set_new(result, "mode", json_integer(0755)); + json_object_set_new(result, "type", json_integer(42)); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_lookup_context))); + context->timeout = 1.0; + context->gid = 0; + context->uid = 0; + wf_impl_operation_lookup_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_lookup, finished_error) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * error = json_object(); + json_object_set_new(error, "code", json_integer(WF_BAD)); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_lookup_context))); + context->timeout = 1.0; + context->gid = 0; + context->uid = 0; + wf_impl_operation_lookup_finished(context, nullptr, error); + json_decref(error); +} diff --git a/test/webfuse/tests/adapter/operation/test_open.cc b/test/webfuse/tests/adapter/operation/test_open.cc new file mode 100644 index 0000000..e3d89bc --- /dev/null +++ b/test/webfuse/tests/adapter/operation/test_open.cc @@ -0,0 +1,100 @@ +#include "webfuse/adapter/impl/operation/open.h" + +#include "webfuse/core/status.h" + +#include "webfuse/mocks/mock_fuse.hpp" +#include "webfuse/mocks/mock_operation_context.hpp" +#include "webfuse/mocks/mock_jsonrpc_proxy.hpp" + +#include + +using webfuse_test::MockJsonRpcProxy; +using webfuse_test::MockOperationContext; +using webfuse_test::FuseMock; +using testing::_; +using testing::Return; +using testing::StrEq; + +TEST(wf_impl_operation_open, invoke_proxy) +{ + MockJsonRpcProxy proxy; + EXPECT_CALL(proxy, wf_jsonrpc_proxy_vinvoke(_,_,_,StrEq("open"),StrEq("sii"))).Times(1); + + MockOperationContext context; + EXPECT_CALL(context, wf_impl_operation_context_get_proxy(_)).Times(1) + .WillOnce(Return(reinterpret_cast(&proxy))); + + wf_impl_operation_context op_context; + op_context.name = nullptr; + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(&op_context)); + + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + fuse_file_info file_info; + file_info.flags = 0; + wf_impl_operation_open(request, inode, &file_info); +} + +TEST(wf_impl_operation_open, fail_rpc_null) +{ + MockOperationContext context; + EXPECT_CALL(context, wf_impl_operation_context_get_proxy(_)).Times(1) + .WillOnce(Return(nullptr)); + + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(nullptr)); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + fuse_file_info * file_info = nullptr; + wf_impl_operation_open(request, inode, file_info); +} + +TEST(wf_impl_operation_open, finished) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "handle", json_integer(42)); + wf_impl_operation_open_finished(nullptr, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_open, finished_fail_error) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * error = json_object(); + json_object_set_new(error, "code", json_integer(WF_BAD)); + wf_impl_operation_open_finished(nullptr, nullptr, error); + json_decref(error); +} + +TEST(wf_impl_operation_open, finished_fail_no_handle) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + wf_impl_operation_open_finished(nullptr, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_open, finished_fail_invalid_handle_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "handle", json_string("42")); + wf_impl_operation_open_finished(nullptr, result, nullptr); + json_decref(result); +} diff --git a/test/webfuse/tests/adapter/operation/test_read.cc b/test/webfuse/tests/adapter/operation/test_read.cc new file mode 100644 index 0000000..15b9f9c --- /dev/null +++ b/test/webfuse/tests/adapter/operation/test_read.cc @@ -0,0 +1,236 @@ +#include "webfuse/adapter/impl/operation/read.h" + +#include "webfuse/mocks/mock_fuse.hpp" +#include "webfuse/mocks/mock_operation_context.hpp" +#include "webfuse/mocks/mock_jsonrpc_proxy.hpp" + +#include + +using webfuse_test::MockJsonRpcProxy; +using webfuse_test::MockOperationContext; +using webfuse_test::FuseMock; +using testing::_; +using testing::Return; +using testing::StrEq; + +TEST(wf_impl_operation_read, invoke_proxy) +{ + MockJsonRpcProxy proxy; + EXPECT_CALL(proxy, wf_jsonrpc_proxy_vinvoke(_,_,_,StrEq("read"),StrEq("siiii"))).Times(1); + + MockOperationContext context; + EXPECT_CALL(context, wf_impl_operation_context_get_proxy(_)).Times(1) + .WillOnce(Return(reinterpret_cast(&proxy))); + + wf_impl_operation_context op_context; + op_context.name = nullptr; + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(&op_context)); + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + size_t size = 42; + off_t offset = 0; + fuse_file_info file_info; + file_info.fh = 1; + wf_impl_operation_read(request, inode, size, offset, &file_info); +} + +TEST(wf_impl_operation_read, invoke_proxy_limit_size) +{ + MockJsonRpcProxy proxy; + EXPECT_CALL(proxy, wf_jsonrpc_proxy_vinvoke(_,_,_,StrEq("read"),StrEq("siiii"))).Times(1); + + MockOperationContext context; + EXPECT_CALL(context, wf_impl_operation_context_get_proxy(_)).Times(1) + .WillOnce(Return(reinterpret_cast(&proxy))); + + wf_impl_operation_context op_context; + op_context.name = nullptr; + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(&op_context)); + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + size_t size = 100 * 1024 * 1024; + off_t offset = 0; + fuse_file_info file_info; + file_info.fh = 1; + wf_impl_operation_read(request, inode, size, offset, &file_info); +} + +TEST(wf_impl_operation_read, fail_rpc_null) +{ + MockOperationContext context; + EXPECT_CALL(context, wf_impl_operation_context_get_proxy(_)).Times(1) + .WillOnce(Return(nullptr)); + + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(nullptr)); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + size_t size = 42; + off_t offset = 0; + fuse_file_info * file_info = nullptr; + wf_impl_operation_read(request, inode, size, offset, file_info); +} + +TEST(wf_impl_operation_read, fill_buffer_identity) +{ + wf_status status; + char * buffer = wf_impl_fill_buffer("brummni", 8, "identity", 8, &status); + ASSERT_EQ(WF_GOOD, status); + ASSERT_STREQ("brummni", buffer); + free(buffer); +} + +TEST(wf_impl_operation_read, fill_buffer_identity_fail_inconsistent_size) +{ + wf_status status; + char * buffer = wf_impl_fill_buffer("brummni", 8, "identity", 7, &status); + ASSERT_NE(WF_GOOD, status); + ASSERT_EQ(nullptr, buffer); +} + +TEST(wf_impl_operation_read, fill_buffer_base64) +{ + wf_status status; + char * buffer = wf_impl_fill_buffer("YnJ1bW1uaQ==", 12, "base64", 7, &status); + + ASSERT_EQ(WF_GOOD, status); + ASSERT_EQ(0, strncmp("brummni", buffer, 7)); + free(buffer); +} + +TEST(wf_impl_operation_read, fill_buffer_base64_fail_invalid_data) +{ + wf_status status; + char * buffer = wf_impl_fill_buffer("YnJ1bW1uaQ=A", 12, "base64", 8, &status); + ASSERT_NE(WF_GOOD, status); + ASSERT_EQ(nullptr, buffer); +} + +TEST(wf_impl_operation_read, fill_buffer_empty) +{ + wf_status status; + char * buffer = wf_impl_fill_buffer(nullptr, 0, "identity", 0, &status); + + ASSERT_EQ(WF_GOOD, status); + free(buffer); +} + +TEST(wf_impl_operation_read, fill_buffer_fail_invalid_format) +{ + wf_status status; + char * buffer = wf_impl_fill_buffer("some data", 9, "unknown", 9, &status); + ASSERT_NE(WF_GOOD, status); + ASSERT_EQ(nullptr, buffer); +} + +TEST(wf_impl_operation_read, finished) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,7)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "data", json_string("brummni")); + json_object_set_new(result, "format", json_string("identity")); + json_object_set_new(result, "count", json_integer(7)); + wf_impl_operation_read_finished(nullptr, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_read, finished_fail_no_data) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, _)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "format", json_string("identity")); + json_object_set_new(result, "count", json_integer(7)); + wf_impl_operation_read_finished(nullptr, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_read, finished_fail_invalid_data_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, _)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "data", json_integer(42)); + json_object_set_new(result, "format", json_string("identity")); + json_object_set_new(result, "count", json_integer(7)); + wf_impl_operation_read_finished(nullptr, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_read, finished_fail_no_format) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, _)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "data", json_string("brummni")); + json_object_set_new(result, "count", json_integer(7)); + wf_impl_operation_read_finished(nullptr, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_read, finished_fail_invalid_format_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, _)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "data", json_string("brummni")); + json_object_set_new(result, "format", json_integer(42)); + json_object_set_new(result, "count", json_integer(7)); + wf_impl_operation_read_finished(nullptr, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_read, finished_fail_no_count) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, _)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "data", json_string("brummni")); + json_object_set_new(result, "format", json_string("identity")); + wf_impl_operation_read_finished(nullptr, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_read, finished_fail_invalid_count_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, _)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "data", json_string("brummni")); + json_object_set_new(result, "format", json_string("identity")); + json_object_set_new(result, "count", json_string("7")); + wf_impl_operation_read_finished(nullptr, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_read, finished_fail_error) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, _)).Times(1).WillOnce(Return(0)); + + json_t * error = json_object(); + json_object_set_new(error, "code", json_integer(WF_BAD)); + wf_impl_operation_read_finished(nullptr, nullptr, error); + json_decref(error); +} diff --git a/test/webfuse/tests/adapter/operation/test_readdir.cc b/test/webfuse/tests/adapter/operation/test_readdir.cc new file mode 100644 index 0000000..c020c3d --- /dev/null +++ b/test/webfuse/tests/adapter/operation/test_readdir.cc @@ -0,0 +1,265 @@ +#include "webfuse/adapter/impl/operation/readdir.h" + +#include "webfuse/core/status.h" + +#include "webfuse/mocks/mock_fuse.hpp" +#include "webfuse/mocks/mock_operation_context.hpp" +#include "webfuse/mocks/mock_jsonrpc_proxy.hpp" + +#include + +using webfuse_test::MockJsonRpcProxy; +using webfuse_test::MockOperationContext; +using webfuse_test::FuseMock; +using testing::_; +using testing::Return; +using testing::Invoke; +using testing::StrEq; + +namespace +{ + +void free_context( + struct wf_jsonrpc_proxy * , + wf_jsonrpc_proxy_finished_fn * , + void * user_data, + char const * , + char const *) +{ + free(user_data); +} + +} + +TEST(wf_impl_operation_readdir, invoke_proxy) +{ + MockJsonRpcProxy proxy; + EXPECT_CALL(proxy, wf_jsonrpc_proxy_vinvoke(_,_,_,StrEq("readdir"),StrEq("si"))) + .Times(1).WillOnce(Invoke(free_context)); + + MockOperationContext context; + EXPECT_CALL(context, wf_impl_operation_context_get_proxy(_)).Times(1) + .WillOnce(Return(reinterpret_cast(&proxy))); + + wf_impl_operation_context op_context; + op_context.name = nullptr; + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(&op_context)); + + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + size_t size = 10; + size_t offset = 0; + fuse_file_info file_info; + file_info.flags = 0; + wf_impl_operation_readdir(request, inode, size, offset, &file_info); +} + +TEST(wf_impl_operation_readdir, fail_rpc_null) +{ + MockOperationContext context; + EXPECT_CALL(context, wf_impl_operation_context_get_proxy(_)).Times(1) + .WillOnce(Return(nullptr)); + + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(nullptr)); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + size_t size = 10; + size_t offset = 0; + fuse_file_info file_info; + file_info.flags = 0; + wf_impl_operation_readdir(request, inode, size, offset, &file_info); +} + +TEST(wf_impl_operation_readdir, finished) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(1).WillOnce(Return(0)); + + json_t * result = json_array(); + json_t * item = json_object(); + json_object_set_new(item, "name", json_string("a.file")); + json_object_set_new(item, "inode", json_integer(42)); + json_array_append_new(result, item); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 1; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_readdir, finished_many_items) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(1).WillOnce(Return(0)); + + json_t * result = json_array(); + for(int i = 0; i < 100; i++) + { + json_t * item = json_object(); + json_object_set_new(item, "name", json_sprintf("file_%d.txt", i)); + json_object_set_new(item, "inode", json_integer(1 + i)); + json_array_append_new(result, item); + } + + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 100; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_readdir, finished_read_after_end) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(1).WillOnce(Return(0)); + + json_t * result = json_array(); + json_t * item = json_object(); + json_object_set_new(item, "name", json_string("a.file")); + json_object_set_new(item, "inode", json_integer(42)); + json_array_append_new(result, item); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 10; + context->offset = 2; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_readdir, finished_fail_error) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * error = json_array(); + json_object_set_new(error, "code", json_integer(WF_BAD)); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 1; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), nullptr, error); + json_decref(error); +} + +TEST(wf_impl_operation_readdir, finished_fail_invalid_result_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 1; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_readdir, finished_fail_missing_name) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_array(); + json_t * item = json_object(); + json_object_set_new(item, "inode", json_integer(42)); + json_array_append_new(result, item); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 1; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_readdir, finished_fail_invalid_name_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_array(); + json_t * item = json_object(); + json_object_set_new(item, "name", json_integer(42)); + json_object_set_new(item, "inode", json_integer(42)); + json_array_append_new(result, item); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 1; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_readdir, finished_fail_missing_inode) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_array(); + json_t * item = json_object(); + json_object_set_new(item, "name", json_string("a.file")); + json_array_append_new(result, item); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 1; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_readdir, finished_fail_invalid_inode_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_array(); + json_t * item = json_object(); + json_object_set_new(item, "name", json_string("a.file")); + json_object_set_new(item, "inode", json_string("42")); + json_array_append_new(result, item); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 1; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_readdir, finished_fail_invalid_item_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_array(); + json_array_append_new(result, json_string("item")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 1; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +} diff --git a/test/adapter/test_authenticator.cc b/test/webfuse/tests/adapter/test_authenticator.cc similarity index 97% rename from test/adapter/test_authenticator.cc rename to test/webfuse/tests/adapter/test_authenticator.cc index 93505a9..07e06e5 100644 --- a/test/adapter/test_authenticator.cc +++ b/test/webfuse/tests/adapter/test_authenticator.cc @@ -1,7 +1,7 @@ #include #include -#include "mock_authenticator.hpp" +#include "webfuse/mocks/mock_authenticator.hpp" #include "webfuse/adapter/impl/authenticator.h" #include "webfuse/adapter/impl/credentials.h" diff --git a/test/adapter/test_authenticators.cc b/test/webfuse/tests/adapter/test_authenticators.cc similarity index 99% rename from test/adapter/test_authenticators.cc rename to test/webfuse/tests/adapter/test_authenticators.cc index 3227e57..6cb8064 100644 --- a/test/adapter/test_authenticators.cc +++ b/test/webfuse/tests/adapter/test_authenticators.cc @@ -3,7 +3,7 @@ #include "webfuse/adapter/impl/authenticators.h" #include "webfuse/adapter/impl/credentials.h" -#include "mock_authenticator.hpp" +#include "webfuse/mocks/mock_authenticator.hpp" using ::testing::_; using ::testing::Return; diff --git a/test/webfuse/tests/adapter/test_credentials.cc b/test/webfuse/tests/adapter/test_credentials.cc new file mode 100644 index 0000000..88e2552 --- /dev/null +++ b/test/webfuse/tests/adapter/test_credentials.cc @@ -0,0 +1,85 @@ +#include + +#include "webfuse/adapter/credentials.h" +#include "webfuse/adapter/impl/credentials.h" +#include + +TEST(Credentials, Type) +{ + struct wf_credentials creds; + + wf_impl_credentials_init(&creds, "test", nullptr); + ASSERT_STREQ("test", wf_credentials_type(&creds)); + wf_impl_credentials_cleanup(&creds); +} + +TEST(Credentials, Get) +{ + struct wf_credentials creds; + json_t * data = json_object(); + json_object_set_new(data, "username", json_string("bob")); + json_object_set_new(data, "password", json_string("")); + + wf_impl_credentials_init(&creds, "username", data); + ASSERT_STREQ("username", wf_credentials_type(&creds)); + ASSERT_STREQ("bob", wf_credentials_get(&creds, "username")); + ASSERT_STREQ("", wf_credentials_get(&creds, "password")); + + wf_impl_credentials_cleanup(&creds); + json_decref(data); +} + +TEST(Credentials, FailedToGetNonexistingValue) +{ + struct wf_credentials creds; + json_t * data = json_object(); + + wf_impl_credentials_init(&creds, "username", data); + ASSERT_STREQ("username", wf_credentials_type(&creds)); + ASSERT_STREQ(nullptr, wf_credentials_get(&creds, "username")); + ASSERT_STREQ(nullptr, wf_credentials_get(&creds, "password")); + + wf_impl_credentials_cleanup(&creds); + json_decref(data); +} + +TEST(Credentials, FailedToGetWithoutData) +{ + struct wf_credentials creds; + + wf_impl_credentials_init(&creds, "username", nullptr); + ASSERT_STREQ("username", wf_credentials_type(&creds)); + ASSERT_STREQ(nullptr, wf_credentials_get(&creds, "username")); + ASSERT_STREQ(nullptr, wf_credentials_get(&creds, "password")); + + wf_impl_credentials_cleanup(&creds); +} + +TEST(Credentials, FailedToGetWrongDataType) +{ + struct wf_credentials creds; + json_t * data = json_string("invalid_creds"); + + wf_impl_credentials_init(&creds, "username", data); + ASSERT_STREQ("username", wf_credentials_type(&creds)); + ASSERT_STREQ(nullptr, wf_credentials_get(&creds, "username")); + ASSERT_STREQ(nullptr, wf_credentials_get(&creds, "password")); + + wf_impl_credentials_cleanup(&creds); + json_decref(data); +} + +TEST(Credentials, FailedToGetWrongElementDataType) +{ + struct wf_credentials creds; + json_t * data = json_object(); + json_object_set_new(data, "username", json_integer(42)); + + wf_impl_credentials_init(&creds, "username", data); + ASSERT_STREQ("username", wf_credentials_type(&creds)); + ASSERT_STREQ(nullptr, wf_credentials_get(&creds, "username")); + ASSERT_STREQ(nullptr, wf_credentials_get(&creds, "password")); + + wf_impl_credentials_cleanup(&creds); + json_decref(data); +} diff --git a/test/adapter/test_fuse_req.cc b/test/webfuse/tests/adapter/test_fuse_req.cc similarity index 100% rename from test/adapter/test_fuse_req.cc rename to test/webfuse/tests/adapter/test_fuse_req.cc diff --git a/test/webfuse/tests/adapter/test_mountpoint.cc b/test/webfuse/tests/adapter/test_mountpoint.cc new file mode 100644 index 0000000..6bb486b --- /dev/null +++ b/test/webfuse/tests/adapter/test_mountpoint.cc @@ -0,0 +1,47 @@ +#include +#include +#include "webfuse/adapter/mountpoint.h" + +namespace +{ + class MockUserDataDisposer + { + public: + MOCK_METHOD1(dispose, void(void * mountpoint)); + }; + + MockUserDataDisposer * global_disposer = nullptr; + + void ondispose(void * user_data) + { + global_disposer->dispose(user_data); + } +} + +TEST(mountpoint, get_path) +{ + wf_mountpoint * mountpoint = wf_mountpoint_create("/some/path"); + ASSERT_NE(nullptr, mountpoint); + + ASSERT_STREQ("/some/path", wf_mountpoint_get_path(mountpoint)); + + wf_mountpoint_dispose(mountpoint); +} + + + +TEST(mountpoint, ondispose) +{ + MockUserDataDisposer disposer; + global_disposer = &disposer; + + wf_mountpoint * mountpoint = wf_mountpoint_create("/some/path"); + ASSERT_NE(nullptr, mountpoint); + + int value = 42; + void * user_data = reinterpret_cast(&value); + wf_mountpoint_set_userdata(mountpoint, user_data, ondispose); + EXPECT_CALL(disposer, dispose(user_data)).Times(1); + + wf_mountpoint_dispose(mountpoint); +} \ No newline at end of file diff --git a/test/adapter/test_server.cc b/test/webfuse/tests/adapter/test_server.cc similarity index 65% rename from test/adapter/test_server.cc rename to test/webfuse/tests/adapter/test_server.cc index ef8fa25..1b2a881 100644 --- a/test/adapter/test_server.cc +++ b/test/webfuse/tests/adapter/test_server.cc @@ -8,13 +8,27 @@ #include "webfuse/adapter/server.h" #include "webfuse/adapter/server_config.h" +namespace +{ +struct wf_mountpoint * +create_mountpoint( + char const * filesystem, + void * user_data) +{ + (void) filesystem; + (void) user_data; + + return nullptr; +} + +} TEST(server, create_dispose) { mkdir("test", 0700); struct wf_server_config * config = wf_server_config_create(); - wf_server_config_set_mountpoint(config, "test"); + wf_server_config_set_mountpoint_factory(config, &create_mountpoint, nullptr); struct wf_server * server = wf_server_create(config); ASSERT_NE(nullptr, server); diff --git a/test/webfuse/tests/adapter/test_server_config.cc b/test/webfuse/tests/adapter/test_server_config.cc new file mode 100644 index 0000000..b9687be --- /dev/null +++ b/test/webfuse/tests/adapter/test_server_config.cc @@ -0,0 +1,155 @@ +#include +#include "webfuse/adapter/server_config.h" +#include "webfuse/adapter/impl/server_config.h" +#include "webfuse/adapter/impl/authenticator.h" +#include "webfuse/utils/tempdir.hpp" + +using webfuse_test::TempDir; + +namespace +{ + +wf_mountpoint * create_mountpoint( + char const * filesystem, + void * user_data) +{ + (void) filesystem; + (void) user_data; + + return nullptr; +} + +bool authenticate( + wf_credentials * credentials, + void * user_data) +{ + (void) credentials; + (void) user_data; + + return false; +} + +} + + +TEST(server_config, create_dispose) +{ + wf_server_config * config = wf_server_config_create(); + ASSERT_NE(nullptr, config); + + wf_server_config_dispose(config); +} + +TEST(server_config, set_documentroot) +{ + wf_server_config * config = wf_server_config_create(); + ASSERT_NE(nullptr, config); + + ASSERT_EQ(nullptr, config->document_root); + + wf_server_config_set_documentroot(config, "www"); + ASSERT_STREQ("www", config->document_root); + + wf_server_config_set_documentroot(config, "/var/www"); + ASSERT_STREQ("/var/www", config->document_root); + + wf_server_config_dispose(config); +} + +TEST(server_config, set_keypath) +{ + wf_server_config * config = wf_server_config_create(); + ASSERT_NE(nullptr, config); + + ASSERT_EQ(nullptr, config->key_path); + + wf_server_config_set_keypath(config, "key.pem"); + ASSERT_STREQ("key.pem", config->key_path); + + wf_server_config_set_keypath(config, "pki/self/key.pem"); + ASSERT_STREQ("pki/self/key.pem", config->key_path); + + wf_server_config_dispose(config); +} + +TEST(server_config, set_certpath) +{ + wf_server_config * config = wf_server_config_create(); + ASSERT_NE(nullptr, config); + + ASSERT_EQ(nullptr, config->key_path); + + wf_server_config_set_certpath(config, "cert.pem"); + ASSERT_STREQ("cert.pem", config->cert_path); + + wf_server_config_set_certpath(config, "pki/self/cert.pem"); + ASSERT_STREQ("pki/self/cert.pem", config->cert_path); + + wf_server_config_dispose(config); +} + +TEST(server_config, set_vhostname) +{ + wf_server_config * config = wf_server_config_create(); + ASSERT_NE(nullptr, config); + + ASSERT_EQ(nullptr, config->key_path); + + wf_server_config_set_vhostname(config, "webfuse"); + ASSERT_STREQ("webfuse", config->vhost_name); + + wf_server_config_set_vhostname(config, "localhost"); + ASSERT_STREQ("localhost", config->vhost_name); + + wf_server_config_dispose(config); +} + +TEST(server_config, set_port) +{ + wf_server_config * config = wf_server_config_create(); + ASSERT_NE(nullptr, config); + + ASSERT_EQ(0, config->port); + + wf_server_config_set_port(config, 8443); + ASSERT_EQ(8443, config->port); + + wf_server_config_set_port(config, 8080); + ASSERT_EQ(8080, config->port); + + wf_server_config_dispose(config); +} + +TEST(server_config, set_mounpoint_factory) +{ + wf_server_config * config = wf_server_config_create(); + ASSERT_NE(nullptr, config); + ASSERT_EQ(nullptr, config->mountpoint_factory.create_mountpoint); + ASSERT_EQ(nullptr, config->mountpoint_factory.user_data); + + int value = 42; + void * user_data = reinterpret_cast(&value); + wf_server_config_set_mountpoint_factory(config, &create_mountpoint, user_data); + ASSERT_EQ(&create_mountpoint, config->mountpoint_factory.create_mountpoint); + ASSERT_EQ(user_data, config->mountpoint_factory.user_data); + + wf_server_config_dispose(config); +} + +TEST(server_config, add_authenticator) +{ + wf_server_config * config = wf_server_config_create(); + ASSERT_NE(nullptr, config); + ASSERT_EQ(nullptr, config->authenticators.first); + + int value = 42; + void * user_data = reinterpret_cast(&value); + wf_server_config_add_authenticator(config, "username", &authenticate, user_data); + + wf_impl_authenticator * authenticator = config->authenticators.first; + ASSERT_STREQ("username", authenticator->type); + ASSERT_EQ(&authenticate, authenticator->authenticate); + ASSERT_EQ(user_data, authenticator->user_data); + + wf_server_config_dispose(config); +} \ No newline at end of file diff --git a/test/webfuse/tests/core/jsonrpc/mock_timer.cc b/test/webfuse/tests/core/jsonrpc/mock_timer.cc new file mode 100644 index 0000000..3cf8e74 --- /dev/null +++ b/test/webfuse/tests/core/jsonrpc/mock_timer.cc @@ -0,0 +1,31 @@ +#include "webfuse/tests/core/jsonrpc/mock_timer.hpp" +#include "webfuse/utils/wrap.hpp" + +extern "C" +{ +static wf_jsonrpc_test::ITimer * wf_jsonrpc_MockTimer = nullptr; + +WF_WRAP_FUNC0(wf_jsonrpc_MockTimer, wf_timer_manager *, wf_timer_manager_create); +WF_WRAP_FUNC1(wf_jsonrpc_MockTimer, void, wf_timer_manager_dispose, wf_timer_manager *); +WF_WRAP_FUNC1(wf_jsonrpc_MockTimer, void, wf_timer_manager_check, wf_timer_manager *); + +WF_WRAP_FUNC3(wf_jsonrpc_MockTimer, wf_timer *, wf_timer_create, wf_timer_manager *, wf_timer_on_timer_fn *, void *); +WF_WRAP_FUNC1(wf_jsonrpc_MockTimer, void, wf_timer_dispose, wf_timer *); +WF_WRAP_FUNC2(wf_jsonrpc_MockTimer, void, wf_timer_start, wf_timer *, int); +WF_WRAP_FUNC1(wf_jsonrpc_MockTimer, void, wf_timer_cancel, wf_timer *); + +} + +namespace wf_jsonrpc_test +{ +MockTimer::MockTimer() +{ + wf_jsonrpc_MockTimer = this; +} + +MockTimer::~MockTimer() +{ + wf_jsonrpc_MockTimer = nullptr; +} + +} \ No newline at end of file diff --git a/test/webfuse/tests/core/jsonrpc/mock_timer.hpp b/test/webfuse/tests/core/jsonrpc/mock_timer.hpp new file mode 100644 index 0000000..5c95852 --- /dev/null +++ b/test/webfuse/tests/core/jsonrpc/mock_timer.hpp @@ -0,0 +1,48 @@ +#ifndef WF_JSONRPC_MOCK_TIMERMANAGER_HPP +#define WF_JSONRPC_MOCK_TIMERMANAGER_HPP + +#include "webfuse/core/timer/timer.h" +#include "webfuse/core/timer/manager.h" +#include + +namespace wf_jsonrpc_test +{ + +class ITimer +{ +public: + virtual ~ITimer() = default; + virtual wf_timer_manager * wf_timer_manager_create() = 0; + virtual void wf_timer_manager_dispose(wf_timer_manager * manager) = 0; + virtual void wf_timer_manager_check(wf_timer_manager * manager) = 0; + virtual wf_timer * wf_timer_create( + wf_timer_manager * manager, + wf_timer_on_timer_fn * on_timer, + void * user_data) = 0; + virtual void wf_timer_dispose(wf_timer * timer) = 0; + virtual void wf_timer_start(wf_timer * timer, int timeout_ms) = 0; + virtual void wf_timer_cancel(wf_timer * timer) = 0; +}; + +class MockTimer: public ITimer +{ +public: + MockTimer(); + ~MockTimer() override; + MOCK_METHOD0(wf_timer_manager_create, wf_timer_manager * ()); + MOCK_METHOD1(wf_timer_manager_dispose, void(wf_timer_manager * manager)); + MOCK_METHOD1(wf_timer_manager_check, void (wf_timer_manager * manager)); + MOCK_METHOD3(wf_timer_create, wf_timer *( + wf_timer_manager * manager, + wf_timer_on_timer_fn * on_timer, + void * user_data)); + MOCK_METHOD1(wf_timer_dispose, void (wf_timer * timer)); + MOCK_METHOD2(wf_timer_start, void (wf_timer * timer, int timeout_ms)); + MOCK_METHOD1(wf_timer_cancel, void (wf_timer * timer)); + +}; + + +} + +#endif diff --git a/test/webfuse/tests/core/jsonrpc/mock_timer_callback.cc b/test/webfuse/tests/core/jsonrpc/mock_timer_callback.cc new file mode 100644 index 0000000..7e62c31 --- /dev/null +++ b/test/webfuse/tests/core/jsonrpc/mock_timer_callback.cc @@ -0,0 +1,41 @@ +#include "webfuse/tests/core/jsonrpc/mock_timer_callback.hpp" + +extern "C" +{ +using wf_jsonrpc_test::MockTimerCallback; + +static void wf_jsonrpc_test_MockTimerCallback_on_timer( + wf_timer * timer, + void * user_data) +{ + auto * self = reinterpret_cast(user_data); + self->on_timer(timer, user_data); +} + +} + +namespace wf_jsonrpc_test +{ + +MockTimerCallback::MockTimerCallback() +{ + +} + +MockTimerCallback::~MockTimerCallback() +{ + +} + +wf_timer_on_timer_fn * MockTimerCallback::on_timer_fn() +{ + return &wf_jsonrpc_test_MockTimerCallback_on_timer; +} + +void * MockTimerCallback::user_data() +{ + return reinterpret_cast(this); +} + + +} \ No newline at end of file diff --git a/test/webfuse/tests/core/jsonrpc/mock_timer_callback.hpp b/test/webfuse/tests/core/jsonrpc/mock_timer_callback.hpp new file mode 100644 index 0000000..e55b7e1 --- /dev/null +++ b/test/webfuse/tests/core/jsonrpc/mock_timer_callback.hpp @@ -0,0 +1,23 @@ +#ifndef WF_JSONRPC_MOCK_TIMERCALLBACK_HPP +#define WF_JSONRPC_MOCK_TIMERCALLBACK_HPP + +#include "webfuse/core/timer/on_timer_fn.h" +#include + +namespace wf_jsonrpc_test +{ +class MockTimerCallback +{ +public: + MockTimerCallback(); + virtual ~MockTimerCallback(); + wf_timer_on_timer_fn * on_timer_fn(); + void * user_data(); + + MOCK_METHOD2(on_timer, void (wf_timer * timer, void * user_data)); + +}; + +} + +#endif diff --git a/test/adapter/jsonrpc/test_is_request.cc b/test/webfuse/tests/core/jsonrpc/test_is_request.cc similarity index 65% rename from test/adapter/jsonrpc/test_is_request.cc rename to test/webfuse/tests/core/jsonrpc/test_is_request.cc index 5fef81e..4599611 100644 --- a/test/adapter/jsonrpc/test_is_request.cc +++ b/test/webfuse/tests/core/jsonrpc/test_is_request.cc @@ -1,112 +1,112 @@ #include -#include "webfuse/adapter/impl/jsonrpc/request.h" +#include "webfuse/core/jsonrpc/request.h" -TEST(jsonrpc_is_request, request_with_object_params) +TEST(wf_jsonrpc_is_request, request_with_object_params) { json_t * request = json_object(); json_object_set_new(request, "method", json_string("method")); json_object_set_new(request, "params", json_object()); json_object_set_new(request, "id", json_integer(42)); - ASSERT_TRUE(wf_impl_jsonrpc_is_request(request)); + ASSERT_TRUE(wf_jsonrpc_is_request(request)); json_decref(request); } -TEST(jsonrpc_is_request, request_with_array_params) +TEST(wf_jsonrpc_is_request, request_with_array_params) { json_t * request = json_object(); json_object_set_new(request, "method", json_string("method")); json_object_set_new(request, "params", json_array()); json_object_set_new(request, "id", json_integer(42)); - ASSERT_TRUE(wf_impl_jsonrpc_is_request(request)); + ASSERT_TRUE(wf_jsonrpc_is_request(request)); json_decref(request); } -TEST(jsonrpc_is_request, null_request) +TEST(wf_jsonrpc_is_request, null_request) { - ASSERT_FALSE(wf_impl_jsonrpc_is_request(nullptr)); + ASSERT_FALSE(wf_jsonrpc_is_request(nullptr)); } -TEST(jsonrpc_is_request, invalid_request) +TEST(wf_jsonrpc_is_request, invalid_request) { json_t * request = json_array(); json_array_append_new(request, json_string("method")); json_array_append_new(request, json_object()); json_array_append_new(request, json_integer(42)); - ASSERT_FALSE(wf_impl_jsonrpc_is_request(request)); + ASSERT_FALSE(wf_jsonrpc_is_request(request)); json_decref(request); } -TEST(jsonrpc_is_request, invalid_request_without_id) +TEST(wf_jsonrpc_is_request, invalid_request_without_id) { json_t * request = json_object(); json_object_set_new(request, "method", json_string("method")); json_object_set_new(request, "params", json_object()); - ASSERT_FALSE(wf_impl_jsonrpc_is_request(request)); + ASSERT_FALSE(wf_jsonrpc_is_request(request)); json_decref(request); } -TEST(jsonrpc_is_request, invalid_request_due_to_invalid_id) +TEST(wf_jsonrpc_is_request, invalid_request_due_to_invalid_id) { json_t * request = json_object(); json_object_set_new(request, "method", json_string("method")); json_object_set_new(request, "params", json_object()); json_object_set_new(request, "id", json_string("42")); - ASSERT_FALSE(wf_impl_jsonrpc_is_request(request)); + ASSERT_FALSE(wf_jsonrpc_is_request(request)); json_decref(request); } -TEST(jsonrpc_is_request, invalid_request_without_method) +TEST(wf_jsonrpc_is_request, invalid_request_without_method) { json_t * request = json_object(); json_object_set_new(request, "params", json_object()); json_object_set_new(request, "id", json_integer(42)); - ASSERT_FALSE(wf_impl_jsonrpc_is_request(request)); + ASSERT_FALSE(wf_jsonrpc_is_request(request)); json_decref(request); } -TEST(jsonrpc_is_request, invalid_request_due_to_invalid_method) +TEST(wf_jsonrpc_is_request, invalid_request_due_to_invalid_method) { json_t * request = json_object(); json_object_set_new(request, "method", json_integer(42)); json_object_set_new(request, "params", json_object()); json_object_set_new(request, "id", json_integer(42)); - ASSERT_FALSE(wf_impl_jsonrpc_is_request(request)); + ASSERT_FALSE(wf_jsonrpc_is_request(request)); json_decref(request); } -TEST(jsonrpc_is_request, invalid_request_without_params) +TEST(wf_jsonrpc_is_request, invalid_request_without_params) { json_t * request = json_object(); json_object_set_new(request, "method", json_string("method")); json_object_set_new(request, "id", json_integer(42)); - ASSERT_FALSE(wf_impl_jsonrpc_is_request(request)); + ASSERT_FALSE(wf_jsonrpc_is_request(request)); json_decref(request); } -TEST(jsonrpc_is_request, invalid_request_due_to_invalid_params) +TEST(wf_jsonrpc_is_request, invalid_request_due_to_invalid_params) { json_t * request = json_object(); json_object_set_new(request, "methdo", json_string("method")); json_object_set_new(request, "params", json_string("params")); json_object_set_new(request, "id", json_integer(42)); - ASSERT_FALSE(wf_impl_jsonrpc_is_request(request)); + ASSERT_FALSE(wf_jsonrpc_is_request(request)); json_decref(request); } diff --git a/test/adapter/jsonrpc/test_is_response.cc b/test/webfuse/tests/core/jsonrpc/test_is_response.cc similarity index 59% rename from test/adapter/jsonrpc/test_is_response.cc rename to test/webfuse/tests/core/jsonrpc/test_is_response.cc index 6182cd0..908ddf0 100644 --- a/test/adapter/jsonrpc/test_is_response.cc +++ b/test/webfuse/tests/core/jsonrpc/test_is_response.cc @@ -1,94 +1,94 @@ #include -#include "webfuse/adapter/impl/jsonrpc/response.h" +#include "webfuse/core/jsonrpc/response.h" -TEST(jsonrpc_is_response, valid_result) +TEST(wf_jsonrpc_is_response, valid_result) { json_t * message = json_object(); json_object_set_new(message, "result", json_object()); json_object_set_new(message, "id", json_integer(42)); - ASSERT_TRUE(wf_impl_jsonrpc_is_response(message)); + ASSERT_TRUE(wf_jsonrpc_is_response(message)); json_decref(message); } -TEST(jsonrpc_is_response, valid_result_string) +TEST(wf_jsonrpc_is_response, valid_result_string) { json_t * message = json_object(); json_object_set_new(message, "result", json_string("also valid")); json_object_set_new(message, "id", json_integer(42)); - ASSERT_TRUE(wf_impl_jsonrpc_is_response(message)); + ASSERT_TRUE(wf_jsonrpc_is_response(message)); json_decref(message); } -TEST(jsonrpc_is_response, valid_error) +TEST(wf_jsonrpc_is_response, valid_error) { json_t * message = json_object(); json_object_set_new(message, "error", json_object()); json_object_set_new(message, "id", json_integer(42)); - ASSERT_TRUE(wf_impl_jsonrpc_is_response(message)); + ASSERT_TRUE(wf_jsonrpc_is_response(message)); json_decref(message); } -TEST(jsonrpc_is_response, invalid_null) +TEST(wf_jsonrpc_is_response, invalid_null) { - ASSERT_FALSE(wf_impl_jsonrpc_is_response(nullptr)); + ASSERT_FALSE(wf_jsonrpc_is_response(nullptr)); } -TEST(jsonrpc_is_response, invalid_message) +TEST(wf_jsonrpc_is_response, invalid_message) { json_t * message = json_array(); json_array_append_new(message, json_object()); json_array_append_new(message, json_integer(42)); - ASSERT_FALSE(wf_impl_jsonrpc_is_response(message)); + ASSERT_FALSE(wf_jsonrpc_is_response(message)); json_decref(message); } -TEST(jsonrpc_is_response, invalid_missing_id) +TEST(wf_jsonrpc_is_response, invalid_missing_id) { json_t * message = json_object(); json_object_set_new(message, "result", json_object()); - ASSERT_FALSE(wf_impl_jsonrpc_is_response(message)); + ASSERT_FALSE(wf_jsonrpc_is_response(message)); json_decref(message); } -TEST(jsonrpc_is_response, invalid_id_wrong_type) +TEST(wf_jsonrpc_is_response, invalid_id_wrong_type) { json_t * message = json_object(); json_object_set_new(message, "result", json_object()); json_object_set_new(message, "id", json_string("42")); - ASSERT_FALSE(wf_impl_jsonrpc_is_response(message)); + ASSERT_FALSE(wf_jsonrpc_is_response(message)); json_decref(message); } -TEST(jsonrpc_is_response, invalid_missing_result_and_error) +TEST(wf_jsonrpc_is_response, invalid_missing_result_and_error) { json_t * message = json_object(); json_object_set_new(message, "id", json_integer(42)); - ASSERT_FALSE(wf_impl_jsonrpc_is_response(message)); + ASSERT_FALSE(wf_jsonrpc_is_response(message)); json_decref(message); } -TEST(jsonrpc_is_response, invalid_error_wrong_type) +TEST(wf_jsonrpc_is_response, invalid_error_wrong_type) { json_t * message = json_object(); json_object_set_new(message, "error", json_array()); json_object_set_new(message, "id", json_integer(42)); - ASSERT_FALSE(wf_impl_jsonrpc_is_response(message)); + ASSERT_FALSE(wf_jsonrpc_is_response(message)); json_decref(message); } diff --git a/test/webfuse/tests/core/jsonrpc/test_proxy.cc b/test/webfuse/tests/core/jsonrpc/test_proxy.cc new file mode 100644 index 0000000..e594e89 --- /dev/null +++ b/test/webfuse/tests/core/jsonrpc/test_proxy.cc @@ -0,0 +1,430 @@ +#include +#include "webfuse/core/jsonrpc/proxy.h" +#include "webfuse/core/status.h" +#include "webfuse/core/timer/manager.h" + +#include "webfuse/tests/core/jsonrpc/mock_timer.hpp" + +#include +#include + +using namespace std::chrono_literals; +using wf_jsonrpc_test::MockTimer; +using testing::Return; +using testing::_; +using testing::DoAll; +using testing::SaveArg; + +#define WF_DEFAULT_TIMEOUT (10 * 1000) + +namespace +{ + int jsonrpc_get_status(json_t * error) + { + json_t * code = json_object_get(error, "code"); + return (json_is_integer(code)) ? json_integer_value(code) : WF_BAD_FORMAT; + } + + struct SendContext + { + json_t * response; + bool result; + bool is_called; + + explicit SendContext(bool result_ = true) + : response(nullptr) + , result(result_) + , is_called(false) + { + } + + ~SendContext() + { + if (nullptr != response) + { + json_decref(response); + } + } + }; + + bool jsonrpc_send( + json_t * request, + void * user_data) + { + SendContext * context = reinterpret_cast(user_data); + context->is_called = true; + context->response = request; + json_incref(request); + + return context->result; + } + + struct FinishedContext + { + bool is_called; + json_t * result; + json_t * error; + + FinishedContext() + : is_called(false) + , result(nullptr) + , error(nullptr) + { + + } + + ~FinishedContext() + { + if (nullptr != result) + { + json_decref(result); + } + + if (nullptr != error) + { + json_decref(error); + } + } + }; + + void jsonrpc_finished( + void * user_data, + json_t const * result, + json_t const * error) + { + FinishedContext * context = reinterpret_cast(user_data); + context->is_called = true; + context->result = json_deep_copy(result); + context->error = json_deep_copy(error); + } +} + +TEST(wf_jsonrpc_proxy, init) +{ + struct wf_timer_manager * timer_manager = wf_timer_manager_create(); + + SendContext context; + void * user_data = reinterpret_cast(&context); + struct wf_jsonrpc_proxy * proxy = wf_jsonrpc_proxy_create(timer_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, user_data); + + wf_jsonrpc_proxy_dispose(proxy); + wf_timer_manager_dispose(timer_manager); + + ASSERT_FALSE(context.is_called); +} + +TEST(wf_jsonrpc_proxy, invoke) +{ + struct wf_timer_manager * timer_manager = wf_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wf_jsonrpc_proxy * proxy = wf_jsonrpc_proxy_create(timer_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + FinishedContext finished_context; + void * finished_data = reinterpret_cast(&finished_context); + wf_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); + + ASSERT_TRUE(send_context.is_called); + ASSERT_TRUE(json_is_object(send_context.response)); + + json_t * method = json_object_get(send_context.response, "method"); + ASSERT_TRUE(json_is_string(method)); + ASSERT_STREQ("foo", json_string_value(method)); + + json_t * params = json_object_get(send_context.response, "params"); + ASSERT_TRUE(json_is_array(params)); + ASSERT_EQ(2, json_array_size(params)); + ASSERT_TRUE(json_is_string(json_array_get(params, 0))); + ASSERT_STREQ("bar", json_string_value(json_array_get(params, 0))); + ASSERT_TRUE(json_is_integer(json_array_get(params, 1))); + ASSERT_EQ(42, json_integer_value(json_array_get(params, 1))); + + json_t * id = json_object_get(send_context.response, "id"); + ASSERT_TRUE(json_is_integer(id)); + + ASSERT_FALSE(finished_context.is_called); + + wf_jsonrpc_proxy_dispose(proxy); + wf_timer_manager_dispose(timer_manager); + + ASSERT_TRUE(finished_context.is_called); + ASSERT_FALSE(nullptr == finished_context.error); +} + +TEST(wf_jsonrpc_proxy, invoke_calls_finish_if_send_fails) +{ + struct wf_timer_manager * timer_manager = wf_timer_manager_create(); + + SendContext send_context(false); + void * send_data = reinterpret_cast(&send_context); + struct wf_jsonrpc_proxy * proxy = wf_jsonrpc_proxy_create(timer_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + FinishedContext finished_context; + void * finished_data = reinterpret_cast(&finished_context); + wf_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); + + ASSERT_TRUE(send_context.is_called); + ASSERT_TRUE(json_is_object(send_context.response)); + + ASSERT_TRUE(finished_context.is_called); + ASSERT_FALSE(nullptr == finished_context.error); + + wf_jsonrpc_proxy_dispose(proxy); + wf_timer_manager_dispose(timer_manager); +} + +TEST(wf_jsonrpc_proxy, invoke_fails_if_another_request_is_pending) +{ + struct wf_timer_manager * timer_manager = wf_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wf_jsonrpc_proxy * proxy = wf_jsonrpc_proxy_create(timer_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + FinishedContext finished_context; + void * finished_data = reinterpret_cast(&finished_context); + wf_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); + + FinishedContext finished_context2; + void * finished_data2 = reinterpret_cast(&finished_context2); + wf_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data2, "foo", ""); + + ASSERT_TRUE(send_context.is_called); + ASSERT_TRUE(json_is_object(send_context.response)); + + ASSERT_FALSE(finished_context.is_called); + + ASSERT_TRUE(finished_context2.is_called); + ASSERT_EQ(WF_BAD_BUSY, jsonrpc_get_status(finished_context2.error)); + + wf_jsonrpc_proxy_dispose(proxy); + wf_timer_manager_dispose(timer_manager); +} + +TEST(wf_jsonrpc_proxy, invoke_fails_if_request_is_invalid) +{ + struct wf_timer_manager * timer_manager = wf_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wf_jsonrpc_proxy * proxy = wf_jsonrpc_proxy_create(timer_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + FinishedContext finished_context; + void * finished_data = reinterpret_cast(&finished_context); + wf_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data, "foo", "?", "error"); + + ASSERT_FALSE(send_context.is_called); + + ASSERT_TRUE(finished_context.is_called); + ASSERT_EQ(WF_BAD, jsonrpc_get_status(finished_context.error)); + + wf_jsonrpc_proxy_dispose(proxy); + wf_timer_manager_dispose(timer_manager); +} + +TEST(wf_jsonrpc_proxy, on_result) +{ + struct wf_timer_manager * timer_manager = wf_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wf_jsonrpc_proxy * proxy = wf_jsonrpc_proxy_create(timer_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + FinishedContext finished_context; + void * finished_data = reinterpret_cast(&finished_context); + wf_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); + + ASSERT_TRUE(send_context.is_called); + ASSERT_TRUE(json_is_object(send_context.response)); + + json_t * id = json_object_get(send_context.response, "id"); + ASSERT_TRUE(json_is_number(id)); + + json_t * response = json_object(); + json_object_set_new(response, "result", json_string("okay")); + json_object_set(response, "id", id); + + wf_jsonrpc_proxy_onresult(proxy, response); + json_decref(response); + + ASSERT_TRUE(finished_context.is_called); + ASSERT_EQ(nullptr, finished_context.error); + ASSERT_TRUE(json_is_string(finished_context.result)); + ASSERT_STREQ("okay", json_string_value(finished_context.result)); + + wf_jsonrpc_proxy_dispose(proxy); + wf_timer_manager_dispose(timer_manager); +} + +TEST(wf_jsonrpc_proxy, on_result_reject_response_with_unknown_id) +{ + struct wf_timer_manager * timer_manager = wf_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wf_jsonrpc_proxy * proxy = wf_jsonrpc_proxy_create(timer_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + FinishedContext finished_context; + void * finished_data = reinterpret_cast(&finished_context); + wf_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); + + ASSERT_TRUE(send_context.is_called); + ASSERT_TRUE(json_is_object(send_context.response)); + + json_t * id = json_object_get(send_context.response, "id"); + ASSERT_TRUE(json_is_number(id)); + + json_t * response = json_object(); + json_object_set_new(response, "result", json_string("okay")); + json_object_set_new(response, "id", json_integer(1 + json_integer_value(id))); + + wf_jsonrpc_proxy_onresult(proxy, response); + json_decref(response); + + ASSERT_FALSE(finished_context.is_called); + + wf_jsonrpc_proxy_dispose(proxy); + wf_timer_manager_dispose(timer_manager); +} + +TEST(wf_jsonrpc_proxy, timeout) +{ + struct wf_timer_manager * timer_manager = wf_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wf_jsonrpc_proxy * proxy = wf_jsonrpc_proxy_create(timer_manager, 0, &jsonrpc_send, send_data); + + FinishedContext finished_context; + void * finished_data = reinterpret_cast(&finished_context); + wf_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); + + ASSERT_TRUE(send_context.is_called); + ASSERT_TRUE(json_is_object(send_context.response)); + + std::this_thread::sleep_for(10ms); + wf_timer_manager_check(timer_manager); + + ASSERT_TRUE(finished_context.is_called); + ASSERT_EQ(WF_BAD_TIMEOUT, jsonrpc_get_status(finished_context.error)); + + wf_jsonrpc_proxy_dispose(proxy); + wf_timer_manager_dispose(timer_manager); +} + +TEST(wf_jsonrpc_proxy, cleanup_pending_request) +{ + struct wf_timer_manager * timer_manager = wf_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wf_jsonrpc_proxy * proxy = wf_jsonrpc_proxy_create(timer_manager, 10, &jsonrpc_send, send_data); + + FinishedContext finished_context; + void * finished_data = reinterpret_cast(&finished_context); + wf_jsonrpc_proxy_invoke(proxy, &jsonrpc_finished, finished_data, "foo", "si", "bar", 42); + + ASSERT_TRUE(send_context.is_called); + ASSERT_TRUE(json_is_object(send_context.response)); + + ASSERT_FALSE(finished_context.is_called); + + wf_jsonrpc_proxy_dispose(proxy); + + ASSERT_TRUE(finished_context.is_called); + + wf_timer_manager_dispose(timer_manager); +} + + + +TEST(wf_jsonrpc_proxy, notify) +{ + struct wf_timer_manager * timer_manager = wf_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wf_jsonrpc_proxy * proxy = wf_jsonrpc_proxy_create(timer_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + wf_jsonrpc_proxy_notify(proxy, "foo", "si", "bar", 42); + + ASSERT_TRUE(send_context.is_called); + ASSERT_TRUE(json_is_object(send_context.response)); + + json_t * method = json_object_get(send_context.response, "method"); + ASSERT_TRUE(json_is_string(method)); + ASSERT_STREQ("foo", json_string_value(method)); + + json_t * params = json_object_get(send_context.response, "params"); + ASSERT_TRUE(json_is_array(params)); + ASSERT_EQ(2, json_array_size(params)); + ASSERT_TRUE(json_is_string(json_array_get(params, 0))); + ASSERT_STREQ("bar", json_string_value(json_array_get(params, 0))); + ASSERT_TRUE(json_is_integer(json_array_get(params, 1))); + ASSERT_EQ(42, json_integer_value(json_array_get(params, 1))); + + json_t * id = json_object_get(send_context.response, "id"); + ASSERT_EQ(nullptr, id); + + wf_jsonrpc_proxy_dispose(proxy); + wf_timer_manager_dispose(timer_manager); +} + +TEST(wf_jsonrpc_proxy, notify_dont_send_invalid_request) +{ + struct wf_timer_manager * timer_manager = wf_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wf_jsonrpc_proxy * proxy = wf_jsonrpc_proxy_create(timer_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + wf_jsonrpc_proxy_notify(proxy, "foo", "?"); + + ASSERT_FALSE(send_context.is_called); + + wf_jsonrpc_proxy_dispose(proxy); + wf_timer_manager_dispose(timer_manager); +} + +TEST(wf_jsonrpc_proxy, swallow_timeout_if_no_request_pending) +{ + MockTimer timer_api; + + wf_timer_on_timer_fn * on_timer = nullptr; + void * timer_context = nullptr; + EXPECT_CALL(timer_api, wf_timer_create(_, _, _)) + .Times(1) + .WillOnce(DoAll(SaveArg<1>(&on_timer), SaveArg<2>(&timer_context), Return(nullptr))); + EXPECT_CALL(timer_api, wf_timer_dispose(_)).Times(1); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wf_jsonrpc_proxy * proxy = wf_jsonrpc_proxy_create(nullptr, 1, &jsonrpc_send, send_data); + + on_timer(nullptr, timer_context); + ASSERT_FALSE(send_context.is_called); + + + wf_jsonrpc_proxy_dispose(proxy); +} + +TEST(wf_jsonrpc_proxy, on_result_swallow_if_no_request_pending) +{ + struct wf_timer_manager * timer_manager = wf_timer_manager_create(); + + SendContext send_context; + void * send_data = reinterpret_cast(&send_context); + struct wf_jsonrpc_proxy * proxy = wf_jsonrpc_proxy_create(timer_manager, WF_DEFAULT_TIMEOUT, &jsonrpc_send, send_data); + + json_t * response = json_object(); + json_object_set_new(response, "result", json_string("okay")); + json_object_set_new(response, "id", json_integer(42)); + + wf_jsonrpc_proxy_onresult(proxy, response); + json_decref(response); + + wf_jsonrpc_proxy_dispose(proxy); + wf_timer_manager_dispose(timer_manager); +} + diff --git a/test/adapter/jsonrpc/test_request.cc b/test/webfuse/tests/core/jsonrpc/test_request.cc similarity index 51% rename from test/adapter/jsonrpc/test_request.cc rename to test/webfuse/tests/core/jsonrpc/test_request.cc index 07c3da8..8d37a9f 100644 --- a/test/adapter/jsonrpc/test_request.cc +++ b/test/webfuse/tests/core/jsonrpc/test_request.cc @@ -1,5 +1,6 @@ #include -#include "webfuse/adapter/impl/jsonrpc/request.h" +#include "webfuse/core/jsonrpc/request.h" +#include "webfuse/core/status.h" namespace { @@ -22,29 +23,29 @@ bool jsonrpc_send( } -TEST(jsonrpc_request, create_dispose) +TEST(wf_jsonrpc_request, create_dispose) { Context context{nullptr}; void * user_data = reinterpret_cast(&context); - struct wf_impl_jsonrpc_request * request = - wf_impl_jsonrpc_request_create(42, &jsonrpc_send, user_data); + struct wf_jsonrpc_request * request = + wf_jsonrpc_request_create(42, &jsonrpc_send, user_data); ASSERT_NE(nullptr, request); - ASSERT_EQ(user_data, wf_impl_jsonrpc_request_get_userdata(request)); + ASSERT_EQ(user_data, wf_jsonrpc_request_get_userdata(request)); - wf_impl_jsonrpc_request_dispose(request); + wf_jsonrpc_request_dispose(request); } -TEST(jsonrpc_request, respond) +TEST(wf_jsonrpc_request, respond) { Context context{nullptr}; void * user_data = reinterpret_cast(&context); - struct wf_impl_jsonrpc_request * request = - wf_impl_jsonrpc_request_create(42, &jsonrpc_send, user_data); + struct wf_jsonrpc_request * request = + wf_jsonrpc_request_create(42, &jsonrpc_send, user_data); - wf_impl_jsonrpc_respond(request, json_string("okay")); + wf_jsonrpc_respond(request, json_string("okay")); ASSERT_NE(nullptr, context.response); @@ -65,15 +66,15 @@ TEST(jsonrpc_request, respond) json_decref(response); } -TEST(jsonrpc_request, respond_error) +TEST(wf_jsonrpc_request, respond_error) { Context context{nullptr}; void * user_data = reinterpret_cast(&context); - struct wf_impl_jsonrpc_request * request = - wf_impl_jsonrpc_request_create(42, &jsonrpc_send, user_data); + struct wf_jsonrpc_request * request = + wf_jsonrpc_request_create(42, &jsonrpc_send, user_data); - wf_impl_jsonrpc_respond_error(request, WF_BAD); + wf_jsonrpc_respond_error(request, WF_BAD, "Bad"); ASSERT_NE(nullptr, context.response); @@ -100,3 +101,38 @@ TEST(jsonrpc_request, respond_error) json_decref(response); } + +TEST(wf_jsonrpc_request, is_request_object_params) +{ + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("some_method")); + json_object_set_new(request, "params", json_object()); + json_object_set_new(request, "id", json_integer(42)); + + ASSERT_TRUE(wf_jsonrpc_is_request(request)); + + json_decref(request); +} + +TEST(wf_jsonrpc_request, is_request_fail_missing_params) +{ + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("some_method")); + json_object_set_new(request, "id", json_integer(42)); + + ASSERT_FALSE(wf_jsonrpc_is_request(request)); + + json_decref(request); +} + +TEST(wf_jsonrpc_request, is_request_fail_params_wrong_type) +{ + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("some_method")); + json_object_set_new(request, "params", json_string("invalid_params")); + json_object_set_new(request, "id", json_integer(42)); + + ASSERT_FALSE(wf_jsonrpc_is_request(request)); + + json_decref(request); +} diff --git a/test/webfuse/tests/core/jsonrpc/test_response.cc b/test/webfuse/tests/core/jsonrpc/test_response.cc new file mode 100644 index 0000000..f6ef9e7 --- /dev/null +++ b/test/webfuse/tests/core/jsonrpc/test_response.cc @@ -0,0 +1,147 @@ +#include +#include "webfuse/core/jsonrpc/response_intern.h" +#include "webfuse/core/status.h" + +TEST(wf_json_response, init_result) +{ + json_t * message = json_object(); + json_object_set_new(message, "result", json_integer(47)); + json_object_set_new(message, "id", json_integer(11)); + + struct wf_jsonrpc_response response; + wf_jsonrpc_response_init(&response, message); + + ASSERT_EQ(nullptr, response.error); + ASSERT_TRUE(json_is_integer(response.result)); + ASSERT_EQ(47, json_integer_value(response.result)); + ASSERT_EQ(11, response.id); + + wf_jsonrpc_response_cleanup(&response); + json_decref(message); +} + +TEST(wf_json_response, init_error) +{ + json_t * message = json_object(); + json_t * err = json_object(); + json_object_set_new(err, "code", json_integer(42)); + json_object_set_new(err, "message", json_string("Don't Panic!")); + json_object_set_new(message, "error", err); + json_object_set_new(message, "id", json_integer(23)); + + struct wf_jsonrpc_response response; + wf_jsonrpc_response_init(&response, message); + + ASSERT_EQ(42, json_integer_value(json_object_get(response.error, "code"))); + ASSERT_STREQ("Don't Panic!", json_string_value(json_object_get(response.error, "message"))); + ASSERT_EQ(nullptr, response.result); + ASSERT_EQ(23, response.id); + + wf_jsonrpc_response_cleanup(&response); + json_decref(message); +} + +TEST(wf_json_response, init_fail_missing_result_and_error) +{ + json_t * message = json_object(); + json_object_set_new(message, "id", json_integer(12)); + + struct wf_jsonrpc_response response; + wf_jsonrpc_response_init(&response, message); + + ASSERT_EQ(WF_BAD_FORMAT, json_integer_value(json_object_get(response.error, "code"))); + ASSERT_EQ(nullptr, response.result); + ASSERT_EQ(12, response.id); + + wf_jsonrpc_response_cleanup(&response); + json_decref(message); +} + +TEST(wf_json_response, init_fail_missing_id) +{ + json_t * message = json_object(); + json_object_set_new(message, "result", json_integer(47)); + + struct wf_jsonrpc_response response; + wf_jsonrpc_response_init(&response, message); + + ASSERT_EQ(WF_BAD_FORMAT, json_integer_value(json_object_get(response.error, "code"))); + ASSERT_EQ(nullptr, response.result); + ASSERT_EQ(-1, response.id); + + wf_jsonrpc_response_cleanup(&response); + json_decref(message); +} + +TEST(wf_json_response, init_fail_wrong_id_type) +{ + json_t * message = json_object(); + json_object_set_new(message, "result", json_integer(47)); + json_object_set_new(message, "id", json_string("42")); + + struct wf_jsonrpc_response response; + wf_jsonrpc_response_init(&response, message); + + ASSERT_EQ(WF_BAD_FORMAT, json_integer_value(json_object_get(response.error, "code"))); + ASSERT_EQ(nullptr, response.result); + ASSERT_EQ(-1, response.id); + + wf_jsonrpc_response_cleanup(&response); + json_decref(message); +} + +TEST(wf_json_response, init_fail_error_missing_code) +{ + json_t * message = json_object(); + json_t * err = json_object(); + json_object_set_new(err, "message", json_string("Don't Panic!")); + json_object_set_new(message, "error", err); + json_object_set_new(message, "id", json_integer(23)); + + struct wf_jsonrpc_response response; + wf_jsonrpc_response_init(&response, message); + + ASSERT_EQ(WF_BAD_FORMAT, json_integer_value(json_object_get(response.error, "code"))); + ASSERT_EQ(nullptr, response.result); + ASSERT_EQ(23, response.id); + + wf_jsonrpc_response_cleanup(&response); + json_decref(message); +} + +TEST(wf_json_response, init_fail_error_wrong_code_type) +{ + json_t * message = json_object(); + json_t * err = json_object(); + json_object_set_new(err, "code", json_string("42")); + json_object_set_new(err, "message", json_string("Don't Panic!")); + json_object_set_new(message, "error", err); + json_object_set_new(message, "id", json_integer(23)); + + struct wf_jsonrpc_response response; + wf_jsonrpc_response_init(&response, message); + + ASSERT_EQ(WF_BAD_FORMAT, json_integer_value(json_object_get(response.error, "code"))); + ASSERT_EQ(nullptr, response.result); + ASSERT_EQ(23, response.id); + + wf_jsonrpc_response_cleanup(&response); + json_decref(message); +} + +TEST(wf_json_response, init_fail_error_wrong_type) +{ + json_t * message = json_object(); + json_object_set_new(message, "error", json_string("invalid error type")); + json_object_set_new(message, "id", json_integer(23)); + + struct wf_jsonrpc_response response; + wf_jsonrpc_response_init(&response, message); + + ASSERT_EQ(WF_BAD_FORMAT, json_integer_value(json_object_get(response.error, "code"))); + ASSERT_EQ(nullptr, response.result); + ASSERT_EQ(23, response.id); + + wf_jsonrpc_response_cleanup(&response); + json_decref(message); +} diff --git a/test/adapter/test_response_parser.cc b/test/webfuse/tests/core/jsonrpc/test_response_parser.cc similarity index 59% rename from test/adapter/test_response_parser.cc rename to test/webfuse/tests/core/jsonrpc/test_response_parser.cc index db7fe2f..f9941c1 100644 --- a/test/adapter/test_response_parser.cc +++ b/test/webfuse/tests/core/jsonrpc/test_response_parser.cc @@ -1,54 +1,58 @@ #include #include -#include "webfuse/adapter/impl/jsonrpc/response.h" +#include "webfuse/core/jsonrpc/response_intern.h" static void response_parse_str( std::string const & buffer, - struct wf_impl_jsonrpc_response * response) + struct wf_jsonrpc_response * response) { json_t * message = json_loadb(buffer.c_str(), buffer.size(), 0, nullptr); if (nullptr != message) { - wf_impl_jsonrpc_response_init(response, message); + wf_jsonrpc_response_init(response, message); json_decref(message); } } TEST(response_parser, test) { - struct wf_impl_jsonrpc_response response; + struct wf_jsonrpc_response response; // no object response_parse_str("[]", &response); - ASSERT_NE(WF_GOOD, response.status); + ASSERT_NE(nullptr, response.error); ASSERT_EQ(-1, response.id); ASSERT_EQ(nullptr, response.result); + wf_jsonrpc_response_cleanup(&response); // empty response_parse_str("{}", &response); - ASSERT_NE(WF_GOOD, response.status); + ASSERT_NE(nullptr, response.error); ASSERT_EQ(-1, response.id); ASSERT_EQ(nullptr, response.result); + wf_jsonrpc_response_cleanup(&response); // no data response_parse_str("{\"id\":42}", &response); - ASSERT_NE(WF_GOOD, response.status); + ASSERT_NE(nullptr, response.error); ASSERT_EQ(42, response.id); ASSERT_EQ(nullptr, response.result); + wf_jsonrpc_response_cleanup(&response); // custom error code response_parse_str("{\"error\":{\"code\": 42}, \"id\": 42}", &response); - ASSERT_NE(WF_GOOD, response.status); - ASSERT_EQ(42, response.status); + ASSERT_NE(nullptr, response.error); + ASSERT_EQ(42, json_integer_value(json_object_get(response.error, "code"))); ASSERT_EQ(42, response.id); ASSERT_EQ(nullptr, response.result); + wf_jsonrpc_response_cleanup(&response); // valid response response_parse_str("{\"result\": true, \"id\": 42}", &response); - ASSERT_EQ(WF_GOOD, response.status); + ASSERT_EQ(nullptr, response.error); ASSERT_EQ(42, response.id); ASSERT_NE(nullptr, response.result); - json_decref(response.result); + wf_jsonrpc_response_cleanup(&response); } diff --git a/test/webfuse/tests/core/jsonrpc/test_server.cc b/test/webfuse/tests/core/jsonrpc/test_server.cc new file mode 100644 index 0000000..688028f --- /dev/null +++ b/test/webfuse/tests/core/jsonrpc/test_server.cc @@ -0,0 +1,241 @@ +#include +#include "webfuse/core/jsonrpc/server.h" +#include "webfuse/core/jsonrpc/request.h" +#include "webfuse/core/status.h" + +namespace +{ + struct Context + { + json_t * response; + bool is_called; + }; + + bool jsonrpc_send( + json_t * request, + void * user_data) + { + Context * context = reinterpret_cast(user_data); + context->is_called = true; + context->response = request; + json_incref(request); + + return true; + } + + void sayHello( + struct wf_jsonrpc_request * request, + char const * method_name, + json_t * params, + void * user_data) + { + (void) method_name; + (void) params; + (void) user_data; + + json_t * result = json_string("Hello"); + wf_jsonrpc_respond(request, result); + } + +} + +TEST(wf_jsonrpc_server, process_request) +{ + struct wf_jsonrpc_server * server = wf_jsonrpc_server_create(); + wf_jsonrpc_server_add(server, "sayHello", &sayHello, nullptr); + + Context context{nullptr, false}; + void * user_data = reinterpret_cast(&context); + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("sayHello")); + json_object_set_new(request, "params", json_array()); + json_object_set_new(request, "id", json_integer(23)); + wf_jsonrpc_server_process(server, request, &jsonrpc_send, user_data); + + ASSERT_TRUE(context.is_called); + ASSERT_NE(nullptr, context.response); + ASSERT_TRUE(json_is_object(context.response)); + + json_t * id = json_object_get(context.response, "id"); + ASSERT_TRUE(json_is_integer(id)); + ASSERT_EQ(23, json_integer_value(id)); + + json_t * result = json_object_get(context.response, "result"); + ASSERT_TRUE(json_is_string(result)); + ASSERT_STREQ("Hello", json_string_value(result)); + + json_decref(context.response); + json_decref(request); + wf_jsonrpc_server_dispose(server); +} + +TEST(wf_jsonrpc_server, process_request_with_oject_params) +{ + struct wf_jsonrpc_server * server = wf_jsonrpc_server_create(); + wf_jsonrpc_server_add(server, "sayHello", &sayHello, nullptr); + + Context context{nullptr, false}; + void * user_data = reinterpret_cast(&context); + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("sayHello")); + json_object_set_new(request, "params", json_object()); + json_object_set_new(request, "id", json_integer(23)); + wf_jsonrpc_server_process(server, request, &jsonrpc_send, user_data); + + ASSERT_TRUE(context.is_called); + ASSERT_NE(nullptr, context.response); + ASSERT_TRUE(json_is_object(context.response)); + + json_t * id = json_object_get(context.response, "id"); + ASSERT_TRUE(json_is_integer(id)); + ASSERT_EQ(23, json_integer_value(id)); + + json_t * result = json_object_get(context.response, "result"); + ASSERT_TRUE(json_is_string(result)); + ASSERT_STREQ("Hello", json_string_value(result)); + + json_decref(context.response); + json_decref(request); + wf_jsonrpc_server_dispose(server); +} + +TEST(wf_jsonrpc_server, invoke_unknown_method) +{ + struct wf_jsonrpc_server * server = wf_jsonrpc_server_create(); + wf_jsonrpc_server_add(server, "sayHello", &sayHello, nullptr); + + Context context{nullptr, false}; + void * user_data = reinterpret_cast(&context); + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("greet")); + json_object_set_new(request, "params", json_array()); + json_object_set_new(request, "id", json_integer(42)); + wf_jsonrpc_server_process(server, request, &jsonrpc_send, user_data); + + ASSERT_TRUE(context.is_called); + ASSERT_NE(nullptr, context.response); + ASSERT_TRUE(json_is_object(context.response)); + + json_t * id = json_object_get(context.response, "id"); + ASSERT_TRUE(json_is_integer(id)); + ASSERT_EQ(42, json_integer_value(id)); + + json_t * err = json_object_get(context.response, "error"); + ASSERT_TRUE(json_is_object(err)); + + json_t * err_code = json_object_get(err, "code"); + ASSERT_TRUE(json_is_integer(err_code)); + ASSERT_EQ(WF_BAD_NOTIMPLEMENTED, json_integer_value(err_code)); + + json_t * err_message = json_object_get(err, "message"); + ASSERT_TRUE(json_is_string(err_message)); + + json_decref(context.response); + json_decref(request); + wf_jsonrpc_server_dispose(server); +} + +TEST(wf_jsonrpc_server, skip_invalid_request_missing_id) +{ + struct wf_jsonrpc_server * server = wf_jsonrpc_server_create(); + + Context context{nullptr, false}; + void * user_data = reinterpret_cast(&context); + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("sayHello")); + json_object_set_new(request, "params", json_array()); + wf_jsonrpc_server_process(server, request, &jsonrpc_send, user_data); + + ASSERT_FALSE(context.is_called); + + json_decref(request); + wf_jsonrpc_server_dispose(server); +} + +TEST(wf_jsonrpc_server, skip_invalid_request_wrong_id_type) +{ + struct wf_jsonrpc_server * server = wf_jsonrpc_server_create(); + + Context context{nullptr, false}; + void * user_data = reinterpret_cast(&context); + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("sayHello")); + json_object_set_new(request, "params", json_array()); + json_object_set_new(request, "id", json_string("42")); + wf_jsonrpc_server_process(server, request, &jsonrpc_send, user_data); + + ASSERT_FALSE(context.is_called); + + json_decref(request); + wf_jsonrpc_server_dispose(server); +} + +TEST(wf_jsonrpc_server, skip_invalid_request_missing_params) +{ + struct wf_jsonrpc_server * server = wf_jsonrpc_server_create(); + + Context context{nullptr, false}; + void * user_data = reinterpret_cast(&context); + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("sayHello")); + json_object_set_new(request, "id", json_integer(42)); + wf_jsonrpc_server_process(server, request, &jsonrpc_send, user_data); + + ASSERT_FALSE(context.is_called); + + json_decref(request); + wf_jsonrpc_server_dispose(server); +} + +TEST(wf_jsonrpc_server, skip_invalid_request_wrong_params_type) +{ + struct wf_jsonrpc_server * server = wf_jsonrpc_server_create(); + + Context context{nullptr, false}; + void * user_data = reinterpret_cast(&context); + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("sayHello")); + json_object_set_new(request, "params", json_string("invalid")); + json_object_set_new(request, "id", json_integer(42)); + wf_jsonrpc_server_process(server, request, &jsonrpc_send, user_data); + + ASSERT_FALSE(context.is_called); + + json_decref(request); + wf_jsonrpc_server_dispose(server); +} + +TEST(wf_jsonrpc_server, skip_invalid_request_missing_method) +{ + struct wf_jsonrpc_server * server = wf_jsonrpc_server_create(); + + Context context{nullptr, false}; + void * user_data = reinterpret_cast(&context); + json_t * request = json_object(); + json_object_set_new(request, "params", json_array()); + json_object_set_new(request, "id", json_integer(42)); + wf_jsonrpc_server_process(server, request, &jsonrpc_send, user_data); + + ASSERT_FALSE(context.is_called); + + json_decref(request); + wf_jsonrpc_server_dispose(server); +} + +TEST(wf_jsonrpc_server, skip_invalid_request_wront_method_type) +{ + struct wf_jsonrpc_server * server = wf_jsonrpc_server_create(); + + Context context{nullptr, false}; + void * user_data = reinterpret_cast(&context); + json_t * request = json_object(); + json_object_set_new(request, "method", json_integer(42)); + json_object_set_new(request, "params", json_array()); + json_object_set_new(request, "id", json_integer(42)); + wf_jsonrpc_server_process(server, request, &jsonrpc_send, user_data); + + ASSERT_FALSE(context.is_called); + + json_decref(request); + wf_jsonrpc_server_dispose(server); +} \ No newline at end of file diff --git a/test/webfuse/tests/core/test_base64.cc b/test/webfuse/tests/core/test_base64.cc new file mode 100644 index 0000000..e6ae97d --- /dev/null +++ b/test/webfuse/tests/core/test_base64.cc @@ -0,0 +1,122 @@ +#include +#include "webfuse/core/base64.h" + +TEST(Base64, EncodedSize) +{ + ASSERT_EQ(4, wf_base64_encoded_size(1)); + ASSERT_EQ(4, wf_base64_encoded_size(2)); + ASSERT_EQ(4, wf_base64_encoded_size(3)); + + ASSERT_EQ(8, wf_base64_encoded_size(4)); + ASSERT_EQ(8, wf_base64_encoded_size(5)); + ASSERT_EQ(8, wf_base64_encoded_size(6)); + + ASSERT_EQ(120, wf_base64_encoded_size(90)); +} + +TEST(Base64, Encode) +{ + char buffer[42]; + + std::string in = "Hello"; + size_t length = wf_base64_encode((uint8_t const*) in.c_str(), in.size(), buffer, 42); + ASSERT_EQ(8, length); + ASSERT_STREQ("SGVsbG8=", buffer); + + in = "Hello\n"; + length = wf_base64_encode((uint8_t const*) in.c_str(), in.size(), buffer, 42); + ASSERT_EQ(8, length); + ASSERT_STREQ("SGVsbG8K", buffer); + + in = "Blue"; + length = wf_base64_encode((uint8_t const*) in.c_str(), in.size(), buffer, 42); + ASSERT_EQ(8, length); + ASSERT_STREQ("Qmx1ZQ==", buffer); +} + +TEST(Base64, FailedToEncodeBufferTooSmall) +{ + char buffer[1]; + + std::string in = "Hello"; + size_t length = wf_base64_encode((uint8_t const*) in.c_str(), in.size(), buffer, 1); + ASSERT_EQ(0, length); +} + +TEST(Base64, DecodedSize) +{ + std::string in = "SGVsbG8="; // Hello + size_t length = wf_base64_decoded_size(in.c_str(), in.size()); + ASSERT_EQ(5, length); + + in = "SGVsbG8K"; // Hello\n + length = wf_base64_decoded_size(in.c_str(), in.size()); + ASSERT_EQ(6, length); + + in = "Qmx1ZQ=="; // Blue + length = wf_base64_decoded_size(in.c_str(), in.size()); + ASSERT_EQ(4, length); +} + +TEST(Base64, IsValid) +{ + std::string in = "SGVsbG8="; // Hello + ASSERT_TRUE(wf_base64_isvalid(in.c_str(), in.size())); + + in = "SGVsbG8K"; // Hello\n + ASSERT_TRUE(wf_base64_isvalid(in.c_str(), in.size())); + + in = "Qmx1ZQ=="; // Blue + ASSERT_TRUE(wf_base64_isvalid(in.c_str(), in.size())); + + in = "Qmx1ZQ=a"; + ASSERT_FALSE(wf_base64_isvalid(in.c_str(), in.size())); + + in = "Qmx1ZQ"; + ASSERT_FALSE(wf_base64_isvalid(in.c_str(), in.size())); + + in = "Qmx1ZQ="; + ASSERT_FALSE(wf_base64_isvalid(in.c_str(), in.size())); + + in = "Qmx1Z==="; + ASSERT_FALSE(wf_base64_isvalid(in.c_str(), in.size())); + + in = "Qmx1ZQ?="; + ASSERT_FALSE(wf_base64_isvalid(in.c_str(), in.size())); + + in = "Qm?1ZQ=="; + ASSERT_FALSE(wf_base64_isvalid(in.c_str(), in.size())); +} + +TEST(Base64, Decode) +{ + char buffer[42]; + + std::string in = "SGVsbG8="; // Hello + size_t length = wf_base64_decode(in.c_str(), in.size(), (uint8_t*) buffer, 42); + ASSERT_EQ(5, length); + buffer[length] = '\0'; + ASSERT_STREQ("Hello", buffer); + + in = "SGVsbG8K"; // Hello\n + length = wf_base64_decode(in.c_str(), in.size(), (uint8_t*) buffer, 42); + ASSERT_EQ(6, length); + buffer[length] = '\0'; + ASSERT_STREQ("Hello\n", buffer); + + in = "Qmx1ZQ=="; // Blue + length = wf_base64_decode(in.c_str(), in.size(), (uint8_t*) buffer, 42); + ASSERT_EQ(4, length); + buffer[length] = '\0'; + ASSERT_STREQ("Blue", buffer); +} + +TEST(Base64, FailToDecodeBufferTooSmall) +{ + char buffer[1]; + + std::string in = "SGVsbG8="; // Hello + size_t length = wf_base64_decode(in.c_str(), in.size(), (uint8_t*) buffer, 1); + ASSERT_EQ(0, length); +} + diff --git a/test/core/test_container_of.cc b/test/webfuse/tests/core/test_container_of.cc similarity index 100% rename from test/core/test_container_of.cc rename to test/webfuse/tests/core/test_container_of.cc diff --git a/test/core/test_message.cc b/test/webfuse/tests/core/test_message.cc similarity index 100% rename from test/core/test_message.cc rename to test/webfuse/tests/core/test_message.cc diff --git a/test/core/test_message_queue.cc b/test/webfuse/tests/core/test_message_queue.cc similarity index 100% rename from test/core/test_message_queue.cc rename to test/webfuse/tests/core/test_message_queue.cc diff --git a/test/core/test_slist.cc b/test/webfuse/tests/core/test_slist.cc similarity index 100% rename from test/core/test_slist.cc rename to test/webfuse/tests/core/test_slist.cc diff --git a/test/core/test_status.cc b/test/webfuse/tests/core/test_status.cc similarity index 100% rename from test/core/test_status.cc rename to test/webfuse/tests/core/test_status.cc diff --git a/test/core/test_string.cc b/test/webfuse/tests/core/test_string.cc similarity index 100% rename from test/core/test_string.cc rename to test/webfuse/tests/core/test_string.cc diff --git a/test/adapter/jsonrpc/test_util.cc b/test/webfuse/tests/core/test_util.cc similarity index 95% rename from test/adapter/jsonrpc/test_util.cc rename to test/webfuse/tests/core/test_util.cc index 562b7c4..3e4b75d 100644 --- a/test/adapter/jsonrpc/test_util.cc +++ b/test/webfuse/tests/core/test_util.cc @@ -1,5 +1,5 @@ #include -#include "webfuse/adapter/impl/jsonrpc/util.h" +#include "webfuse/core/json_util.h" TEST(jsonrpc_util, get_int) { diff --git a/test/webfuse/tests/core/timer/test_timepoint.cc b/test/webfuse/tests/core/timer/test_timepoint.cc new file mode 100644 index 0000000..f55adab --- /dev/null +++ b/test/webfuse/tests/core/timer/test_timepoint.cc @@ -0,0 +1,38 @@ +#include + +#include "webfuse/core/timer/timepoint.h" + +#include +#include + +using namespace std::chrono_literals; + +TEST(wf_timer_timepoint, now) +{ + wf_timer_timepoint start = wf_timer_timepoint_now(); + std::this_thread::sleep_for(42ms); + wf_timer_timepoint end = wf_timer_timepoint_now(); + + ASSERT_LT(start, end); + ASSERT_LT(end, start + 500); +} + +TEST(wf_timer_timepoint, in_msec) +{ + wf_timer_timepoint now = wf_timer_timepoint_now(); + wf_timer_timepoint later = wf_timer_timepoint_in_msec(42); + + ASSERT_LT(now, later); + ASSERT_LT(later, now + 500); +} + +TEST(wf_timer_timepoint, elapsed) +{ + wf_timer_timepoint now; + + now = wf_timer_timepoint_now(); + ASSERT_TRUE(wf_timer_timepoint_is_elapsed(now - 1)); + + now = wf_timer_timepoint_now(); + ASSERT_FALSE(wf_timer_timepoint_is_elapsed(now + 500)); +} diff --git a/test/webfuse/tests/core/timer/test_timer.cc b/test/webfuse/tests/core/timer/test_timer.cc new file mode 100644 index 0000000..d25b1f5 --- /dev/null +++ b/test/webfuse/tests/core/timer/test_timer.cc @@ -0,0 +1,148 @@ +#include + +#include +#include +#include + +#include "webfuse/core/timer/timer.h" +#include "webfuse/core/timer/manager.h" + +using std::size_t; +using namespace std::chrono_literals; + +extern "C" +{ + void on_timeout(struct wf_timer * timer, void * user_data) + { + (void) timer; + + bool * triggered = reinterpret_cast(user_data); + *triggered = true; + } +} + +TEST(wf_timer, init) +{ + bool triggered = false; + struct wf_timer_manager * manager = wf_timer_manager_create(); + struct wf_timer * timer = wf_timer_create(manager, &on_timeout, reinterpret_cast(&triggered)); + + wf_timer_dispose(timer); + wf_timer_manager_dispose(manager); +} + +TEST(wf_timer, trigger) +{ + bool triggered = false; + struct wf_timer_manager * manager = wf_timer_manager_create(); + struct wf_timer * timer = wf_timer_create(manager, &on_timeout, reinterpret_cast(&triggered)); + + wf_timer_start(timer, -1); + wf_timer_manager_check(manager); + + ASSERT_TRUE(triggered); + + wf_timer_dispose(timer); + wf_timer_manager_dispose(manager); +} + +TEST(wf_timer, trigger_on_dispose) +{ + bool triggered = false; + struct wf_timer_manager * manager = wf_timer_manager_create(); + struct wf_timer * timer = wf_timer_create(manager, &on_timeout, reinterpret_cast(&triggered)); + + wf_timer_start(timer, (5 * 60 * 1000)); + + wf_timer_manager_dispose(manager); + ASSERT_TRUE(triggered); + + wf_timer_dispose(timer); +} + +TEST(wf_timer, cancel) +{ + bool triggered = false; + struct wf_timer_manager * manager = wf_timer_manager_create(); + struct wf_timer * timer = wf_timer_create(manager, &on_timeout, reinterpret_cast(&triggered)); + + wf_timer_start(timer, 250); + std::this_thread::sleep_for(500ms); + wf_timer_cancel(timer); + wf_timer_manager_check(manager); + + ASSERT_FALSE(triggered); + + wf_timer_dispose(timer); + wf_timer_manager_dispose(manager); +} + +TEST(wf_timer, cancel_multiple_timers) +{ + static size_t const count = 5; + struct wf_timer_manager * manager = wf_timer_manager_create(); + struct wf_timer * timer[count]; + + bool triggered = false; + for(size_t i = 0; i < count; i++) + { + timer[i] = wf_timer_create(manager, &on_timeout, reinterpret_cast(&triggered)); + wf_timer_start(timer[i], 0); + } + + std::this_thread::sleep_for(10ms); + for(size_t i = 0; i < count; i++) + { + wf_timer_cancel(timer[i]); + } + + wf_timer_manager_check(manager); + ASSERT_FALSE(triggered); + + for(size_t i = 0; i < count; i++) + { + wf_timer_dispose(timer[i]); + } + wf_timer_manager_dispose(manager); +} + +TEST(wf_timer, multiple_timers) +{ + static size_t const count = 5; + struct wf_timer_manager * manager = wf_timer_manager_create(); + struct wf_timer * timer[count]; + bool triggered[count]; + + for(size_t i = 0; i < count; i++) + { + timer[i] = wf_timer_create(manager, &on_timeout, reinterpret_cast(&triggered[i])); + triggered[i] = false; + wf_timer_start(timer[i], (300 - (50 * i))); + } + + for(size_t i = 0; i < count; i++) + { + std::this_thread::sleep_for(100ms); + wf_timer_manager_check(manager); + } + + for(size_t i = 0; i < count; i++) + { + ASSERT_TRUE(triggered[i]); + wf_timer_dispose(timer[i]); + } + + wf_timer_manager_dispose(manager); +} + +TEST(wf_timer, dont_trigger_null_callback) +{ + struct wf_timer_manager * manager = wf_timer_manager_create(); + struct wf_timer * timer = wf_timer_create(manager, nullptr, nullptr); + + wf_timer_start(timer, -1); + wf_timer_manager_check(manager); + + wf_timer_dispose(timer); + wf_timer_manager_dispose(manager); +} diff --git a/test/webfuse/tests/integration/file.cc b/test/webfuse/tests/integration/file.cc new file mode 100644 index 0000000..5719382 --- /dev/null +++ b/test/webfuse/tests/integration/file.cc @@ -0,0 +1,85 @@ +#include "webfuse/tests/integration/file.hpp" +#include +#include + +namespace +{ + +bool invoke(std::string const & command) +{ + int exit_code = -1; + + FILE * file = ::popen(command.c_str(), "r"); + if (nullptr != file) + { + exit_code = ::pclose(file); + } + + return (0 == exit_code); +} + +} + +namespace webfuse_test +{ + +File::File(std::string const& path) +: path_(path) +{ + +} + +File::~File() +{ + +} + +bool File::isFile() +{ + std::stringstream command; + command << "./fs_check -c is_file -f " << path_; + + return invoke(command.str()); +} + +bool File::isDirectory() +{ + std::stringstream command; + command << "./fs_check -c is_dir -f " << path_; + + return invoke(command.str()); +} + +bool File::hasAccessRights(int accessRights) +{ + std::stringstream command; + command << "./fs_check -c has_mode -f " << path_ << " -a " << accessRights; + + return invoke(command.str()); +} + +bool File::hasSize(size_t size) +{ + std::stringstream command; + command << "./fs_check -c has_size -f " << path_ << " -a " << size; + + return invoke(command.str()); +} + +bool File::hasSubdirectory(std::string const & subdir) +{ + std::stringstream command; + command << "./fs_check -c has_subdir -f " << path_ << " -a " << subdir; + + return invoke(command.str()); +} + +bool File::hasContents(std::string const & contents) +{ + std::stringstream command; + command << "./fs_check -c has_contents -f " << path_ << " -a " << contents; + + return invoke(command.str()); +} + +} \ No newline at end of file diff --git a/test/webfuse/tests/integration/file.hpp b/test/webfuse/tests/integration/file.hpp new file mode 100644 index 0000000..e36e6ce --- /dev/null +++ b/test/webfuse/tests/integration/file.hpp @@ -0,0 +1,26 @@ +#ifndef WF_TEST_INTEGRATION_FILE_HPP +#define WF_TEST_INTEGRATION_FILE_HPP + +#include + +namespace webfuse_test +{ + +class File final +{ +public: + explicit File(std::string const& path); + ~File(); + bool isFile(); + bool isDirectory(); + bool hasAccessRights(int accessRights); + bool hasSize(size_t size); + bool hasSubdirectory(std::string const & subdir); + bool hasContents(std::string const & contents); +private: + std::string path_; +}; + +} + +#endif diff --git a/test/webfuse/tests/integration/fs_check.c b/test/webfuse/tests/integration/fs_check.c new file mode 100644 index 0000000..55d24c4 --- /dev/null +++ b/test/webfuse/tests/integration/fs_check.c @@ -0,0 +1,288 @@ +/* Why this tool is used: + * + * In order to test webfuse as a fuse filesystem, file system operations should be made. + * To check for memory leaks, valgind (memcheck) is used. + * + * Early tests discovered some ugly behavior of valgrind: + * - valgrind intercepts syscalls like open, read and write + * - valgrind does not expect that syscalls are handled within the process to be checked + * + * There is a more or less (un-) documented switch, which changes valgrind's bevahior, but + * this caused other problems. + * + * The second approach used GTests's death tests. Death tests were used quite a while, + * until we discovered a configuration bug when running CTest: + * - memory leaks did not lead to test error + * + * After fixing CTest configuration, memory leaks within the death tests were shown. + * Which is correct, since death tests pematurely exits the program an therefore no + * cleanup is done. + * + * Finally, it was decided to use good old popen together with this tool. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +struct command; + +typedef bool +command_invoke_fn( + struct command * command); + +struct command +{ + command_invoke_fn * invoke; + char * file; + char * arg; + bool success; + bool has_arg; +}; + +static bool print_usage( + struct command * command) +{ + printf( + "fs_check, (c) 2020 by Webfuse authors (https://github.com/falk-werner/webfuse)\n" + "Checks file information\n" + "\n" + "Usage:\n" + "\t./fs_check --command --file [--arg ]\n" + "\n" + "Options:\n" + "\t-c, --command - Check to perform (see below)\n" + "\t-f, --file - Path to file to check\n" + "\t-a, --arg - Argument, depending on command\n" + "\n" + "Commands:\n" + "\tis_file - checks, if is a regular file; no arg\n" + "\tis_dir - checks, if is a directory; no arg\n" + "\thas_mode - checks, if has the mode given in \n" + "\thas_size - checks, if has the size given in \n" + "\thas_subdir - checks, if contains the sub directory given in \n" + "\thas_contents - checks, if has the contents given in \n" + ); + + return command->success; +} + +static bool is_file( + struct command * command) +{ + struct stat buffer; + int rc = stat(command->file, &buffer); + + return ((0 == rc) && (S_ISREG(buffer.st_mode))); +} + +static bool is_dir( + struct command * command) +{ + struct stat buffer; + int rc = stat(command->file, &buffer); + + return ((0 == rc) && (S_ISDIR(buffer.st_mode))); +} + +static bool has_mode( + struct command * command) +{ + mode_t mode = (mode_t) atoi(command->arg); + struct stat buffer; + int rc = stat(command->file, &buffer); + + return ((0 == rc) && (mode == (buffer.st_mode & 0777))); +} + +static bool has_size( + struct command * command) +{ + int size = atoi(command->arg); + struct stat buffer; + int rc = stat(command->file, &buffer); + + return ((0 == rc) && (size == (buffer.st_size))); +} + +static bool has_subdir( + struct command * command) +{ + bool result = false; + char const * subdir = command->arg; + DIR * dir = opendir(command->file); + if (NULL != dir) + { + struct dirent * entry = readdir(dir); + while (NULL != entry) + { + if (0 == strcmp(subdir, entry->d_name)) + { + result = true; + break; + } + + entry = readdir(dir); + } + + closedir(dir); + } + + return result; +} + +static bool has_contents( + struct command * command) +{ + bool result = false; + char const * contents = command->arg; + size_t length = strlen(contents); + + char * buffer = malloc(length); + FILE * file = fopen(command->file, "rb"); + { + ssize_t count = fread(buffer, 1, length, file); + fclose(file); + + result = (count == (ssize_t) length) && (0 == strncmp(buffer, contents, length)); + } + + free(buffer); + return result; +} + +static bool get_command( + struct command * command, + char const * name) +{ + static struct { + char const * name; + command_invoke_fn * invoke; + bool has_arg; + } commands[] = + { + {"is_file" , &is_file , false}, + {"is_dir" , &is_dir , false}, + {"has_mode" , &has_mode , true}, + {"has_size" , &has_size , true}, + {"has_subdir" , &has_subdir , true}, + {"has_contents", &has_contents, true}, + {NULL, NULL, false} + }; + + for (int i = 0; NULL != commands[i].name; i++) + { + if (0 == strcmp(commands[i].name, name)) + { + command->invoke = commands[i].invoke; + command->has_arg = commands[i].has_arg; + return true; + } + } + + return false; +} + +static void command_init( + struct command * command, + int argc, + char * argv[]) +{ + static struct option const options[] = + { + {"file" , required_argument, NULL, 'f'}, + {"command", required_argument, NULL, 'c'}, + {"arg" , required_argument, NULL, 'a'}, + {"help" , no_argument , NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + command->invoke = &print_usage; + command->file = NULL; + command->arg = NULL; + command->success = true; + command->has_arg = false; + + optind = 0; + bool is_finished = false; + while (!is_finished) + { + int option_index = 0; + int const c = getopt_long(argc, argv, "f:c:a:h", options, &option_index); + + switch(c) + { + case -1: + is_finished = true; + break; + case 'c': + if (!get_command(command, optarg)) + { + fprintf(stderr, "error: unknown command\n"); + command->invoke = &print_usage; + command->success = false; + is_finished = true; + } + break; + case 'f': + free(command->file); + command->file = strdup(optarg); + break; + case 'a': + free(command->arg); + command->arg = strdup(optarg); + break; + case 'h': + command->invoke = &print_usage; + break; + default: + fprintf(stderr, "error: unknown argument\n"); + command->invoke = &print_usage; + command->success = false; + is_finished = true; + break; + } + } + + if (command->success) + { + if (NULL == command->file) + { + fprintf(stderr, "error: missing required arg: -f\n"); + command->invoke = &print_usage; + command->success = false; + } + else if ((command->has_arg) && (NULL == command->arg)) + { + fprintf(stderr, "error: missing required arg: -a\n"); + command->invoke = &print_usage; + command->success = false; + } + } +} + +static void command_cleanup( + struct command * command) +{ + free(command->file); + free(command->arg); +} + +int main(int argc, char* argv[]) +{ + struct command command; + command_init(&command, argc, argv); + + bool success = command.invoke(&command); + + command_cleanup(&command); + return success ? EXIT_SUCCESS : EXIT_FAILURE; +} \ No newline at end of file diff --git a/test/integration/provider.cc b/test/webfuse/tests/integration/provider.cc similarity index 69% rename from test/integration/provider.cc rename to test/webfuse/tests/integration/provider.cc index 3ee4285..e813b4d 100644 --- a/test/integration/provider.cc +++ b/test/webfuse/tests/integration/provider.cc @@ -1,10 +1,13 @@ -#include "integration/provider.hpp" +#include "webfuse/tests/integration/provider.hpp" #include "webfuse_provider.h" #include "webfuse/provider/impl/client.h" #include #include +#include #include -#include "msleep.hpp" +#include "webfuse/utils/static_filesystem.h" + +using namespace std::chrono_literals; namespace webfuse_test { @@ -16,6 +19,9 @@ public: : is_shutdown_requested(false) { config = wfp_client_config_create(); + wfp_client_config_set_certpath(config, "client-cert.pem"); + wfp_client_config_set_keypath(config, "client-key.pem"); + wfp_client_config_set_ca_filepath(config, "server-cert.pem"); fs = wfp_static_filesystem_create(config); wfp_static_filesystem_add_text(fs, "hello.txt", 0444, "Hello, World"); @@ -24,11 +30,11 @@ public: wfp_client_connect(client, url); while (!wfp_impl_client_is_connected(client)) { - wfp_client_service(client, 100); + wfp_client_service(client); } thread = std::thread(Run, this); - webfuse_test::msleep(200); + std::this_thread::sleep_for(200ms); } ~Private() @@ -52,13 +58,20 @@ private: { std::lock_guard lock(shutdown_lock); is_shutdown_requested = true; + wfp_client_interrupt(client); } static void Run(Provider::Private * context) { while (!context->IsShutdownRequested()) { - wfp_client_service(context->client, 100); + wfp_client_service(context->client); + } + + wfp_client_disconnect(context->client); + while (wfp_impl_client_is_connected(context->client)) + { + wfp_client_service(context->client); } } diff --git a/test/integration/provider.hpp b/test/webfuse/tests/integration/provider.hpp similarity index 100% rename from test/integration/provider.hpp rename to test/webfuse/tests/integration/provider.hpp diff --git a/test/integration/server.cc b/test/webfuse/tests/integration/server.cc similarity index 53% rename from test/integration/server.cc rename to test/webfuse/tests/integration/server.cc index 0659efe..50badcc 100644 --- a/test/integration/server.cc +++ b/test/webfuse/tests/integration/server.cc @@ -1,15 +1,46 @@ -#include "integration/server.hpp" +#include "webfuse/tests/integration/server.hpp" #include #include #include #include #include +#include #include "webfuse_adapter.h" #include "webfuse/adapter/impl/server.h" -#include "msleep.hpp" #define WF_PATH_MAX (100) +extern "C" +{ + +static void webfuse_test_server_cleanup_mountpoint( + void * user_data) +{ + char * path = reinterpret_cast(user_data); + rmdir(path); + free(path); +} + +static struct wf_mountpoint * +webfuse_test_server_create_mountpoint( + char const * filesystem, + void * user_data) +{ + char const * base_dir = reinterpret_cast(user_data); + char path[WF_PATH_MAX]; + snprintf(path, WF_PATH_MAX, "%s/%s", base_dir, filesystem); + mkdir(path, 0755); + struct wf_mountpoint * mountpoint = wf_mountpoint_create(path); + wf_mountpoint_set_userdata( + mountpoint, + reinterpret_cast(strdup(path)), + &webfuse_test_server_cleanup_mountpoint); + + return mountpoint; +} + +} + namespace webfuse_test { @@ -20,17 +51,26 @@ public: : is_shutdown_requested(false) { snprintf(base_dir, WF_PATH_MAX, "%s", "/tmp/webfuse_test_integration_XXXXXX"); - mkdtemp(base_dir); + char const * result = mkdtemp(base_dir); + if (NULL == result) + { + throw std::runtime_error("unable to create temp dir"); + } + config = wf_server_config_create(); wf_server_config_set_port(config, 8080); - wf_server_config_set_mountpoint(config, base_dir); + wf_server_config_set_mountpoint_factory(config, + &webfuse_test_server_create_mountpoint, + reinterpret_cast(base_dir)); + wf_server_config_set_keypath(config, "server-key.pem"); + wf_server_config_set_certpath(config, "server-cert.pem"); server = wf_server_create(config); while (!wf_impl_server_is_operational(server)) { - wf_server_service(server, 100); + wf_server_service(server); } thread = std::thread(Run, this); @@ -57,13 +97,14 @@ private: { std::lock_guard lock(shutdown_lock); is_shutdown_requested = true; + wf_server_interrupt(server); } static void Run(Server::Private * context) { while (!context->IsShutdownRequested()) { - wf_server_service(context->server, 100); + wf_server_service(context->server); } } diff --git a/test/integration/server.hpp b/test/webfuse/tests/integration/server.hpp similarity index 100% rename from test/integration/server.hpp rename to test/webfuse/tests/integration/server.hpp diff --git a/test/webfuse/tests/integration/test_integration.cc b/test/webfuse/tests/integration/test_integration.cc new file mode 100644 index 0000000..e7e8215 --- /dev/null +++ b/test/webfuse/tests/integration/test_integration.cc @@ -0,0 +1,97 @@ +#include +#include "webfuse/tests/integration/server.hpp" +#include "webfuse/tests/integration/provider.hpp" +#include "webfuse/tests/integration/file.hpp" + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include "webfuse/core/lws_log.h" + +using webfuse_test::Server; +using webfuse_test::Provider; +using webfuse_test::File; + +namespace +{ + class IntegrationTest: public ::testing::Test + { + public: + IntegrationTest() + : server(nullptr) + , provider(nullptr) + { + json_object_seed(0); + wf_lwslog_disable(); + } + + protected: + void SetUp() + { + server = new Server(); + provider = new Provider("wss://localhost:8080/"); + } + + void TearDown() + { + delete provider; + delete server; + } + + char const * GetBaseDir() const + { + return server->GetBaseDir(); + } + private: + Server * server; + Provider * provider; + }; +} + +TEST_F(IntegrationTest, HasMountpoint) +{ + struct stat buffer; + int rc = stat(GetBaseDir(), &buffer); + + ASSERT_EQ(0, rc); + ASSERT_TRUE(S_ISDIR(buffer.st_mode)); +} + +TEST_F(IntegrationTest, ProvidesTextFile) +{ + std::string file_name = std::string(GetBaseDir()) + "/cprovider/hello.txt"; + + File file(file_name); + ASSERT_TRUE(file.isFile()); + ASSERT_TRUE(file.hasAccessRights(0444)); + ASSERT_TRUE(file.hasSize(12)); +} + +TEST_F(IntegrationTest, ReadTextFile) +{ + std::string file_name = std::string(GetBaseDir()) + "/cprovider/hello.txt"; + + File file(file_name); + ASSERT_TRUE(file.hasContents("Hello, World")); +} + +TEST_F(IntegrationTest, ReadDir) +{ + std::string dir_name = std::string(GetBaseDir()) + "/cprovider"; + + File dir(dir_name); + ASSERT_TRUE(dir.isDirectory()); + ASSERT_TRUE(dir.hasSubdirectory(".")); + ASSERT_TRUE(dir.hasSubdirectory("..")); + ASSERT_TRUE(dir.hasSubdirectory("hello.txt")); + ASSERT_FALSE(dir.hasSubdirectory("other")); +} \ No newline at end of file diff --git a/test/webfuse/tests/integration/test_lowlevel.cc b/test/webfuse/tests/integration/test_lowlevel.cc new file mode 100644 index 0000000..050b23a --- /dev/null +++ b/test/webfuse/tests/integration/test_lowlevel.cc @@ -0,0 +1,112 @@ +#include "webfuse_adapter.h" +#include "webfuse_provider.h" +#include + +#include "webfuse/utils/tempdir.hpp" + +#include + +using ::webfuse_test::TempDir; + +extern "C" +{ + +wf_mountpoint * +wf_test_integration_lowlevel_create_mountpoint( + char const *, void * user_data) +{ + auto * tempDir = reinterpret_cast(user_data); + return wf_mountpoint_create(tempDir->path()); +} + +void +wf_test_integration_lowlevel_on_connected( + void * user_data) +{ + int * state = reinterpret_cast(user_data); + *state = 1; +} + +void +wf_test_integration_lowlevel_on_disconnected( + void * user_data) +{ + int * state = reinterpret_cast(user_data); + *state = -1; +} + +bool +wf_test_integration_lowlevel_authenticate( + struct wf_credentials * credentials, + void * ) +{ + char const * username = wf_credentials_get(credentials, "username"); + char const * password = wf_credentials_get(credentials, "password"); + + return ((0 == strcmp(username, "bob")) && (0 == strcmp(password, "secret"))); +} + +void +wf_test_integration_lowlevel_get_credentials( + struct wfp_credentials * credentials, + void * ) +{ + wfp_credentials_set_type(credentials, "username"); + wfp_credentials_add(credentials, "username", "bob"); + wfp_credentials_add(credentials, "password", "secret"); +} + +} + +TEST(integration, lowlevel) +{ + TempDir dir("wf_test"); + + wf_server_protocol * server_protocol = wf_server_protocol_create( + &wf_test_integration_lowlevel_create_mountpoint, + reinterpret_cast(&dir)); + ASSERT_NE(nullptr, server_protocol); + wf_server_protocol_add_authenticator(server_protocol, "username", + &wf_test_integration_lowlevel_authenticate, nullptr); + + int state = 0; + wfp_client_config * client_config = wfp_client_config_create(); + ASSERT_NE(nullptr, client_config); + wfp_client_config_set_userdata(client_config, reinterpret_cast(&state)); + wfp_client_config_set_onconnected(client_config, &wf_test_integration_lowlevel_on_connected); + wfp_client_config_set_ondisconnected(client_config, &wf_test_integration_lowlevel_on_disconnected); + wfp_client_config_enable_authentication(client_config, &wf_test_integration_lowlevel_get_credentials); + + wfp_client_protocol * client_protocol = wfp_client_protocol_create(client_config); + ASSERT_NE(nullptr, client_protocol); + + lws_protocols protocols[3]; + memset(protocols, 0, 3 * sizeof(lws_protocols)); + wf_server_protocol_init_lws(server_protocol, &protocols[0]); + wfp_client_protocol_init_lws(client_protocol, &protocols[1]); + + lws_context_creation_info info; + memset(&info, 0, sizeof(info)); + info.port = 8080; + info.protocols = protocols; + info.vhost_name = "localhost"; + info.ws_ping_pong_interval = 10; + info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; + + struct lws_context * context = lws_create_context(&info); + ASSERT_NE(nullptr, context); + + wfp_client_protocol_connect(client_protocol, context, "ws://localhost:8080/"); + while (0 == state) + { + lws_service(context, 0); + } + + EXPECT_EQ(1, state); + + lws_context_destroy(context); + + wfp_client_protocol_dispose(client_protocol); + wfp_client_config_dispose(client_config); + wf_server_protocol_dispose(server_protocol); +} \ No newline at end of file diff --git a/test/webfuse/tests/provider/operation/test_close.cc b/test/webfuse/tests/provider/operation/test_close.cc new file mode 100644 index 0000000..3769579 --- /dev/null +++ b/test/webfuse/tests/provider/operation/test_close.cc @@ -0,0 +1,99 @@ +#include "webfuse/provider/impl/operation/close.h" +#include "webfuse/mocks/mock_provider.hpp" +#include "webfuse/mocks/fake_invokation_context.hpp" + +#include + +using ::webfuse_test::MockProvider; +using ::webfuse_test::create_context; +using ::testing::_; + +TEST(wfp_close, close) +{ + int inode = 42; + int handle = 0xdeadbeef; + int flags = 23; + + MockProvider provider; + EXPECT_CALL(provider, close(inode, handle, flags)).Times(1); + + wfp_impl_invokation_context context = create_context(provider); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(inode)); + json_array_append_new(params, json_integer(handle)); + json_array_append_new(params, json_integer(flags)); + + wfp_impl_close(&context, params, 42); + json_decref(params); +} + +TEST(wfp_close, close_fail_invalid_param_count) +{ + MockProvider provider; + EXPECT_CALL(provider, close(_,_,_)).Times(0); + + wfp_impl_invokation_context context = create_context(provider); + + json_t * params = json_array(); + wfp_impl_close(&context, params, 42); + json_decref(params); +} + +TEST(wfp_close, close_fail_inode_invalid_type) +{ + MockProvider provider; + EXPECT_CALL(provider, close(_,_,_)).Times(0); + + wfp_impl_invokation_context context = create_context(provider); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_string("42")); + json_array_append_new(params, json_integer(0)); + json_array_append_new(params, json_integer(0)); + + wfp_impl_close(&context, params, 42); + json_decref(params); +} + +TEST(wfp_close, close_fail_handle_invalid_type) +{ + MockProvider provider; + EXPECT_CALL(provider, close(_,_,_)).Times(0); + + wfp_impl_invokation_context context = create_context(provider); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(0)); + json_array_append_new(params, json_string("42")); + json_array_append_new(params, json_integer(0)); + + wfp_impl_close(&context, params, 42); + json_decref(params); +} + +TEST(wfp_close, close_fail_flags_invalid_type) +{ + MockProvider provider; + EXPECT_CALL(provider, close(_,_,_)).Times(0); + + wfp_impl_invokation_context context = create_context(provider); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(0)); + json_array_append_new(params, json_integer(0)); + json_array_append_new(params, json_string("42")); + + wfp_impl_close(&context, params, 42); + json_decref(params); +} + + +TEST(wfp_close, default_nop) +{ + wfp_impl_close_default(0, 0, 0, nullptr); +} \ No newline at end of file diff --git a/test/webfuse/tests/provider/operation/test_getattr.cc b/test/webfuse/tests/provider/operation/test_getattr.cc new file mode 100644 index 0000000..332ab89 --- /dev/null +++ b/test/webfuse/tests/provider/operation/test_getattr.cc @@ -0,0 +1,107 @@ +#include "webfuse/provider/impl/operation/getattr.h" +#include "webfuse/mocks/mock_request.hpp" +#include "webfuse/mocks/mock_provider.hpp" +#include "webfuse/mocks/fake_invokation_context.hpp" + +#include +#include + +using ::webfuse_test::MockProvider; +using ::webfuse_test::MockRequest; +using ::webfuse_test::StatMatcher; +using ::webfuse_test::create_context; +using ::testing::_; +using ::testing::Invoke; + +namespace +{ + +void free_request(wfp_request * request, ino_t) +{ + free(request); +} + +} + +TEST(wfp_impl_getattr, default_responds_error) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond_error(_,42)).Times(1); + + wfp_impl_getattr_default(req, 0, nullptr); +} + +TEST(wfp_impl_getattr, respond_file) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond(StatMatcher(23, 0754, "file"), 42)).Times(1); + + struct stat buffer; + memset(&buffer, 0, sizeof(buffer)); + buffer.st_ino = 23; + buffer.st_mode = S_IFREG | 0754; + wfp_impl_respond_getattr(req, &buffer); +} + +TEST(wfp_impl_getattr, respond_dir) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond(StatMatcher(23, 0754, "dir"), 42)).Times(1); + + struct stat buffer; + memset(&buffer, 0, sizeof(buffer)); + buffer.st_ino = 23; + buffer.st_mode = S_IFDIR | 0754; + wfp_impl_respond_getattr(req, &buffer); +} + +TEST(wfp_impl_getattr, invoke_provider) +{ + ino_t inode = 23; + MockProvider provider; + EXPECT_CALL(provider,getattr(_, inode)).Times(1).WillOnce(Invoke(free_request)); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(inode)); + + wfp_impl_getattr(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_getattr, fail_invalid_param_count) +{ + MockProvider provider; + EXPECT_CALL(provider,getattr(_, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + + wfp_impl_getattr(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_getattr, fail_invalid_inode_type) +{ + MockProvider provider; + EXPECT_CALL(provider,getattr(_, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_string("42")); + + wfp_impl_getattr(&context, params, 42); + json_decref(params); +} diff --git a/test/webfuse/tests/provider/operation/test_lookup.cc b/test/webfuse/tests/provider/operation/test_lookup.cc new file mode 100644 index 0000000..4cbccd4 --- /dev/null +++ b/test/webfuse/tests/provider/operation/test_lookup.cc @@ -0,0 +1,129 @@ +#include "webfuse/provider/impl/operation/lookup.h" +#include "webfuse/mocks/mock_request.hpp" +#include "webfuse/mocks/mock_provider.hpp" +#include "webfuse/mocks/fake_invokation_context.hpp" + +#include +#include + +using ::webfuse_test::MockProvider; +using ::webfuse_test::MockRequest; +using ::webfuse_test::StatMatcher; +using ::webfuse_test::create_context; +using ::testing::_; +using ::testing::Invoke; +using ::testing::StrEq; + +namespace +{ + +void free_request(wfp_request * request, ino_t, char const *) +{ + free(request); +} + +} + +TEST(wfp_impl_lookup, invoke_provider) +{ + ino_t inode = 42; + MockProvider provider; + EXPECT_CALL(provider,lookup(_, inode,StrEq("some.file"))).Times(1) + .WillOnce(Invoke(free_request)); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(inode)); + json_array_append_new(params, json_string("some.file")); + + wfp_impl_lookup(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_lookup, fail_invalid_param_count) +{ + MockProvider provider; + EXPECT_CALL(provider,lookup(_, _,_)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(23)); + + wfp_impl_lookup(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_lookup, fail_invalid_inode_type) +{ + MockProvider provider; + EXPECT_CALL(provider,lookup(_, _,_)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_string("23")); + json_array_append_new(params, json_string("some.file")); + + wfp_impl_lookup(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_lookup, fail_invalid_name_type) +{ + MockProvider provider; + EXPECT_CALL(provider,lookup(_, _,_)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(23)); + json_array_append_new(params, json_integer(1)); + + wfp_impl_lookup(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_lookup, default_responds_error) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond_error(_,42)).Times(1); + + wfp_impl_lookup_default(req, 1, "some.file", nullptr); +} + +TEST(wfp_impl_lookup, respond_file) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond(StatMatcher(23, 0754, "file"), 42)).Times(1); + + struct stat buffer; + memset(&buffer, 0, sizeof(buffer)); + buffer.st_ino = 23; + buffer.st_mode = S_IFREG | 0754; + wfp_impl_respond_lookup(req, &buffer); +} + +TEST(wfp_impl_lookup, respond_dir) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond(StatMatcher(23, 0754, "dir"), 42)).Times(1); + + struct stat buffer; + memset(&buffer, 0, sizeof(buffer)); + buffer.st_ino = 23; + buffer.st_mode = S_IFDIR | 0754; + wfp_impl_respond_lookup(req, &buffer); +} diff --git a/test/webfuse/tests/provider/operation/test_open.cc b/test/webfuse/tests/provider/operation/test_open.cc new file mode 100644 index 0000000..4c52b02 --- /dev/null +++ b/test/webfuse/tests/provider/operation/test_open.cc @@ -0,0 +1,114 @@ +#include "webfuse/provider/impl/operation/open.h" +#include "webfuse/mocks/mock_request.hpp" +#include "webfuse/mocks/mock_provider.hpp" +#include "webfuse/mocks/fake_invokation_context.hpp" + +#include +#include + +using ::webfuse_test::MockProvider; +using ::webfuse_test::MockRequest; +using ::webfuse_test::OpenMatcher; +using ::webfuse_test::create_context; +using ::testing::_; +using ::testing::Invoke; +using ::testing::StrEq; + +namespace +{ + +void free_request(wfp_request * request, ino_t, int) +{ + free(request); +} + +} + +TEST(wfp_impl_open, invoke_provider) +{ + ino_t inode = 42; + int flags = 0; + MockProvider provider; + EXPECT_CALL(provider,open(_, inode, flags)).Times(1) + .WillOnce(Invoke(free_request)); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(inode)); + json_array_append_new(params, json_integer(flags)); + + wfp_impl_open(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_open, fail_invalid_param_count) +{ + MockProvider provider; + EXPECT_CALL(provider,open(_, _, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(23)); + + wfp_impl_open(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_open, fail_invalid_inode_type) +{ + MockProvider provider; + EXPECT_CALL(provider,open(_, _, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_string("")); + json_array_append_new(params, json_integer(0)); + + wfp_impl_open(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_open, fail_invalid_flags_type) +{ + MockProvider provider; + EXPECT_CALL(provider,open(_, _, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(23)); + json_array_append_new(params, json_string("")); + + wfp_impl_open(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_open, default_responds_error) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond_error(_,42)).Times(1); + + wfp_impl_open_default(req, 1, 0, nullptr); +} + +TEST(wfp_impl_open, respond) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond(OpenMatcher(23), 42)).Times(1); + + wfp_impl_respond_open(req, 23); +} + diff --git a/test/webfuse/tests/provider/operation/test_read.cc b/test/webfuse/tests/provider/operation/test_read.cc new file mode 100644 index 0000000..36bf14f --- /dev/null +++ b/test/webfuse/tests/provider/operation/test_read.cc @@ -0,0 +1,173 @@ +#include "webfuse/provider/impl/operation/read.h" +#include "webfuse/mocks/mock_request.hpp" +#include "webfuse/mocks/mock_provider.hpp" +#include "webfuse/mocks/fake_invokation_context.hpp" + +#include +#include + +using ::webfuse_test::MockProvider; +using ::webfuse_test::MockRequest; +using ::webfuse_test::ReadResultMatcher; +using ::webfuse_test::create_context; +using ::testing::_; +using ::testing::Invoke; +using ::testing::StrEq; + +namespace +{ + +void free_request(wfp_request * request, ino_t, uint32_t, size_t ,size_t) +{ + free(request); +} + +} + +TEST(wfp_impl_read, invoke_provider) +{ + ino_t inode = 42; + uint32_t handle = 5; + size_t offset = 2; + size_t length = 1; + MockProvider provider; + EXPECT_CALL(provider, read(_, inode, handle, offset, length)).Times(1) + .WillOnce(Invoke(free_request)); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(inode)); + json_array_append_new(params, json_integer(handle)); + json_array_append_new(params, json_integer(offset)); + json_array_append_new(params, json_integer(length)); + + wfp_impl_read(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_read, fail_invalid_param_count) +{ + MockProvider provider; + EXPECT_CALL(provider, read(_, _, _, _, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(1)); + json_array_append_new(params, json_integer(2)); + json_array_append_new(params, json_integer(3)); + json_array_append_new(params, json_integer(4)); + json_array_append_new(params, json_integer(5)); + + wfp_impl_read(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_read, fail_invalid_inode_type) +{ + MockProvider provider; + EXPECT_CALL(provider, read(_, _, _, _, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_string("42")); + json_array_append_new(params, json_integer(2)); + json_array_append_new(params, json_integer(3)); + json_array_append_new(params, json_integer(4)); + + wfp_impl_read(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_read, fail_invalid_handle_type) +{ + MockProvider provider; + EXPECT_CALL(provider, read(_, _, _, _, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(1)); + json_array_append_new(params, json_string("42")); + json_array_append_new(params, json_integer(3)); + json_array_append_new(params, json_integer(4)); + + wfp_impl_read(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_read, fail_invalid_offset_type) +{ + MockProvider provider; + EXPECT_CALL(provider, read(_, _, _, _, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(1)); + json_array_append_new(params, json_integer(2)); + json_array_append_new(params, json_string("42")); + json_array_append_new(params, json_integer(4)); + + wfp_impl_read(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_read, fail_invalid_length_type) +{ + MockProvider provider; + EXPECT_CALL(provider, read(_, _, _, _, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(1)); + json_array_append_new(params, json_integer(2)); + json_array_append_new(params, json_integer(3)); + json_array_append_new(params, json_string("42")); + + wfp_impl_read(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_read, default_responds_error) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond_error(_,42)).Times(1); + + wfp_impl_read_default(req, 0, 0, 1, 2, nullptr); +} + +TEST(wfp_impl_read, respond) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond(ReadResultMatcher("d2Y=", "base64", 2), 42)).Times(1); + + char const data[] = "wf"; + wfp_impl_respond_read(req, data, 2); +} + +TEST(wfp_impl_read, respond_empty) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond(ReadResultMatcher("", "identity", 0), 42)).Times(1); + + wfp_impl_respond_read(req, nullptr, 0); +} diff --git a/test/webfuse/tests/provider/operation/test_readdir.cc b/test/webfuse/tests/provider/operation/test_readdir.cc new file mode 100644 index 0000000..33557f5 --- /dev/null +++ b/test/webfuse/tests/provider/operation/test_readdir.cc @@ -0,0 +1,100 @@ +#include "webfuse/provider/impl/operation/readdir.h" +#include "webfuse/mocks/mock_request.hpp" +#include "webfuse/mocks/mock_provider.hpp" +#include "webfuse/mocks/fake_invokation_context.hpp" +#include "webfuse/provider/dirbuffer.h" + +#include +#include + +using ::webfuse_test::MockProvider; +using ::webfuse_test::MockRequest; +using ::webfuse_test::ReaddirMatcher; +using ::webfuse_test::create_context; +using ::testing::_; +using ::testing::Invoke; + +namespace +{ + +void free_request(wfp_request * request, ino_t) +{ + free(request); +} + +} + +TEST(wfp_impl_readdir, invoke_provider) +{ + ino_t inode = 23; + MockProvider provider; + EXPECT_CALL(provider,readdir(_, inode)).Times(1).WillOnce(Invoke(free_request)); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(inode)); + + wfp_impl_readdir(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_readdir, fail_invalid_param_count) +{ + MockProvider provider; + EXPECT_CALL(provider,readdir(_, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_integer(1)); + json_array_append_new(params, json_integer(1)); + + wfp_impl_readdir(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_readdir, fail_invalid_inode_type) +{ + MockProvider provider; + EXPECT_CALL(provider,readdir(_, _)).Times(0); + + wfp_request request = {nullptr, nullptr, 0}; + wfp_impl_invokation_context context = create_context(provider, &request); + + json_t * params = json_array(); + json_array_append_new(params, json_string("test.filesystem")); + json_array_append_new(params, json_string("1")); + + wfp_impl_readdir(&context, params, 42); + json_decref(params); +} + +TEST(wfp_impl_readdir, default_responds_error) +{ + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond_error(_,42)).Times(1); + + wfp_impl_readdir_default(req, 0, nullptr); +} + +TEST(wfp_impl_readdir, respond) +{ + char const item0[] = "some.file"; + char const * items[] = { item0, nullptr }; + + MockRequest request; + auto * req = request.create_request(42); + EXPECT_CALL(request, respond(ReaddirMatcher(items), 42)).Times(1); + + wfp_dirbuffer * buffer = wfp_dirbuffer_create(); + wfp_dirbuffer_add(buffer, item0, 42); + wfp_impl_respond_readdir(req, buffer); + wfp_dirbuffer_dispose(buffer); + +} diff --git a/test/webfuse/tests/provider/test_client_protocol.cc b/test/webfuse/tests/provider/test_client_protocol.cc new file mode 100644 index 0000000..2035b0f --- /dev/null +++ b/test/webfuse/tests/provider/test_client_protocol.cc @@ -0,0 +1,235 @@ +#include +#include + +#include +#include +#include "webfuse/utils/ws_server.hpp" +#include "webfuse/mocks/mock_provider_client.hpp" + +#include +#include +#include + +using webfuse_test::WebsocketServer; +using webfuse_test::MockProviderClient; +using webfuse_test::IProviderClient; +using testing::_; +using testing::AtMost; +using testing::Invoke; + +namespace +{ + +class ClientProtocolFixture +{ + ClientProtocolFixture(ClientProtocolFixture const &) = delete; + ClientProtocolFixture& operator=(ClientProtocolFixture const &) = delete; +public: + explicit ClientProtocolFixture(IProviderClient& client, bool enableAuthentication = false) + { + 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); + + server = new WebsocketServer(54321, &client_protocol, 1); + } + + ~ClientProtocolFixture() + { + delete server; + wfp_client_protocol_dispose(protocol); + wfp_client_config_dispose(config); + } + + void Connect() + { + wfp_client_protocol_connect(protocol, server->getContext(), "ws://localhost:54321/"); + server->waitForConnection(); + } + + void Disconnect() + { + wfp_client_protocol_disconnect(protocol); + } + + void SendToClient(json_t * request) + { + server->sendMessage(request); + } + + json_t * ReceiveMessageFromClient() + { + return server->receiveMessage(); + } + + void AwaitAuthentication( + std::string const & expected_username, + std::string const & expected_password) + { + json_t * request = server->receiveMessage(); + ASSERT_TRUE(json_is_object(request)); + + json_t * method = json_object_get(request, "method"); + ASSERT_TRUE(json_is_string(method)); + ASSERT_STREQ("authenticate", json_string_value(method)); + + json_t * id = json_object_get(request, "id"); + ASSERT_TRUE(json_is_integer(id)); + + json_t * params = json_object_get(request, "params"); + ASSERT_TRUE(json_is_array(params)); + ASSERT_EQ(2, json_array_size(params)); + + json_t * type = json_array_get(params, 0); + ASSERT_TRUE(json_is_string(type)); + ASSERT_STREQ("username", json_string_value(type)); + + json_t * credentials = json_array_get(params, 1); + ASSERT_TRUE(json_is_object(credentials)); + + json_t * username = json_object_get(credentials, "username"); + ASSERT_TRUE(json_is_string(username)); + ASSERT_STREQ(expected_username.c_str(), json_string_value(username)); + + json_t * password = json_object_get(credentials, "password"); + ASSERT_TRUE(json_is_string(password)); + ASSERT_STREQ(expected_password.c_str(), json_string_value(password)); + + json_t * response = json_object(); + json_object_set_new(response, "result", json_object()); + json_object_set(response, "id", id); + server->sendMessage(response); + + json_decref(request); + } + + void AwaitAddFilesystem(std::string& filesystemName) + { + json_t * addFilesystemRequest = server->receiveMessage(); + ASSERT_NE(nullptr, addFilesystemRequest); + ASSERT_TRUE(json_is_object(addFilesystemRequest)); + + json_t * method = json_object_get(addFilesystemRequest, "method"); + ASSERT_TRUE(json_is_string(method)); + ASSERT_STREQ("add_filesystem", json_string_value(method)); + + json_t * params = json_object_get(addFilesystemRequest, "params"); + ASSERT_TRUE(json_is_array(params)); + ASSERT_EQ(1, json_array_size(params)); + + json_t * filesystem = json_array_get(params, 0); + ASSERT_TRUE(json_is_string(filesystem)); + filesystemName = json_string_value(filesystem); + + json_t * id = json_object_get(addFilesystemRequest, "id"); + ASSERT_TRUE(json_is_integer(id)); + + json_t * response = json_object(); + json_t * result = json_object(); + json_object_set(result, "id", filesystem); + json_object_set_new(response, "result", result); + json_object_set(response, "id", id); + + server->sendMessage(response); + + json_decref(addFilesystemRequest); + } + +private: + WebsocketServer * server; + wfp_client_config * config; + wfp_client_protocol * protocol; +}; + +void GetCredentials(wfp_credentials * credentials) +{ + wfp_credentials_set_type(credentials, "username"); + wfp_credentials_add(credentials, "username", "bob"); + wfp_credentials_add(credentials, "password", "secret"); +} + +} + +TEST(client_protocol, connect) +{ + MockProviderClient provider; + ClientProtocolFixture fixture(provider); + + EXPECT_CALL(provider, OnConnected()).Times(AtMost(1)); + EXPECT_CALL(provider, OnDisconnected()).Times(1); + + fixture.Connect(); + if (HasFatalFailure()) { return; } + + std::string filesystem; + fixture.AwaitAddFilesystem(filesystem); + if (HasFatalFailure()) { return; } +} + +TEST(client_protocol, disconnect_without_connect) +{ + MockProviderClient provider; + ClientProtocolFixture fixture(provider); + + EXPECT_CALL(provider, OnDisconnected()).Times(1); + + fixture.Disconnect(); +} + +TEST(client_protocol, connect_with_username_authentication) +{ + MockProviderClient provider; + ClientProtocolFixture fixture(provider, true); + + EXPECT_CALL(provider, OnConnected()).Times(AtMost(1)); + EXPECT_CALL(provider, OnDisconnected()).Times(1); + EXPECT_CALL(provider, GetCredentials(_)).Times(1).WillOnce(Invoke(GetCredentials)); + + fixture.Connect(); + if (HasFatalFailure()) { return; } + + fixture.AwaitAuthentication("bob", "secret"); + if (HasFatalFailure()) { return; } + + std::string filesystem; + fixture.AwaitAddFilesystem(filesystem); + if (HasFatalFailure()) { return; } +} + +TEST(client_protocol, getattr) +{ + MockProviderClient provider; + ClientProtocolFixture fixture(provider); + + EXPECT_CALL(provider, OnConnected()).Times(1); + EXPECT_CALL(provider, OnDisconnected()).Times(1); + EXPECT_CALL(provider, GetAttr(1, _)).Times(1); + + fixture.Connect(); + if (HasFatalFailure()) { return; } + + std::string filesystem; + fixture.AwaitAddFilesystem(filesystem); + if (HasFatalFailure()) { return; } + + json_t * params = json_array(); + json_array_append_new(params, json_string(filesystem.c_str())); + json_array_append_new(params, json_integer(1)); + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("getattr")); + json_object_set_new(request, "params", params); + json_object_set_new(request, "id", json_integer(42)); + + fixture.SendToClient(request); + json_t * response = fixture.ReceiveMessageFromClient(); + ASSERT_TRUE(json_is_object(response)); + + json_decref(response); + + fixture.Disconnect(); +} \ No newline at end of file diff --git a/test/provider/test_url.cc b/test/webfuse/tests/provider/test_url.cc similarity index 100% rename from test/provider/test_url.cc rename to test/webfuse/tests/provider/test_url.cc diff --git a/test/webfuse/utils/file_utils.cc b/test/webfuse/utils/file_utils.cc new file mode 100644 index 0000000..7a3d29c --- /dev/null +++ b/test/webfuse/utils/file_utils.cc @@ -0,0 +1,38 @@ +#include "webfuse/utils/file_utils.hpp" + +#include +#include +#include + +namespace webfuse_test +{ + +bool is_dir(std::string const & path) +{ + struct stat info; + int rc = stat(path.c_str(), &info); + + return (0 == rc) && (S_ISDIR(info.st_mode)); +} + +bool is_symlink(std::string const & path) +{ + struct stat info; + int rc = lstat(path.c_str(), &info); + + return (0 == rc) && (S_ISLNK(info.st_mode)); +} + +bool is_same_path(std::string const & path, std::string const & other) +{ + struct stat info; + int rc = stat(path.c_str(), &info); + + struct stat info_other; + int rc_other = stat(other.c_str(), &info_other); + + return (0 == rc) && (0 == rc_other) && (info.st_ino == info_other.st_ino); +} + + +} \ No newline at end of file diff --git a/test/webfuse/utils/file_utils.hpp b/test/webfuse/utils/file_utils.hpp new file mode 100644 index 0000000..0c8cbc1 --- /dev/null +++ b/test/webfuse/utils/file_utils.hpp @@ -0,0 +1,17 @@ +#ifndef WF_TEST_FILE_UTILS_HPP +#define WF_TEST_FILE_UTILS_HPP + +#include + +namespace webfuse_test +{ + +bool is_dir(std::string const & path); + +bool is_symlink(std::string const & path); + +bool is_same_path(std::string const & path, std::string const & other); + +} + +#endif diff --git a/lib/webfuse/core/path.c b/test/webfuse/utils/path.c similarity index 76% rename from lib/webfuse/core/path.c rename to test/webfuse/utils/path.c index 36689f0..e63aafb 100644 --- a/lib/webfuse/core/path.c +++ b/test/webfuse/utils/path.c @@ -1,4 +1,4 @@ -#include "webfuse/core/path.h" +#include "webfuse/utils/path.h" #include #include @@ -45,24 +45,21 @@ wf_path_create( char const * value) { struct wf_path * path = malloc(sizeof(struct wf_path)); - if (NULL != path) + path->elements = malloc(sizeof(char*) * WF_PATH_DEFAULT_CAPACITY); + path->capacity = WF_PATH_DEFAULT_CAPACITY; + path->count = 0; + + char const * remainder = value; + char const * pos = strchr(remainder, '/'); + while (NULL != pos) { - path->elements = malloc(sizeof(char*) * WF_PATH_DEFAULT_CAPACITY); - path->capacity = WF_PATH_DEFAULT_CAPACITY; - path->count = 0; - - char const * remainder = value; - char const * pos = strchr(remainder, '/'); - while (NULL != pos) - { - wf_path_add(path, remainder, (pos - remainder)); - remainder = pos + 1; - pos = strchr(remainder, '/'); - } - - wf_path_add(path, remainder, strlen(remainder)); + wf_path_add(path, remainder, (pos - remainder)); + remainder = pos + 1; + pos = strchr(remainder, '/'); } + wf_path_add(path, remainder, strlen(remainder)); + return path; } diff --git a/lib/webfuse/core/path.h b/test/webfuse/utils/path.h similarity index 100% rename from lib/webfuse/core/path.h rename to test/webfuse/utils/path.h diff --git a/lib/webfuse/provider/impl/static_filesystem.c b/test/webfuse/utils/static_filesystem.c similarity index 58% rename from lib/webfuse/provider/impl/static_filesystem.c rename to test/webfuse/utils/static_filesystem.c index f378f3d..855564b 100644 --- a/lib/webfuse/provider/impl/static_filesystem.c +++ b/test/webfuse/utils/static_filesystem.c @@ -1,9 +1,9 @@ -#include "webfuse/provider/impl/static_filesystem.h" +#include "webfuse/utils/static_filesystem.h" #include "webfuse/provider/client_config.h" #include "webfuse/provider/dirbuffer.h" #include "webfuse/provider/operation/error.h" -#include "webfuse/core/path.h" +#include "webfuse/utils/path.h" #include "webfuse/core/util.h" #include @@ -16,7 +16,7 @@ #include #define WFP_STATIC_FILESYSTEM_DEFAULT_CAPACITY (16) -#define WFP_STATIC_FILSYSTEM_INDOE_ROOT (1) +#define WFP_STATIC_FILSYSTEM_INODE_ROOT (1) #define WFP_STATIC_FILESYSTEM_MAX_READ_SIZE (4 * 1024) struct wfp_static_filesystem_entry @@ -41,7 +41,7 @@ struct wfp_static_filesystem }; static struct wfp_static_filesystem_entry * -wfp_impl_static_filesystem_get_entry( +wfp_static_filesystem_get_entry( struct wfp_static_filesystem * filesystem, size_t inode) { @@ -56,7 +56,7 @@ wfp_impl_static_filesystem_get_entry( } static struct wfp_static_filesystem_entry * -wfp_impl_static_filesystem_get_entry_by_name( +wfp_static_filesystem_get_entry_by_name( struct wfp_static_filesystem * filesystem, size_t parent, char const * name) @@ -76,7 +76,7 @@ wfp_impl_static_filesystem_get_entry_by_name( } static struct wfp_static_filesystem_entry * -wfp_impl_static_filesystem_add_entry( +wfp_static_filesystem_add_entry( struct wfp_static_filesystem * filesystem) { struct wfp_static_filesystem_entry * entry = NULL; @@ -107,7 +107,7 @@ wfp_impl_static_filesystem_add_entry( } static size_t -wfp_impl_static_filesystem_entry_read( +wfp_static_filesystem_entry_read( size_t offset, char * buffer, size_t buffer_size, @@ -126,7 +126,7 @@ wfp_impl_static_filesystem_entry_read( } static void -wfp_impl_static_filesystem_entry_get_info( +wfp_static_filesystem_entry_get_info( void * user_data, int * result_mode, size_t * result_size) @@ -137,79 +137,36 @@ wfp_impl_static_filesystem_entry_get_info( } static size_t -wfp_impl_static_filesystem_file_read( - size_t offset, - char * buffer, - size_t buffer_size, - void * user_data) -{ - size_t result = 0; - struct wfp_static_filesystem_entry * entry = user_data; - char const * filename = entry->content; - - FILE * file = fopen(filename, "rb"); - if (NULL != file) - { - fseek(file, offset, SEEK_SET); - result = fread(buffer, buffer_size, 1, file); - fclose(file); - } - - return result; -} - -static void -wfp_impl_static_filesystem_file_get_info( - void * user_data, - int * result_mode, - size_t * result_size) -{ - struct wfp_static_filesystem_entry * entry = user_data; - char const * filename = entry->content; - - struct stat buffer; - stat(filename, &buffer); - - *result_mode = (int) (buffer.st_mode & 0777); - *result_size = (size_t) buffer.st_size; -} - - -static size_t -wfp_impl_static_filesystem_add_dir( +wfp_static_filesystem_add_dir( struct wfp_static_filesystem * filesystem, size_t parent, char const * name ) { - size_t result = 0; - - struct wfp_static_filesystem_entry * entry = wfp_impl_static_filesystem_get_entry_by_name(filesystem, parent, name); + struct wfp_static_filesystem_entry * entry = wfp_static_filesystem_get_entry_by_name(filesystem, parent, name); if (NULL == entry) { - entry = wfp_impl_static_filesystem_add_entry(filesystem); + entry = wfp_static_filesystem_add_entry(filesystem); entry->parent = parent; entry->is_file = false; entry->mode = 0555; entry->name = strdup(name); entry->user_data = entry; - entry->read = &wfp_impl_static_filesystem_entry_read; - entry->get_info = &wfp_impl_static_filesystem_entry_get_info; + entry->read = &wfp_static_filesystem_entry_read; + entry->get_info = &wfp_static_filesystem_entry_get_info; entry->size = 0; entry->content = NULL; - - result = entry->inode; } - - return result; + + return entry->inode; } static size_t -wfp_impl_static_filesystem_make_parent( +wfp_static_filesystem_make_parent( struct wfp_static_filesystem * filesystem, struct wf_path * path) { - size_t result = WFP_STATIC_FILSYSTEM_INDOE_ROOT; + size_t result = WFP_STATIC_FILSYSTEM_INODE_ROOT; size_t count = wf_path_element_count(path); if (0 < count) @@ -217,7 +174,7 @@ wfp_impl_static_filesystem_make_parent( for(size_t i = 0; i < (count - 1); i++) { char const * name = wf_path_get_element(path, i); - result = wfp_impl_static_filesystem_add_dir(filesystem, result, name); + result = wfp_static_filesystem_add_dir(filesystem, result, name); } } @@ -225,7 +182,7 @@ wfp_impl_static_filesystem_make_parent( } static void -wfp_impl_static_filesystem_stat( +wfp_static_filesystem_stat( struct wfp_static_filesystem_entry * entry, struct stat * stat ) @@ -242,19 +199,19 @@ wfp_impl_static_filesystem_stat( stat->st_mode |= (entry->is_file) ? S_IFREG: S_IFDIR; } -static void wfp_impl_static_filesystem_lookup( +static void wfp_static_filesystem_lookup( struct wfp_request * request, ino_t parent, char const * name, void * user_data) { struct wfp_static_filesystem * filesystem = user_data; - struct wfp_static_filesystem_entry * entry = wfp_impl_static_filesystem_get_entry_by_name(filesystem, parent, name); + struct wfp_static_filesystem_entry * entry = wfp_static_filesystem_get_entry_by_name(filesystem, parent, name); if (NULL != entry) { struct stat stat; - wfp_impl_static_filesystem_stat(entry, &stat); + wfp_static_filesystem_stat(entry, &stat); wfp_respond_lookup(request, &stat); } else @@ -264,18 +221,18 @@ static void wfp_impl_static_filesystem_lookup( } -static void wfp_impl_static_filesystem_getattr( +static void wfp_static_filesystem_getattr( struct wfp_request * request, ino_t inode, void * user_data) { struct wfp_static_filesystem * filesystem = user_data; - struct wfp_static_filesystem_entry * entry = wfp_impl_static_filesystem_get_entry(filesystem, inode); + struct wfp_static_filesystem_entry * entry = wfp_static_filesystem_get_entry(filesystem, inode); if (NULL != entry) { struct stat stat; - wfp_impl_static_filesystem_stat(entry, &stat); + wfp_static_filesystem_stat(entry, &stat); wfp_respond_getattr(request, &stat); } else @@ -284,13 +241,13 @@ static void wfp_impl_static_filesystem_getattr( } } -static void wfp_impl_static_filesystem_readdir( +static void wfp_static_filesystem_readdir( struct wfp_request * request, ino_t directory, void * user_data) { struct wfp_static_filesystem * filesystem = user_data; - struct wfp_static_filesystem_entry * dir = wfp_impl_static_filesystem_get_entry(filesystem, directory); + struct wfp_static_filesystem_entry * dir = wfp_static_filesystem_get_entry(filesystem, directory); if ((NULL != dir) && (!dir->is_file)) { @@ -316,14 +273,14 @@ static void wfp_impl_static_filesystem_readdir( } } -static void wfp_impl_static_filesystem_open( +static void wfp_static_filesystem_open( struct wfp_request * request, ino_t inode, int flags, void * user_data) { struct wfp_static_filesystem * filesystem = user_data; - struct wfp_static_filesystem_entry * entry = wfp_impl_static_filesystem_get_entry(filesystem, inode); + struct wfp_static_filesystem_entry * entry = wfp_static_filesystem_get_entry(filesystem, inode); if ((NULL != entry) && (entry->is_file)) { @@ -342,7 +299,7 @@ static void wfp_impl_static_filesystem_open( } } -static void wfp_impl_static_filesystem_read( +static void wfp_static_filesystem_read( struct wfp_request * request, ino_t inode, uint32_t WF_UNUSED_PARAM(handle), @@ -351,7 +308,7 @@ static void wfp_impl_static_filesystem_read( void * user_data) { struct wfp_static_filesystem * filesystem = user_data; - struct wfp_static_filesystem_entry * entry = wfp_impl_static_filesystem_get_entry(filesystem, inode); + struct wfp_static_filesystem_entry * entry = wfp_static_filesystem_get_entry(filesystem, inode); if ((NULL != entry) && (entry->is_file)) { @@ -369,33 +326,30 @@ static void wfp_impl_static_filesystem_read( struct wfp_static_filesystem * -wfp_impl_static_filesystem_create( +wfp_static_filesystem_create( struct wfp_client_config * config) { (void) config; struct wfp_static_filesystem * filesystem = malloc(sizeof(struct wfp_static_filesystem)); - if (NULL != filesystem) - { - filesystem->entries = malloc(sizeof(struct wfp_static_filesystem_entry) * WFP_STATIC_FILESYSTEM_DEFAULT_CAPACITY); - filesystem->size = 0; - filesystem->capacity = WFP_STATIC_FILESYSTEM_DEFAULT_CAPACITY; + filesystem->entries = malloc(sizeof(struct wfp_static_filesystem_entry) * WFP_STATIC_FILESYSTEM_DEFAULT_CAPACITY); + filesystem->size = 0; + filesystem->capacity = WFP_STATIC_FILESYSTEM_DEFAULT_CAPACITY; - wfp_impl_static_filesystem_add_dir(filesystem, 0, ""); + wfp_static_filesystem_add_dir(filesystem, 0, ""); - wfp_client_config_set_userdata(config, filesystem); - wfp_client_config_set_onlookup(config, &wfp_impl_static_filesystem_lookup); - wfp_client_config_set_ongetattr(config, &wfp_impl_static_filesystem_getattr); - wfp_client_config_set_onreaddir(config, &wfp_impl_static_filesystem_readdir); - wfp_client_config_set_onopen(config, &wfp_impl_static_filesystem_open); - wfp_client_config_set_onread(config, &wfp_impl_static_filesystem_read); - } + wfp_client_config_set_userdata(config, filesystem); + wfp_client_config_set_onlookup(config, &wfp_static_filesystem_lookup); + wfp_client_config_set_ongetattr(config, &wfp_static_filesystem_getattr); + wfp_client_config_set_onreaddir(config, &wfp_static_filesystem_readdir); + wfp_client_config_set_onopen(config, &wfp_static_filesystem_open); + wfp_client_config_set_onread(config, &wfp_static_filesystem_read); return filesystem; } void -wfp_impl_static_filesystem_dispose( +wfp_static_filesystem_dispose( struct wfp_static_filesystem * filesystem) { for(size_t i = 0; i < filesystem->size; i++) @@ -410,7 +364,7 @@ wfp_impl_static_filesystem_dispose( } void -wfp_impl_static_filesystem_add( +wfp_static_filesystem_add( struct wfp_static_filesystem * filesystem, char const * path, int mode, @@ -420,15 +374,15 @@ wfp_impl_static_filesystem_add( struct wf_path * path_ = wf_path_create(path); if (NULL != path_) { - size_t parent = wfp_impl_static_filesystem_make_parent(filesystem, path_); - struct wfp_static_filesystem_entry * entry = wfp_impl_static_filesystem_add_entry(filesystem); + size_t parent = wfp_static_filesystem_make_parent(filesystem, path_); + struct wfp_static_filesystem_entry * entry = wfp_static_filesystem_add_entry(filesystem); entry->parent = parent; entry->is_file = true; entry->name = strdup(wf_path_get_filename(path_)); entry->mode = mode; entry->size = length; - entry->get_info = &wfp_impl_static_filesystem_entry_get_info; - entry->read = &wfp_impl_static_filesystem_entry_read; + entry->get_info = &wfp_static_filesystem_entry_get_info; + entry->read = &wfp_static_filesystem_entry_read; entry->user_data = entry; entry->content = malloc(length); @@ -439,64 +393,12 @@ wfp_impl_static_filesystem_add( } void -wfp_impl_static_filesystem_add_text( +wfp_static_filesystem_add_text( struct wfp_static_filesystem * filesystem, char const * path, int mode, char const * content) { size_t length = strlen(content); - wfp_impl_static_filesystem_add(filesystem, path, mode, content, length); -} - -void -wfp_impl_static_filesystem_add_file( - struct wfp_static_filesystem * filesystem, - char const * path, - char const * filename) -{ - struct wf_path * path_ = wf_path_create(path); - if (NULL != path_) - { - size_t parent = wfp_impl_static_filesystem_make_parent(filesystem, path_); - struct wfp_static_filesystem_entry * entry = wfp_impl_static_filesystem_add_entry(filesystem); - entry->parent = parent; - entry->is_file = true; - entry->mode = 0; - entry->content = strdup(filename); - entry->size = 0; - entry->name = strdup(wf_path_get_filename(path_)); - entry->get_info = &wfp_impl_static_filesystem_file_get_info; - entry->read = &wfp_impl_static_filesystem_file_read; - entry->user_data = entry; - - wf_path_dispose(path_); - } -} - -void -wfp_impl_static_filesystem_add_generic( - struct wfp_static_filesystem * filesystem, - char const * path, - wfp_static_filesystem_read_fn * read, - wfp_static_filesystem_get_info_fn * get_info, - void * user_data) -{ - struct wf_path * path_ = wf_path_create(path); - if (NULL != path_) - { - size_t parent = wfp_impl_static_filesystem_make_parent(filesystem, path_); - struct wfp_static_filesystem_entry * entry = wfp_impl_static_filesystem_add_entry(filesystem); - entry->parent = parent; - entry->is_file = true; - entry->mode = 0; - entry->content = NULL; - entry->size = 0; - entry->name = strdup(wf_path_get_filename(path_)); - entry->get_info = get_info; - entry->read = read; - entry->user_data = user_data; - - wf_path_dispose(path_); - } + wfp_static_filesystem_add(filesystem, path, mode, content, length); } diff --git a/include/webfuse/provider/static_filesystem.h b/test/webfuse/utils/static_filesystem.h similarity index 74% rename from include/webfuse/provider/static_filesystem.h rename to test/webfuse/utils/static_filesystem.h index b2bdbfc..bf23b88 100644 --- a/include/webfuse/provider/static_filesystem.h +++ b/test/webfuse/utils/static_filesystem.h @@ -16,6 +16,7 @@ extern "C" #endif struct wfp_client_config; + struct wfp_static_filesystem; typedef size_t @@ -55,20 +56,6 @@ wfp_static_filesystem_add_text( int mode, char const * content); -extern WFP_API void -wfp_static_filesystem_add_file( - struct wfp_static_filesystem * filesystem, - char const * path, - char const * filename); - -extern WFP_API void -wfp_static_filesystem_add_generic( - struct wfp_static_filesystem * filesystem, - char const * path, - wfp_static_filesystem_read_fn * read, - wfp_static_filesystem_get_info_fn * get_info, - void * user_data); - #ifdef __cplusplus } #endif diff --git a/test/webfuse/utils/tempdir.cc b/test/webfuse/utils/tempdir.cc new file mode 100644 index 0000000..31dce25 --- /dev/null +++ b/test/webfuse/utils/tempdir.cc @@ -0,0 +1,33 @@ +#include "webfuse/core/string.h" +#include "webfuse/utils/tempdir.hpp" + +#include +#include +#include + +namespace webfuse_test +{ + +TempDir::TempDir(char const * prefix) +: path_(wf_create_string("/tmp/%s_XXXXXX", prefix)) +{ + char * result = mkdtemp(path_); + if (NULL == result) + { + throw std::runtime_error("unable to create temp dir"); + } +} + +TempDir::~TempDir() +{ + rmdir(path_); + free(path_); +} + +char const * TempDir::path() +{ + return path_; +} + + +} \ No newline at end of file diff --git a/test/webfuse/utils/tempdir.hpp b/test/webfuse/utils/tempdir.hpp new file mode 100644 index 0000000..6a03fc5 --- /dev/null +++ b/test/webfuse/utils/tempdir.hpp @@ -0,0 +1,21 @@ +#ifndef WF_TEST_TEMPDIR_HPP +#define WF_TEST_TEMPDIR_HPP + +namespace webfuse_test +{ + +class TempDir +{ + TempDir(TempDir const &) = delete; + TempDir & operator=(TempDir const &) = delete; +public: + explicit TempDir(char const * prefix); + ~TempDir(); + char const * path(); +private: + char * path_; +}; + +} + +#endif diff --git a/test/webfuse/utils/timeout_watcher.cc b/test/webfuse/utils/timeout_watcher.cc new file mode 100644 index 0000000..8d9add7 --- /dev/null +++ b/test/webfuse/utils/timeout_watcher.cc @@ -0,0 +1,44 @@ +#include "webfuse/utils/timeout_watcher.hpp" +#include + +using std::chrono::milliseconds; +using std::chrono::duration_cast; +using std::chrono::steady_clock; + +namespace +{ + milliseconds now() + { + return duration_cast(steady_clock::now().time_since_epoch()); + } +} + +namespace webfuse_test +{ + +TimeoutWatcher::TimeoutWatcher(milliseconds timeout) +: startedAt(now()) +, timeout_(timeout) +{ + +} + +TimeoutWatcher::~TimeoutWatcher() +{ + +} + +bool TimeoutWatcher::isTimeout() +{ + return (now() - startedAt) > timeout_; +} + +void TimeoutWatcher::check() +{ + if (isTimeout()) + { + throw std::runtime_error("timeout"); + } +} + +} \ No newline at end of file diff --git a/test/webfuse/utils/timeout_watcher.hpp b/test/webfuse/utils/timeout_watcher.hpp new file mode 100644 index 0000000..278d25f --- /dev/null +++ b/test/webfuse/utils/timeout_watcher.hpp @@ -0,0 +1,26 @@ +#ifndef WF_TEST_TIMEOUT_WATCHER_HPP +#define WF_TEST_TIMEOUT_WATCHER_HPP + +#include + +namespace webfuse_test +{ + +class TimeoutWatcher final +{ + TimeoutWatcher(TimeoutWatcher const & other) = delete; + TimeoutWatcher& operator=(TimeoutWatcher const & other) = delete; +public: + explicit TimeoutWatcher(std::chrono::milliseconds timeout); + ~TimeoutWatcher(); + bool isTimeout(); + void check(); +private: + std::chrono::milliseconds startedAt; + std::chrono::milliseconds timeout_; +}; + +} + + +#endif diff --git a/test/webfuse/utils/wrap.hpp b/test/webfuse/utils/wrap.hpp new file mode 100644 index 0000000..30694de --- /dev/null +++ b/test/webfuse/utils/wrap.hpp @@ -0,0 +1,132 @@ +#ifndef WF_WRAP_HPP +#define WF_WRAP_HPP + +#define WF_WRAP_FUNC0( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (); \ + RETURN_TYPE __wrap_ ## FUNC_NAME () \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(); \ + } \ + } + +#define WF_WRAP_FUNC1( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME, ARG1_TYPE ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (ARG1_TYPE); \ + RETURN_TYPE __wrap_ ## FUNC_NAME (ARG1_TYPE arg1) \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (arg1); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(arg1); \ + } \ + } + +#define WF_WRAP_FUNC2( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME, ARG1_TYPE, ARG2_TYPE ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (ARG1_TYPE, ARG2_TYPE); \ + RETURN_TYPE __wrap_ ## FUNC_NAME (ARG1_TYPE arg1, ARG2_TYPE arg2) \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (arg1, arg2); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(arg1, arg2); \ + } \ + } + +#define WF_WRAP_FUNC3( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (ARG1_TYPE, ARG2_TYPE, ARG3_TYPE); \ + RETURN_TYPE __wrap_ ## FUNC_NAME (ARG1_TYPE arg1, ARG2_TYPE arg2, ARG3_TYPE arg3) \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (arg1, arg2, arg3); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(arg1, arg2, arg3); \ + } \ + } + +#define WF_WRAP_FUNC4( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE); \ + RETURN_TYPE __wrap_ ## FUNC_NAME (ARG1_TYPE arg1, ARG2_TYPE arg2, ARG3_TYPE arg3, ARG4_TYPE arg4) \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (arg1, arg2, arg3, arg4); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(arg1, arg2, arg3, arg4); \ + } \ + } + +#define WF_WRAP_FUNC5( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE, ARG5_TYPE ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE, ARG5_TYPE); \ + RETURN_TYPE __wrap_ ## FUNC_NAME (ARG1_TYPE arg1, ARG2_TYPE arg2, ARG3_TYPE arg3, ARG4_TYPE arg4, ARG5_TYPE arg5) \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (arg1, arg2, arg3, arg4, arg5); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(arg1, arg2, arg3, arg4, arg5); \ + } \ + } + +#define WF_WRAP_FUNC6( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE, ARG5_TYPE, ARG6_TYPE ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE, ARG5_TYPE, ARG6_TYPE); \ + RETURN_TYPE __wrap_ ## FUNC_NAME (ARG1_TYPE arg1, ARG2_TYPE arg2, ARG3_TYPE arg3, ARG4_TYPE arg4, ARG5_TYPE arg5, ARG6_TYPE arg6) \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (arg1, arg2, arg3, arg4, arg5, arg6); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(arg1, arg2, arg3, arg4, arg5, arg6); \ + } \ + } + + +#define WF_WRAP_VFUNC3( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, va_list); \ + RETURN_TYPE __wrap_ ## FUNC_NAME (ARG1_TYPE arg1, ARG2_TYPE arg2, ARG3_TYPE arg3, va_list args) \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (arg1, arg2, arg3, args); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(arg1, arg2, arg3); \ + } \ + } + +#define WF_WRAP_VFUNC5( GLOBAL_VAR, RETURN_TYPE, FUNC_NAME, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE, ARG5_TYPE ) \ + extern RETURN_TYPE __real_ ## FUNC_NAME (ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE, ARG5_TYPE, va_list); \ + RETURN_TYPE __wrap_ ## FUNC_NAME (ARG1_TYPE arg1, ARG2_TYPE arg2, ARG3_TYPE arg3, ARG4_TYPE arg4, ARG5_TYPE arg5, va_list args) \ + { \ + if (nullptr == GLOBAL_VAR ) \ + { \ + return __real_ ## FUNC_NAME (arg1, arg2, arg3, arg4, arg5, args); \ + } \ + else \ + { \ + return GLOBAL_VAR -> FUNC_NAME(arg1, arg2, arg3, arg4, arg5); \ + } \ + } + + +#endif diff --git a/test/webfuse/utils/ws_server.cc b/test/webfuse/utils/ws_server.cc new file mode 100644 index 0000000..48e2588 --- /dev/null +++ b/test/webfuse/utils/ws_server.cc @@ -0,0 +1,266 @@ +#include "webfuse/utils/ws_server.hpp" +#include "webfuse/utils/timeout_watcher.hpp" + +#include "webfuse/core/util.h" +#include "webfuse/core/protocol_names.h" +#include +#include +#include +#include +#include + +using webfuse_test::TimeoutWatcher; + +#define DEFAULT_TIMEOUT (std::chrono::milliseconds(5 * 1000)) + +namespace +{ + +class IServer +{ +public: + virtual ~IServer() = default; + virtual void onConnectionEstablished(struct lws * wsi) = 0; + virtual void onConnectionClosed(struct lws * wsi) = 0; + virtual void onMessageReceived(struct lws * wsi, char const * data, size_t length) = 0; + virtual void onWritable(struct lws * wsi) = 0; +}; + +} + +extern "C" +{ + +static int wf_test_utils_ws_server_callback( + struct lws * wsi, + enum lws_callback_reasons reason, + void * WF_UNUSED_PARAM(user), + void * in, + size_t len) +{ + struct lws_protocols const * ws_protocol = lws_get_protocol(wsi); + if (NULL == ws_protocol) + { + return 0; + } + + auto * server = reinterpret_cast(ws_protocol->user); + switch(reason) + { + case LWS_CALLBACK_ESTABLISHED: + server->onConnectionEstablished(wsi); + break; + case LWS_CALLBACK_CLOSED: + server->onConnectionClosed(wsi); + break; + case LWS_CALLBACK_RECEIVE: + { + auto * data = reinterpret_cast(in); + server->onMessageReceived(wsi, data, len); + } + break; + case LWS_CALLBACK_SERVER_WRITEABLE: + server->onWritable(wsi); + break; + default: + break; + } + + return 0; +} + +} + +namespace webfuse_test +{ + +class WebsocketServer::Private: public IServer +{ +public: + Private(int port, struct lws_protocols * additionalProtocols, size_t additionalProtocolsCount) + : client_wsi(nullptr) + { + ws_protocols = new struct lws_protocols[2 + additionalProtocolsCount]; + memset(ws_protocols, 0, sizeof(struct lws_protocols) * (2 + additionalProtocolsCount)); + + ws_protocols[0].name = WF_PROTOCOL_NAME_ADAPTER_SERVER; + ws_protocols[0].callback = &wf_test_utils_ws_server_callback; + ws_protocols[0].per_session_data_size = 0; + ws_protocols[0].user = reinterpret_cast(this); + + if (0 < additionalProtocolsCount) + { + memcpy(&ws_protocols[additionalProtocolsCount], additionalProtocols, sizeof(struct lws_protocols) * additionalProtocolsCount); + } + + memset(&info, 0, sizeof(struct lws_context_creation_info)); + info.port = port; + info.mounts = NULL; + info.protocols =ws_protocols; + info.vhost_name = "localhost"; + info.ws_ping_pong_interval = 10; + info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; + + context = lws_create_context(&info); + + } + + virtual ~Private() + { + lws_context_destroy(context); + delete[] ws_protocols; + } + + struct lws_context * getContext() + { + return context; + } + + void waitForConnection() + { + TimeoutWatcher watcher(DEFAULT_TIMEOUT); + + while (nullptr == client_wsi) + { + watcher.check(); + lws_service(context, 100); + } + } + + void sendMessage(json_t * message) + { + char* message_text = json_dumps(message, JSON_COMPACT); + writeQueue.push(message_text); + json_decref(message); + free(message_text); + + if (nullptr != client_wsi) + { + lws_callback_on_writable(client_wsi); + + TimeoutWatcher watcher(DEFAULT_TIMEOUT); + while (!writeQueue.empty()) + { + watcher.check(); + lws_service(context, 100); + } + } + } + + json_t * receiveMessage() + { + TimeoutWatcher watcher(DEFAULT_TIMEOUT); + + while (recvQueue.empty()) + { + watcher.check(); + lws_service(context, 100); + } + + std::string const & message_text = recvQueue.front(); + json_t * message = json_loads(message_text.c_str(), JSON_DECODE_ANY, nullptr); + recvQueue.pop(); + + return message; + } + + void onConnectionEstablished(struct lws * wsi) override + { + client_wsi = wsi; + } + + void onConnectionClosed(struct lws * wsi) override + { + if (wsi == client_wsi) + { + client_wsi = nullptr; + } + } + + void onMessageReceived(struct lws * wsi, char const * data, size_t length) override + { + if (wsi == client_wsi) + { + recvQueue.push(std::string(data, length)); + } + } + + void onWritable(struct lws * wsi) override + { + if (!writeQueue.empty()) + { + std::string const & message = writeQueue.front(); + + unsigned char * data = new unsigned char[LWS_PRE + message.size()]; + memcpy(&data[LWS_PRE], message.c_str(), message.size()); + lws_write(wsi, &data[LWS_PRE], message.size(), LWS_WRITE_TEXT); + delete[] data; + + writeQueue.pop(); + if (!writeQueue.empty()) + { + lws_callback_on_writable(wsi); + } + } + } + + +private: + void send(std::string const & message) + { + if (nullptr != client_wsi) + { + writeQueue.push(message); + lws_callback_on_writable(client_wsi); + } + } + + struct lws * client_wsi; + + struct lws_protocols * ws_protocols; + struct lws_context_creation_info info; + struct lws_context * context; + std::queue writeQueue; + std::queue recvQueue; + +}; + +WebsocketServer::WebsocketServer(int port) +: d(new Private(port, nullptr, 0)) +{ + +} + +WebsocketServer::WebsocketServer(int port, struct lws_protocols * additionalProtocols, std::size_t additionalProtocolsCount) +: d(new Private(port, additionalProtocols, additionalProtocolsCount)) +{ + +} + +WebsocketServer::~WebsocketServer() +{ + delete d; +} + +struct lws_context * WebsocketServer::getContext() +{ + return d->getContext(); +} + +void WebsocketServer::waitForConnection() +{ + d->waitForConnection(); +} + +void WebsocketServer::sendMessage(json_t * message) +{ + d->sendMessage(message); +} + +json_t * WebsocketServer::receiveMessage() +{ + return d->receiveMessage(); +} + + +} \ No newline at end of file diff --git a/test/webfuse/utils/ws_server.hpp b/test/webfuse/utils/ws_server.hpp new file mode 100644 index 0000000..e9d825a --- /dev/null +++ b/test/webfuse/utils/ws_server.hpp @@ -0,0 +1,29 @@ +#ifndef WF_TEST_UTILS_WS_SERVER_HPP +#define WF_TEST_UTILS_WS_SERVER_HPP + +#include +#include + +namespace webfuse_test +{ + +class WebsocketServer +{ + WebsocketServer(WebsocketServer const &) = delete; + WebsocketServer & operator=(WebsocketServer const &) = delete; +public: + explicit WebsocketServer(int port); + WebsocketServer(int port, struct lws_protocols * additionalProtocols, std::size_t additionalProtocolsCount); + ~WebsocketServer(); + struct lws_context * getContext(); + void waitForConnection(); + void sendMessage(json_t * message); + json_t * receiveMessage(); +private: + class Private; + Private * d; +}; + +} + +#endif