diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..5ee5829 --- /dev/null +++ b/.clangd @@ -0,0 +1,10 @@ +CompileFlags: + Add: + - "-std=c++20" + - "-Wall" + - "-Wextra" + CompilationDatabase: build/Release + +Diagnostics: + ClangTidy: + Remove: bugprone-unused-return-value diff --git a/CMakeLists.txt b/CMakeLists.txt index 060cb82..a1f91d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,5 +51,17 @@ target_link_libraries(framed_pipe_client PRIVATE jsonrpc) add_executable(framed_pipe_server examples/framed_pipe_server.cpp) target_link_libraries(framed_pipe_server PRIVATE jsonrpc) +add_executable(socket_client examples/socket_client.cpp) +target_link_libraries(socket_client PRIVATE jsonrpc) + +add_executable(socket_server examples/socket_server.cpp) +target_link_libraries(socket_server PRIVATE jsonrpc) + +add_executable(framed_socket_client examples/framed_socket_client.cpp) +target_link_libraries(framed_socket_client PRIVATE jsonrpc) + +add_executable(framed_socket_server examples/framed_socket_server.cpp) +target_link_libraries(framed_socket_server PRIVATE jsonrpc) + enable_testing() add_subdirectory(tests) diff --git a/MODULE.bazel b/MODULE.bazel index 042c5eb..e0fe3f5 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,22 +1,25 @@ -"""This is the MODULE.bazel file for the jsonrpc module.""" +""" +MODULE.bazel file for the jsonrpc module +""" module( name = "jsonrpc", version = "1.0.0", ) -# Register the rules_foreign_cc and rules_cc dependencies -bazel_dep(name = "rules_foreign_cc", version = "0.11.1") +# Dependencies from the Bazel Central Registry bazel_dep(name = "rules_cc", version = "0.0.9") - -# Register the nlohmann_json dependency bazel_dep(name = "nlohmann_json", version = "3.11.3") - -# Register the spdlog dependency bazel_dep(name = "spdlog", version = "1.14.1") - -# Register the asio dependency bazel_dep(name = "asio", version = "1.28.2") - -# Register the catch2 dependency bazel_dep(name = "catch2", version = "3.6.0") + +# Dependency using traditional HTTP archive +http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +http_archive( + name = "thread_pool", + build_file = "//third_party/thread_pool:BUILD.bazel", + sha256 = "be7abecbc420bb87919eeef729b13ff7c29d5ce547bdae284923296c695415bd", + strip_prefix = "thread-pool-4.1.0", + urls = ["https://github.com/bshoshany/thread-pool/archive/refs/tags/v4.1.0.tar.gz"], +) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 02eb667..1bc5b55 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -42,8 +42,6 @@ "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", "https://bcr.bazel.build/modules/rules_cc/0.0.9/source.json": "1f1ba6fea244b616de4a554a0f4983c91a9301640c8fe0dd1d410254115c8430", - "https://bcr.bazel.build/modules/rules_foreign_cc/0.11.1/MODULE.bazel": "beeb0dd8d488d3cff57fa12ab3378051a7299aa9de2476d61c1d46f664d6398d", - "https://bcr.bazel.build/modules/rules_foreign_cc/0.11.1/source.json": "be2106be697115c10c03c6505a07bd4e259719c6608f08a61d600a560b8cf172", "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", "https://bcr.bazel.build/modules/rules_java/7.6.1/source.json": "8f3f3076554e1558e8e468b2232991c510ecbcbed9e6f8c06ac31c93bcf38362", @@ -59,8 +57,7 @@ "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/source.json": "d57902c052424dfda0e71646cb12668d39c4620ee0544294d9d941e7d12bc3a9", "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel": "26114f0c0b5e93018c0c066d6673f1a2c3737c7e90af95eff30cfee38d0bbac7", - "https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300", - "https://bcr.bazel.build/modules/rules_python/0.23.1/source.json": "a6d9965700e3bd75df4e19140c0e651851bb720d8b9eb280ecd1ee44b92d7646", + "https://bcr.bazel.build/modules/rules_python/0.22.1/source.json": "57226905e783bae7c37c2dd662be078728e48fa28ee4324a7eabcafb5a43d014", "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", "https://bcr.bazel.build/modules/spdlog/1.14.1/MODULE.bazel": "52e4b8dbfac282ce6123ecff8b374ff38e595a583ddb7d0af331eae7993faf40", "https://bcr.bazel.build/modules/spdlog/1.14.1/source.json": "f26b37601d2bfd5be755626965cbc07fcd4c0e9ab1657bf17fbb2f738693c752", @@ -120,392 +117,6 @@ "recordedRepoMappingEntries": [] } }, - "@@rules_foreign_cc~//foreign_cc:extensions.bzl%tools": { - "general": { - "bzlTransitiveDigest": "5Xt39wqg6Xufojy5gN4ke9V2Mv5ANvdeLlAL1hp6+ic=", - "usagesDigest": "WnYOMrYXMz7KcDu0mMpDP5Eue8sHuFMA1dmDT6I124Q=", - "recordedFileInputs": {}, - "recordedDirentsInputs": {}, - "envVariables": {}, - "generatedRepoSpecs": { - "cmake-3.23.2-linux-aarch64": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "urls": [ - "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-linux-aarch64.tar.gz" - ], - "sha256": "f2654bf780b53f170bbbec44d8ac67d401d24788e590faa53036a89476efa91e", - "strip_prefix": "cmake-3.23.2-linux-aarch64", - "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n" - } - }, - "rules_foreign_cc_framework_toolchain_macos": { - "bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl", - "ruleClassName": "framework_toolchain_repository", - "attributes": { - "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:macos_commands.bzl", - "exec_compatible_with": [ - "@platforms//os:macos" - ] - } - }, - "ninja_1.12.0_mac": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "urls": [ - "https://github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-mac.zip" - ], - "sha256": "19806019c9623a062c3d9fa0d5f45b633a3d150f88e73fbd6c0ff6ea5534df10", - "strip_prefix": "", - "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" - } - }, - "ninja_1.12.0_mac_aarch64": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "urls": [ - "https://github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-mac.zip" - ], - "sha256": "19806019c9623a062c3d9fa0d5f45b633a3d150f88e73fbd6c0ff6ea5534df10", - "strip_prefix": "", - "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" - } - }, - "gnumake_src": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", - "sha256": "dd16fb1d67bfab79a72f5e8390735c49e3e8e70b4945a15ab1f81ddb78658fb3", - "strip_prefix": "make-4.4.1", - "urls": [ - "https://mirror.bazel.build/ftpmirror.gnu.org/gnu/make/make-4.4.1.tar.gz", - "http://ftpmirror.gnu.org/gnu/make/make-4.4.1.tar.gz" - ] - } - }, - "gettext_runtime": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "build_file_content": "\ncc_import(\n name = \"gettext_runtime\",\n shared_library = \"bin/libintl-8.dll\",\n visibility = [\"//visibility:public\"],\n)\n ", - "sha256": "1f4269c0e021076d60a54e98da6f978a3195013f6de21674ba0edbc339c5b079", - "urls": [ - "https://mirror.bazel.build/download.gnome.org/binaries/win64/dependencies/gettext-runtime_0.18.1.1-2_win64.zip", - "https://download.gnome.org/binaries/win64/dependencies/gettext-runtime_0.18.1.1-2_win64.zip" - ] - } - }, - "cmake_src": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", - "sha256": "f316b40053466f9a416adf981efda41b160ca859e97f6a484b447ea299ff26aa", - "strip_prefix": "cmake-3.23.2", - "urls": [ - "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2.tar.gz" - ], - "patches": [ - "@@rules_foreign_cc~//toolchains:cmake-c++11.patch" - ] - } - }, - "bazel_skylib": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "urls": [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz", - "https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz" - ], - "sha256": "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728" - } - }, - "cmake-3.23.2-macos-universal": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "urls": [ - "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-macos-universal.tar.gz" - ], - "sha256": "853a0f9af148c5ef47282ffffee06c4c9f257be2635936755f39ca13c3286c88", - "strip_prefix": "cmake-3.23.2-macos-universal/CMake.app/Contents", - "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n" - } - }, - "ninja_1.12.0_win": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "urls": [ - "https://github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-win.zip" - ], - "sha256": "51d99be9ceea8835edf536d52d47fa4c316aa332e57f71a08df5bd059da11417", - "strip_prefix": "", - "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja.exe\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" - } - }, - "meson_src": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "build_file_content": "exports_files([\"meson.py\"])\n\nfilegroup(\n name = \"runtime\",\n srcs = glob([\"mesonbuild/**\"]),\n visibility = [\"//visibility:public\"],\n)\n", - "sha256": "d04b541f97ca439fb82fab7d0d480988be4bd4e62563a5ca35fadb5400727b1c", - "strip_prefix": "meson-1.1.1", - "urls": [ - "https://mirror.bazel.build/github.com/mesonbuild/meson/releases/download/1.1.1/meson-1.1.1.tar.gz", - "https://github.com/mesonbuild/meson/releases/download/1.1.1/meson-1.1.1.tar.gz" - ] - } - }, - "rules_foreign_cc_framework_toolchain_freebsd": { - "bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl", - "ruleClassName": "framework_toolchain_repository", - "attributes": { - "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:freebsd_commands.bzl", - "exec_compatible_with": [ - "@platforms//os:freebsd" - ] - } - }, - "rules_foreign_cc_framework_toolchain_linux": { - "bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl", - "ruleClassName": "framework_toolchain_repository", - "attributes": { - "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:linux_commands.bzl", - "exec_compatible_with": [ - "@platforms//os:linux" - ] - } - }, - "rules_python": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "sha256": "84aec9e21cc56fbc7f1335035a71c850d1b9b5cc6ff497306f84cced9a769841", - "strip_prefix": "rules_python-0.23.1", - "url": "https://github.com/bazelbuild/rules_python/archive/refs/tags/0.23.1.tar.gz" - } - }, - "pkgconfig_src": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", - "sha256": "6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591", - "strip_prefix": "pkg-config-0.29.2", - "patches": [ - "@@rules_foreign_cc~//toolchains:pkgconfig-detectenv.patch", - "@@rules_foreign_cc~//toolchains:pkgconfig-makefile-vc.patch", - "@@rules_foreign_cc~//toolchains:pkgconfig-builtin-glib-int-conversion.patch" - ], - "urls": [ - "https://pkgconfig.freedesktop.org/releases/pkg-config-0.29.2.tar.gz", - "https://mirror.bazel.build/pkgconfig.freedesktop.org/releases/pkg-config-0.29.2.tar.gz" - ] - } - }, - "ninja_build_src": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", - "integrity": "sha256-iyyGzUg9x/y3l1xexzKRNdIQCZqJvH2wWQoHsLv+SaU=", - "strip_prefix": "ninja-1.12.0", - "urls": [ - "https://mirror.bazel.build/github.com/ninja-build/ninja/archive/v1.12.0.tar.gz", - "https://github.com/ninja-build/ninja/archive/v1.12.0.tar.gz" - ] - } - }, - "glib_src": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "build_file_content": "\ncc_import(\n name = \"msvc_hdr\",\n hdrs = [\"msvc_recommended_pragmas.h\"],\n visibility = [\"//visibility:public\"],\n)\n ", - "sha256": "bc96f63112823b7d6c9f06572d2ad626ddac7eb452c04d762592197f6e07898e", - "strip_prefix": "glib-2.26.1", - "urls": [ - "https://mirror.bazel.build/download.gnome.org/sources/glib/2.26/glib-2.26.1.tar.gz", - "https://download.gnome.org/sources/glib/2.26/glib-2.26.1.tar.gz" - ] - } - }, - "cmake-3.23.2-windows-x86_64": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "urls": [ - "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-windows-x86_64.zip" - ], - "sha256": "2329387f3166b84c25091c86389fb891193967740c9bcf01e7f6d3306f7ffda0", - "strip_prefix": "cmake-3.23.2-windows-x86_64", - "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake.exe\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake.exe\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n" - } - }, - "glib_runtime": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "build_file_content": "\nexports_files(\n [\n \"bin/libgio-2.0-0.dll\",\n \"bin/libglib-2.0-0.dll\",\n \"bin/libgmodule-2.0-0.dll\",\n \"bin/libgobject-2.0-0.dll\",\n \"bin/libgthread-2.0-0.dll\",\n ],\n visibility = [\"//visibility:public\"],\n)\n ", - "sha256": "88d857087e86f16a9be651ee7021880b3f7ba050d34a1ed9f06113b8799cb973", - "urls": [ - "https://mirror.bazel.build/download.gnome.org/binaries/win64/glib/2.26/glib_2.26.1-1_win64.zip", - "https://download.gnome.org/binaries/win64/glib/2.26/glib_2.26.1-1_win64.zip" - ] - } - }, - "rules_foreign_cc_framework_toolchains": { - "bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl", - "ruleClassName": "framework_toolchain_repository_hub", - "attributes": {} - }, - "glib_dev": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "build_file_content": "\nload(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\ncc_import(\n name = \"glib_dev\",\n hdrs = glob([\"include/**\"]),\n shared_library = \"@glib_runtime//:bin/libglib-2.0-0.dll\",\n visibility = [\"//visibility:public\"],\n)\n ", - "sha256": "bdf18506df304d38be98a4b3f18055b8b8cca81beabecad0eece6ce95319c369", - "urls": [ - "https://mirror.bazel.build/download.gnome.org/binaries/win64/glib/2.26/glib-dev_2.26.1-1_win64.zip", - "https://download.gnome.org/binaries/win64/glib/2.26/glib-dev_2.26.1-1_win64.zip" - ] - } - }, - "cmake_3.23.2_toolchains": { - "bzlFile": "@@rules_foreign_cc~//toolchains:prebuilt_toolchains_repository.bzl", - "ruleClassName": "prebuilt_toolchains_repository", - "attributes": { - "repos": { - "cmake-3.23.2-linux-aarch64": [ - "@platforms//cpu:aarch64", - "@platforms//os:linux" - ], - "cmake-3.23.2-linux-x86_64": [ - "@platforms//cpu:x86_64", - "@platforms//os:linux" - ], - "cmake-3.23.2-macos-universal": [ - "@platforms//os:macos" - ], - "cmake-3.23.2-windows-i386": [ - "@platforms//cpu:x86_32", - "@platforms//os:windows" - ], - "cmake-3.23.2-windows-x86_64": [ - "@platforms//cpu:x86_64", - "@platforms//os:windows" - ] - }, - "tool": "cmake" - } - }, - "ninja_1.12.0_linux-aarch64": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "urls": [ - "https://github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-linux-aarch64.zip" - ], - "sha256": "375a49c79095334c88338ff15f90730e08a4d03997ef660f48f11ee7e450db7a", - "strip_prefix": "", - "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" - } - }, - "cmake-3.23.2-windows-i386": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "urls": [ - "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-windows-i386.zip" - ], - "sha256": "6a4fcd6a2315b93cb23c93507efccacc30c449c2bf98f14d6032bb226c582e07", - "strip_prefix": "cmake-3.23.2-windows-i386", - "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake.exe\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake.exe\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n" - } - }, - "ninja_1.12.0_linux": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "urls": [ - "https://github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-linux.zip" - ], - "sha256": "ddc96efa3c7c9d41de733d15e2eda07a8a212555cb43f35d727e080d2ca687ab", - "strip_prefix": "", - "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" - } - }, - "cmake-3.23.2-linux-x86_64": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "urls": [ - "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-linux-x86_64.tar.gz" - ], - "sha256": "aaced6f745b86ce853661a595bdac6c5314a60f8181b6912a0a4920acfa32708", - "strip_prefix": "cmake-3.23.2-linux-x86_64", - "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n" - } - }, - "ninja_1.12.0_toolchains": { - "bzlFile": "@@rules_foreign_cc~//toolchains:prebuilt_toolchains_repository.bzl", - "ruleClassName": "prebuilt_toolchains_repository", - "attributes": { - "repos": { - "ninja_1.12.0_linux": [ - "@platforms//cpu:x86_64", - "@platforms//os:linux" - ], - "ninja_1.12.0_linux-aarch64": [ - "@platforms//cpu:aarch64", - "@platforms//os:linux" - ], - "ninja_1.12.0_mac": [ - "@platforms//cpu:x86_64", - "@platforms//os:macos" - ], - "ninja_1.12.0_mac_aarch64": [ - "@platforms//cpu:aarch64", - "@platforms//os:macos" - ], - "ninja_1.12.0_win": [ - "@platforms//cpu:x86_64", - "@platforms//os:windows" - ] - }, - "tool": "ninja" - } - }, - "rules_foreign_cc_framework_toolchain_windows": { - "bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl", - "ruleClassName": "framework_toolchain_repository", - "attributes": { - "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:windows_commands.bzl", - "exec_compatible_with": [ - "@platforms//os:windows" - ] - } - } - }, - "recordedRepoMappingEntries": [ - [ - "rules_foreign_cc~", - "bazel_tools", - "bazel_tools" - ], - [ - "rules_foreign_cc~", - "rules_foreign_cc", - "rules_foreign_cc~" - ] - ] - } - }, "@@rules_python~//python/extensions:python.bzl%python": { "general": { "bzlTransitiveDigest": "XaaZIw4dO4l6naftU5IBdrfCE1mOmelaT/Sq9uyBnhs=", diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel index dd1b671..e69de29 100644 --- a/WORKSPACE.bazel +++ b/WORKSPACE.bazel @@ -1,9 +0,0 @@ -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - -http_archive( - name = "thread_pool", - build_file = "//third_party/thread_pool:BUILD.bazel", - sha256 = "be7abecbc420bb87919eeef729b13ff7c29d5ce547bdae284923296c695415bd", - strip_prefix = "thread-pool-4.1.0", - urls = ["https://github.com/bshoshany/thread-pool/archive/refs/tags/v4.1.0.tar.gz"], -) diff --git a/examples/BUILD.bazel b/examples/BUILD.bazel index 74cb5d9..7da458d 100644 --- a/examples/BUILD.bazel +++ b/examples/BUILD.bazel @@ -1,5 +1,6 @@ # examples/BUILD +# Standard I/O examples cc_binary( name = "stdio_client", srcs = ["stdio_client.cpp"], @@ -14,3 +15,67 @@ cc_binary( ], deps = ["//src:jsonrpc_lib"], ) + +# Unix domain socket examples +cc_binary( + name = "pipe_client", + srcs = ["pipe_client.cpp"], + deps = ["//src:jsonrpc_lib"], +) + +cc_binary( + name = "pipe_server", + srcs = [ + "calculator.hpp", + "pipe_server.cpp", + ], + deps = ["//src:jsonrpc_lib"], +) + +# Framed Unix domain socket examples +cc_binary( + name = "framed_pipe_client", + srcs = ["framed_pipe_client.cpp"], + deps = ["//src:jsonrpc_lib"], +) + +cc_binary( + name = "framed_pipe_server", + srcs = [ + "calculator.hpp", + "framed_pipe_server.cpp", + ], + deps = ["//src:jsonrpc_lib"], +) + +# Socket examples +cc_binary( + name = "socket_client", + srcs = ["socket_client.cpp"], + deps = ["//src:jsonrpc_lib"], +) + +cc_binary( + name = "socket_server", + srcs = [ + "calculator.hpp", + "socket_server.cpp", + ], + deps = ["//src:jsonrpc_lib"], +) + +# Framed socket examples +cc_binary( + name = "framed_socket_client", + srcs = ["framed_socket_client.cpp"], + deps = ["//src:jsonrpc_lib"], +) + +cc_binary( + name = "framed_socket_server", + srcs = [ + "calculator.hpp", + "framed_socket_server.cpp", + ], + deps = ["//src:jsonrpc_lib"], +) diff --git a/examples/framed_pipe_client.cpp b/examples/framed_pipe_client.cpp index ee2d1c3..9166b77 100644 --- a/examples/framed_pipe_client.cpp +++ b/examples/framed_pipe_client.cpp @@ -1,4 +1,3 @@ -#include #include #include diff --git a/examples/framed_socket_client.cpp b/examples/framed_socket_client.cpp new file mode 100644 index 0000000..d4d252a --- /dev/null +++ b/examples/framed_socket_client.cpp @@ -0,0 +1,37 @@ +#include + +#include +#include +#include +#include +#include + +#include "jsonrpc/transport/framed_socket_transport.hpp" + +using namespace jsonrpc::client; +using namespace jsonrpc::transport; +using Json = nlohmann::json; + +int main() { + auto logger = spdlog::basic_logger_mt("client", "logs/client.log", true); + spdlog::set_default_logger(logger); + spdlog::set_level(spdlog::level::debug); + spdlog::flush_on(spdlog::level::debug); + + std::string host = "127.0.0.1"; + uint16_t port = 12345; + auto transport = std::make_unique(host, port, false); + Client client(std::move(transport)); + client.Start(); + + Json addRes = client.SendMethodCall("add", Json({{"a", 10}, {"b", 5}})); + spdlog::info("Add result: {}", addRes.dump()); + + Json divRes = client.SendMethodCall("divide", Json({{"a", 10}, {"b", 0}})); + spdlog::info("Divide result: {}", divRes.dump()); + + client.SendNotification("stop"); + + client.Stop(); + return 0; +} diff --git a/examples/framed_socket_server.cpp b/examples/framed_socket_server.cpp new file mode 100644 index 0000000..e2cf663 --- /dev/null +++ b/examples/framed_socket_server.cpp @@ -0,0 +1,44 @@ +#include + +#include +#include +#include +#include +#include + +#include "jsonrpc/transport/framed_socket_transport.hpp" + +#include "calculator.hpp" + +using namespace jsonrpc::server; +using namespace jsonrpc::transport; +using Json = nlohmann::json; + +int main() { + auto logger = spdlog::basic_logger_mt("server", "logs/server.log", true); + spdlog::set_default_logger(logger); + spdlog::set_level(spdlog::level::debug); + spdlog::flush_on(spdlog::level::debug); + + std::string host = "0.0.0.0"; + uint16_t port = 12345; + auto transport = std::make_unique(host, port, true); + Server server(std::move(transport)); + Calculator calculator; + + server.RegisterMethodCall( + "add", [&calculator](const std::optional ¶ms) { + return calculator.Add(params.value()); + }); + + server.RegisterMethodCall( + "divide", [&calculator](const std::optional ¶ms) { + return calculator.Divide(params.value()); + }); + + server.RegisterNotification( + "stop", [&server](const std::optional &) { server.Stop(); }); + + server.Start(); + return 0; +} diff --git a/examples/pipe_client.cpp b/examples/pipe_client.cpp index 6453f4a..a31296d 100644 --- a/examples/pipe_client.cpp +++ b/examples/pipe_client.cpp @@ -1,4 +1,3 @@ -#include #include #include diff --git a/examples/socket_client.cpp b/examples/socket_client.cpp new file mode 100644 index 0000000..87e8c4f --- /dev/null +++ b/examples/socket_client.cpp @@ -0,0 +1,35 @@ +#include + +#include +#include +#include +#include +#include + +using namespace jsonrpc::client; +using namespace jsonrpc::transport; +using Json = nlohmann::json; + +int main() { + auto logger = spdlog::basic_logger_mt("client", "logs/client.log", true); + spdlog::set_default_logger(logger); + spdlog::set_level(spdlog::level::debug); + spdlog::flush_on(spdlog::level::debug); + + std::string host = "127.0.0.1"; + uint16_t port = 12345; + auto transport = std::make_unique(host, port, false); + Client client(std::move(transport)); + client.Start(); + + Json addRes = client.SendMethodCall("add", Json({{"a", 10}, {"b", 5}})); + spdlog::info("Add result: {}", addRes.dump()); + + Json divRes = client.SendMethodCall("divide", Json({{"a", 10}, {"b", 0}})); + spdlog::info("Divide result: {}", divRes.dump()); + + client.SendNotification("stop"); + + client.Stop(); + return 0; +} diff --git a/examples/socket_server.cpp b/examples/socket_server.cpp new file mode 100644 index 0000000..2bbf3b8 --- /dev/null +++ b/examples/socket_server.cpp @@ -0,0 +1,43 @@ +#include + +#include +#include +#include +#include +#include + +#include "calculator.hpp" + +using namespace jsonrpc::server; +using namespace jsonrpc::transport; +using Json = nlohmann::json; + +int main() { + auto logger = spdlog::basic_logger_mt("server", "logs/server.log", true); + spdlog::set_default_logger(logger); + spdlog::set_level(spdlog::level::debug); + spdlog::flush_on(spdlog::level::debug); + + std::string host = "0.0.0.0"; + uint16_t port = 12345; + + auto transport = std::make_unique(host, port, true); + Server server(std::move(transport)); + Calculator calculator; + + server.RegisterMethodCall( + "add", [&calculator](const std::optional ¶ms) { + return calculator.Add(params.value()); + }); + + server.RegisterMethodCall( + "divide", [&calculator](const std::optional ¶ms) { + return calculator.Divide(params.value()); + }); + + server.RegisterNotification( + "stop", [&server](const std::optional &) { server.Stop(); }); + + server.Start(); + return 0; +} diff --git a/examples/stdio_client.cpp b/examples/stdio_client.cpp index 568a76b..73b8e7b 100644 --- a/examples/stdio_client.cpp +++ b/examples/stdio_client.cpp @@ -1,4 +1,3 @@ -#include #include #include diff --git a/include/jsonrpc/client/client.hpp b/include/jsonrpc/client/client.hpp index 0b3f870..4ee7d3a 100644 --- a/include/jsonrpc/client/client.hpp +++ b/include/jsonrpc/client/client.hpp @@ -9,7 +9,6 @@ #include #include #include -#include #include diff --git a/include/jsonrpc/client/request.hpp b/include/jsonrpc/client/request.hpp index 0ecdd56..9fd5676 100644 --- a/include/jsonrpc/client/request.hpp +++ b/include/jsonrpc/client/request.hpp @@ -3,7 +3,6 @@ #include #include #include -#include #include diff --git a/include/jsonrpc/transport/framed_pipe_transport.hpp b/include/jsonrpc/transport/framed_pipe_transport.hpp index 8ae6310..d1a7254 100644 --- a/include/jsonrpc/transport/framed_pipe_transport.hpp +++ b/include/jsonrpc/transport/framed_pipe_transport.hpp @@ -6,7 +6,6 @@ #include "jsonrpc/transport/framed_transport.hpp" #include "jsonrpc/transport/pipe_transport.hpp" -#include "jsonrpc/transport/transport.hpp" namespace jsonrpc { namespace transport { diff --git a/include/jsonrpc/transport/framed_socket_transport.hpp b/include/jsonrpc/transport/framed_socket_transport.hpp new file mode 100644 index 0000000..3b5395c --- /dev/null +++ b/include/jsonrpc/transport/framed_socket_transport.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include + +#include "jsonrpc/transport/framed_transport.hpp" +#include "jsonrpc/transport/socket_transport.hpp" + +namespace jsonrpc::transport { + +/** + * @brief Transport layer using Asio sockets for JSON-RPC + * communication with framing. + */ +class FramedSocketTransport : + public SocketTransport, + protected FramedTransport { +public: + FramedSocketTransport(const std::string &host, uint16_t port, bool isServer); + + void SendMessage(const std::string &message) override; + std::string ReceiveMessage() override; +}; + +} // namespace jsonrpc::transport diff --git a/include/jsonrpc/transport/socket_transport.hpp b/include/jsonrpc/transport/socket_transport.hpp new file mode 100644 index 0000000..2b14826 --- /dev/null +++ b/include/jsonrpc/transport/socket_transport.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +#include "jsonrpc/transport/transport.hpp" + +namespace jsonrpc { +namespace transport { + +/** + * @brief Transport implementation using TCP/IP sockets. + * + * This class provides transport functionality over TCP/IP sockets, + * supporting both client and server modes for communication over a network. + */ +class SocketTransport : public Transport { +public: + /** + * @brief Constructs a SocketTransport. + * @param host The host address (IP or domain name). + * @param port The port number. + * @param isServer True if the transport acts as a server; false if it acts as + * a client. + */ + SocketTransport(const std::string &host, uint16_t port, bool isServer); + + ~SocketTransport(); + + void SendMessage(const std::string &message) override; + std::string ReceiveMessage() override; + +protected: + asio::ip::tcp::socket &GetSocket(); + +private: + void Connect(); + void BindAndListen(); + + asio::io_context ioContext_; + asio::ip::tcp::socket socket_; + std::string host_; + uint16_t port_; + bool isServer_; +}; + +} // namespace transport +} // namespace jsonrpc diff --git a/include/jsonrpc/utils/string_utils.hpp b/include/jsonrpc/utils/string_utils.hpp index ed64f60..5c77d09 100644 --- a/include/jsonrpc/utils/string_utils.hpp +++ b/include/jsonrpc/utils/string_utils.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include diff --git a/src/transport/framed_socket_transport.cpp b/src/transport/framed_socket_transport.cpp new file mode 100644 index 0000000..dd0af7d --- /dev/null +++ b/src/transport/framed_socket_transport.cpp @@ -0,0 +1,75 @@ +#include "jsonrpc/transport/framed_socket_transport.hpp" + +#include +#include + +#include +#include + +namespace jsonrpc { +namespace transport { + +FramedSocketTransport::FramedSocketTransport( + const std::string &host, uint16_t port, bool isServer) + : SocketTransport(host, port, isServer), FramedTransport() { + spdlog::info("FramedSocketTransport initialized with host: {} and port: {}", + host, port); +} + +void FramedSocketTransport::SendMessage(const std::string &message) { + try { + asio::streambuf messageBuf; + std::ostream messageStream(&messageBuf); + FrameMessage(messageStream, message); + + asio::error_code ec; + std::size_t bytesWritten = asio::write(GetSocket(), messageBuf.data(), ec); + + if (ec) { + throw std::runtime_error("Error sending message: " + ec.message()); + } + + spdlog::info( + "FramedSocketTransport sent message with {} bytes", bytesWritten); + } catch (const std::exception &e) { + spdlog::error("FramedSocketTransport failed to send message: {}", e.what()); + throw; + } +} + +std::string FramedSocketTransport::ReceiveMessage() { + asio::streambuf buffer; + asio::error_code ec; + + // Read headers until \r\n\r\n delimiter + asio::read_until(GetSocket(), buffer, HEADER_DELIMITER, ec); + if (ec) { + throw std::runtime_error("Failed to read message headers: " + ec.message()); + } + + std::istream headerStream(&buffer); + + // Extract content length from the headers + int contentLength = ReadContentLengthFromStream(headerStream); + + // Calculate how much more content we need to read + std::size_t remainingContentLength = contentLength - buffer.size(); + + // Read any remaining content directly into the buffer + if (remainingContentLength > 0) { + asio::read(GetSocket(), buffer.prepare(remainingContentLength), ec); + if (ec && ec != asio::error::eof) { + throw std::runtime_error( + "Failed to read message content: " + ec.message()); + } + buffer.commit(remainingContentLength); + } + + // Convert the entire buffer to a string + std::string content( + asio::buffers_begin(buffer.data()), asio::buffers_end(buffer.data())); + return content; +} + +} // namespace transport +} // namespace jsonrpc diff --git a/src/transport/framed_stdio_transport.cpp b/src/transport/framed_stdio_transport.cpp index 36e2d20..ee60fdc 100644 --- a/src/transport/framed_stdio_transport.cpp +++ b/src/transport/framed_stdio_transport.cpp @@ -1,9 +1,6 @@ #include "jsonrpc/transport/framed_stdio_transport.hpp" #include -#include -#include -#include #include diff --git a/src/transport/framed_transport.cpp b/src/transport/framed_transport.cpp index 9c2802f..872559d 100644 --- a/src/transport/framed_transport.cpp +++ b/src/transport/framed_transport.cpp @@ -1,6 +1,5 @@ #include "jsonrpc/transport/framed_transport.hpp" -#include #include #include "jsonrpc/utils/string_utils.hpp" diff --git a/src/transport/pipe_transport.cpp b/src/transport/pipe_transport.cpp index 74b82ee..46da912 100644 --- a/src/transport/pipe_transport.cpp +++ b/src/transport/pipe_transport.cpp @@ -89,7 +89,7 @@ std::string PipeTransport::ReceiveMessage() { } catch (const std::exception &e) { spdlog::error("Error receiving message: {}", e.what()); socket_.close(); - return ""; + throw std::runtime_error("Error receiving message"); } } diff --git a/src/transport/socket_transport.cpp b/src/transport/socket_transport.cpp new file mode 100644 index 0000000..5df876a --- /dev/null +++ b/src/transport/socket_transport.cpp @@ -0,0 +1,112 @@ +#include "jsonrpc/transport/socket_transport.hpp" + +#include + +#include + +namespace jsonrpc { +namespace transport { + +SocketTransport::SocketTransport( + const std::string &host, uint16_t port, bool isServer) + : socket_(ioContext_), host_(host), port_(port), isServer_(isServer) { + spdlog::info( + "Initializing SocketTransport with host: {} and port: {}", host, port); + + if (isServer_) { + BindAndListen(); + } else { + Connect(); + } +} + +asio::ip::tcp::socket &SocketTransport::GetSocket() { + return socket_; +} + +SocketTransport::~SocketTransport() { + spdlog::info("Closing socket and shutting down SocketTransport."); + std::error_code ec; + socket_.close(ec); + if (ec) { + spdlog::warn("Socket close error: {}", ec.message()); + } + ioContext_.stop(); +} + +void SocketTransport::Connect() { + asio::ip::tcp::resolver resolver(ioContext_); + auto endpoints = resolver.resolve(host_, std::to_string(port_)); + + asio::steady_timer timer(ioContext_); + timer.expires_after(std::chrono::seconds(3)); + + std::error_code connectError; + asio::async_connect(socket_, endpoints, + [&](const asio::error_code &error, const asio::ip::tcp::endpoint &) { + if (!error) { + timer.cancel(); + } else { + connectError = error; + } + }); + + timer.async_wait([&](const asio::error_code &error) { + if (!error) { + connectError = asio::error::timed_out; + socket_.close(); + } + }); + + ioContext_.run(); + + if (connectError) { + spdlog::error("Error connecting to {}:{}. Error: {}", host_, port_, + connectError.message()); + throw std::runtime_error("Error connecting to socket"); + } +} + +void SocketTransport::BindAndListen() { + try { + asio::ip::tcp::acceptor acceptor( + ioContext_, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port_)); + acceptor.listen(); + spdlog::info("Listening on {}:{}", host_, port_); + acceptor.accept(socket_); + spdlog::info("Accepted connection on {}:{}", host_, port_); + } catch (const std::exception &e) { + spdlog::error( + "Error binding/listening on {}:{}. Error: {}", host_, port_, e.what()); + throw std::runtime_error("Error binding/listening on socket"); + } +} + +void SocketTransport::SendMessage(const std::string &message) { + try { + asio::write(socket_, asio::buffer(message + "\n")); + spdlog::debug("Sent message: {}", message); + } catch (const std::exception &e) { + spdlog::error("Error sending message: {}", e.what()); + throw std::runtime_error("Error sending message"); + } +} + +std::string SocketTransport::ReceiveMessage() { + try { + asio::streambuf buffer; + asio::read_until(socket_, buffer, '\n'); + std::istream is(&buffer); + std::string message; + std::getline(is, message); + spdlog::debug("Received message: {}", message); + return message; + } catch (const std::exception &e) { + spdlog::error("Error receiving message: {}", e.what()); + socket_.close(); + return ""; + } +} + +} // namespace transport +} // namespace jsonrpc diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel index 283f218..37394b4 100644 --- a/tests/BUILD.bazel +++ b/tests/BUILD.bazel @@ -91,3 +91,13 @@ cc_test( "@catch2//:catch2_main", ], ) + +cc_test( + name = "test_socket_transport", + size = "small", + srcs = ["transports/test_socket_transport.cpp"], + deps = [ + "//src:jsonrpc_lib", + "@catch2//:catch2_main", + ], +) diff --git a/tests/server/test_dispatcher.cpp b/tests/server/test_dispatcher.cpp index e6a8370..af78133 100644 --- a/tests/server/test_dispatcher.cpp +++ b/tests/server/test_dispatcher.cpp @@ -1,12 +1,9 @@ -#include #include #include #include #include "jsonrpc/server/dispatcher.hpp" -#include "jsonrpc/server/request.hpp" -#include "jsonrpc/server/response.hpp" using namespace jsonrpc::server; diff --git a/tests/server/test_server.cpp b/tests/server/test_server.cpp index 764c1cf..9ee6fa0 100644 --- a/tests/server/test_server.cpp +++ b/tests/server/test_server.cpp @@ -1,11 +1,9 @@ #include -#include #include #include #include "jsonrpc/server/server.hpp" -#include "jsonrpc/transport/stdio_transport.hpp" #include "../common/mock_transport.hpp" diff --git a/tests/transports/test_framed_transport.cpp b/tests/transports/test_framed_transport.cpp index d7fe86c..3d41446 100644 --- a/tests/transports/test_framed_transport.cpp +++ b/tests/transports/test_framed_transport.cpp @@ -1,6 +1,4 @@ -#include #include -#include #include #include diff --git a/tests/transports/test_socket_transport.cpp b/tests/transports/test_socket_transport.cpp new file mode 100644 index 0000000..410bb76 --- /dev/null +++ b/tests/transports/test_socket_transport.cpp @@ -0,0 +1,65 @@ +#include + +#include +#include + +#include "jsonrpc/transport/socket_transport.hpp" + +TEST_CASE("SocketTransport starts server and client communication", + "[SocketTransport]") { + std::string host = "127.0.0.1"; + uint16_t port = 12345; + + // Start the server in a separate thread + std::thread serverThread([&]() { + jsonrpc::transport::SocketTransport serverTransport(host, port, true); + + // Wait for a message from the client + std::string receivedMessage = serverTransport.ReceiveMessage(); + REQUIRE(receivedMessage == "Hello, Server!"); + + // Send a response back to the client + serverTransport.SendMessage("Hello, Client!"); + }); + + // Give the server some time to start + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + // Start the client and connect to the server + jsonrpc::transport::SocketTransport clientTransport(host, port, false); + + // Send a message to the server + clientTransport.SendMessage("Hello, Server!"); + + // Wait for a response from the server + std::string response = clientTransport.ReceiveMessage(); + REQUIRE(response == "Hello, Client!"); + + serverThread.join(); +} + +TEST_CASE( + "SocketTransport handles empty message correctly", "[SocketTransport]") { + std::string host = "127.0.0.1"; + uint16_t port = 12346; + + // Start the server in a separate thread + std::thread serverThread([&]() { + jsonrpc::transport::SocketTransport serverTransport(host, port, true); + + // Send an empty message to the client + serverTransport.SendMessage(""); + }); + + // Give the server some time to start + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + // Start the client and connect to the server + jsonrpc::transport::SocketTransport clientTransport(host, port, false); + + // Wait for the empty response from the server + std::string response = clientTransport.ReceiveMessage(); + REQUIRE(response.empty()); + + serverThread.join(); +}