{"id":13836375,"url":"https://github.com/tdv/nanorpc","last_synced_at":"2025-07-10T15:32:43.629Z","repository":{"id":152522869,"uuid":"133567867","full_name":"tdv/nanorpc","owner":"tdv","description":"nanorpc - lightweight RPC in pure C++ 17","archived":false,"fork":false,"pushed_at":"2023-01-28T18:37:38.000Z","size":76,"stargazers_count":232,"open_issues_count":1,"forks_count":26,"subscribers_count":16,"default_branch":"master","last_synced_at":"2024-02-17T07:33:51.203Z","etag":null,"topics":["cpp","cpp17","rpc","rpc-http","rpc-library"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tdv.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-05-15T20:10:14.000Z","updated_at":"2024-01-19T19:35:46.000Z","dependencies_parsed_at":"2024-01-13T16:52:02.751Z","dependency_job_id":"84c3c79f-6113-4ac1-802d-98a7f90bf78b","html_url":"https://github.com/tdv/nanorpc","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tdv%2Fnanorpc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tdv%2Fnanorpc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tdv%2Fnanorpc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tdv%2Fnanorpc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tdv","download_url":"https://codeload.github.com/tdv/nanorpc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225647340,"owners_count":17502046,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","cpp17","rpc","rpc-http","rpc-library"],"created_at":"2024-08-04T15:00:43.384Z","updated_at":"2024-11-20T23:31:01.316Z","avatar_url":"https://github.com/tdv.png","language":"C++","readme":"# nanorpc - lightweight RPC in pure C++ 17\nNano RPC is a lightweight RPC in C++ 17 with support for user-defined data structures, \nwithout code generation and without macros, only pure C++ with HTTP/HTTPS transport.   \n\n# Version\n1.1.1  \n\n# Features\n- base for client-server applications\n- simple reflection for users structures in pure C++\n- support for nested structures\n- NO macros\n- NO code generation\n- you can use types from STL, such as vector, list, set, map, string, etc. and similar types from the boost library\n- customization for serialization and transport and easy interface for beginners\n- the build in the pure mode for usage with your own transport (without boost)  \n- HTTP/HTTPS transport based on boost.asio and boost.beast  \n\n**NOTE**  \nCurrently, C++ reflection is not supported out of the box, \nso this library has some restrictions in using types for function parameters and return values.  \n\n## Restrictions\n- user-defined data structures should not have a user-defined constructor\n- no inheritance\n- you can't use arbitrary types from STL and boost\n- you can't use raw pointers and non-const references  \n\n# Compiler\nThe minimum compiler version required is gcc 7.3 (other compilers were not tested)  \n\n# OS\nLinux (Tested on Ubuntu 16.04 and Ubuntu 18.04)\n\n**NOTE**  \nThe code is cross-platform. Perhaps you will be able to compile under another OS with another compiler, \nwith your own modifications for a build script.  \n\n# Dependencies\n- Boost only  \n\n# Build and install\n\n## Clone and build with installed Boost  \n```bash\ngit clone https://github.com/tdv/nanorpc.git  \ncd nanorpc\nmkdir build  \ncd build  \ncmake ..  \nmake  \nmake install  \n```\nYou can try using CMAKE_INSTALL_PREFIX to select the installation directory  \n\n## Clone and build without installed Boost  \n```bash\ngit clone https://github.com/tdv/nanorpc.git  \ncd nanorpc\n./build_with_boost.sh\n```\n\n**NOTE**  \nNanoRPC has two build options  \n- with SSL  \n- pure core only  \n\nUse cmake -D with NANORPC_WITH_SSL or NANORPC_PURE_CORE. You can't enable both options at the same time.  \nThe 'pure core' build you can use with your own transport.  \n\n## Build examples\n### Build examples with installed boost and nanorpc\n```bash\ncd examples/{example_project}\nmkdir build  \ncd build  \ncmake ..  \nmake  \n```\n### Build examples without installed boost and nanorpc\n```bash\ncd examples/{example_project}\nmkdir build  \ncd build  \ncmake -DBOOST_ROOT=$PWD/../../../third_party/boost -Dnanorpc_DIR=$PWD/../../../target/nanorpc ..\nmake  \n```\n\n# Examples\n\n## Hello World\n[Source code](https://github.com/tdv/nanorpc/tree/master/examples/hello_world)  \n**Description**  \nThe \"Hello World\" example demonstrates a basic client-server application with RPC and HTTP communication.  \n\n**Server application**  \n```cpp\n// STD\n#include \u003ccstdlib\u003e\n#include \u003ciostream\u003e\n\n// NANORPC\n#include \u003cnanorpc/http/easy.h\u003e\n\nint main()\n{\n    try\n    {\n        auto server = nanorpc::http::easy::make_server(\"0.0.0.0\", \"55555\", 8, \"/api/\",\n                std::pair{\"test\", [] (std::string const \u0026s) { return \"Tested: \" + s; } }\n            );\n\n        std::cout \u003c\u003c \"Press Enter for quit.\" \u003c\u003c std::endl;\n\n        std::cin.get();\n    }\n    catch (std::exception const \u0026e)\n    {\n        std::cerr \u003c\u003c \"Error: \" \u003c\u003c nanorpc::core::exception::to_string(e) \u003c\u003c std::endl;\n        return EXIT_FAILURE;\n    }\n\n    return EXIT_SUCCESS;\n}\n```\n**Client application**  \n```cpp\n// STD\n#include \u003ccstdlib\u003e\n#include \u003ciostream\u003e\n\n// NANORPC\n#include \u003cnanorpc/http/easy.h\u003e\n\nint main()\n{\n    try\n    {\n        auto client = nanorpc::http::easy::make_client(\"localhost\", \"55555\", 8, \"/api/\");\n\n        std::string result = client.call(\"test\", std::string{\"test\"});\n        std::cout \u003c\u003c \"Response from server: \" \u003c\u003c result \u003c\u003c std::endl;\n    }\n    catch (std::exception const \u0026e)\n    {\n        std::cerr \u003c\u003c \"Error: \" \u003c\u003c nanorpc::core::exception::to_string(e) \u003c\u003c std::endl;\n        return EXIT_FAILURE;\n    }\n\n    return EXIT_SUCCESS;\n}\n\n```\n\n## Complex Type\n[Source code](https://github.com/tdv/nanorpc/tree/master/examples/complex_type)  \n**Description**  \nThis example is the same as \"Hello World\". \nThe difference is in calling remote methods with user-defined data structures as parameters and returning a value. \nThe project structure is the same as in the previous example, \nonly the definitions of the user-defined data structures were added.  \n\n**Common data**  \n```cpp\n// STD\n#include \u003ccstdint\u003e\n#include \u003cmap\u003e\n#include \u003cstring\u003e\n#include \u003cvector\u003e\n\nnamespace data\n{\n\nenum class occupation_type\n{\n    unknown,\n    developer,\n    manager\n};\n\nstruct task\n{\n    std::string name;\n    std::string description;\n};\n\nusing tasks = std::vector\u003ctask\u003e;\n\nstruct employee\n{\n    std::string name;\n    std::string last_name;\n    std::uint16_t age;\n    std::string company;\n    occupation_type occupation;\n    tasks job;\n};\n\nusing employees = std::map\u003cstd::string, employee\u003e;\n\n}\n```\n\n**Server application**  \n```cpp\n// STD\n#include \u003ccstdlib\u003e\n#include \u003ciostream\u003e\n#include \u003cmutex\u003e\n\n// NANORPC\n#include \u003cnanorpc/http/easy.h\u003e\n\n// THIS\n#include \"common/data.h\"\n\nint main()\n{\n    try\n    {\n        std::mutex mutex;\n        data::employees employees;\n\n        auto server = nanorpc::http::easy::make_server(\"0.0.0.0\", \"55555\", 8, \"/api/\",\n                std::pair{\"create\", [\u0026]\n                    (std::string const \u0026id, data::employee const \u0026employee)\n                    {\n                        std::lock_guard loxk{mutex};\n                        if (employees.find(id) != std::end(employees))\n                            throw std::invalid_argument{\"Employee with id \\\"\" + id + \"\\\" already exists.\"};\n                        employees.emplace(id, employee);\n                        return id;\n                    } },\n                std::pair{\"read\", [\u0026]\n                    (std::string const \u0026id)\n                    {\n                        std::lock_guard loxk{mutex};\n                        auto const iter = employees.find(id);\n                        if (iter == std::end(employees))\n                            throw std::invalid_argument{\"Employee with id \\\"\" + id + \"\\\" not found.\"};\n                        return iter-\u003esecond;\n                    } },\n                std::pair{\"update\", [\u0026]\n                    (std::string const \u0026id, data::employee const \u0026employee)\n                    {\n                        std::lock_guard loxk{mutex};\n                        auto iter = employees.find(id);\n                        if (iter == std::end(employees))\n                            throw std::invalid_argument{\"Employee with id \\\"\" + id + \"\\\" not found.\"};\n                        iter-\u003esecond = employee;\n                    } },\n                std::pair{\"delete\", [\u0026]\n                    (std::string const \u0026id)\n                    {\n                        std::lock_guard loxk{mutex};\n                        auto iter = employees.find(id);\n                        if (iter == std::end(employees))\n                            throw std::invalid_argument{\"Employee with id \\\"\" + id + \"\\\" not found.\"};\n                        employees.erase(iter);\n                    } }\n            );\n\n        std::cout \u003c\u003c \"Press Enter for quit.\" \u003c\u003c std::endl;\n\n        std::cin.get();\n    }\n    catch (std::exception const \u0026e)\n    {\n        std::cerr \u003c\u003c \"Error: \" \u003c\u003c nanorpc::core::exception::to_string(e) \u003c\u003c std::endl;\n        return EXIT_FAILURE;\n    }\n\n    return EXIT_SUCCESS;\n}\n\n```\n\n**Client application**  \n```cpp\n// STD\n#include \u003ccstdlib\u003e\n#include \u003ciostream\u003e\n\n// NANORPC\n#include \u003cnanorpc/http/easy.h\u003e\n\n// THIS\n#include \"common/data.h\"\n\nint main()\n{\n    try\n    {\n        auto client = nanorpc::http::easy::make_client(\"localhost\", \"55555\", 8, \"/api/\");\n\n        std::string employee_id = \"employee_1\";\n\n        {\n            data::employee employee;\n\n            employee.name = \"John\";\n            employee.last_name = \"Brown\";\n            employee.age = 33;\n            employee.company = \"Google\";    // Johns dreams\n            employee.occupation = data::occupation_type::developer;\n            employee.job.push_back({\"Task 1\", \"Do something.\"});\n            employee.job.push_back({\"Task 2\", \"Do something more.\"});\n\n            employee_id = client.call(\"create\", employee_id, employee).as\u003cstd::string\u003e();\n            std::cout \u003c\u003c \"added employee with id \\\"\" \u003c\u003c employee_id \u003c\u003c \"\\\".\" \u003c\u003c std::endl;\n        }\n\n        auto show_employee_info = [] (data::employee const \u0026employee)\n            {\n                std::cout \u003c\u003c \"name: \" \u003c\u003c employee.name \u003c\u003c std::endl;\n                std::cout \u003c\u003c \"last_name: \" \u003c\u003c employee.last_name \u003c\u003c std::endl;\n                std::cout \u003c\u003c \"age: \" \u003c\u003c employee.age \u003c\u003c std::endl;\n                std::cout \u003c\u003c \"company: \" \u003c\u003c employee.company \u003c\u003c std::endl;\n                std::cout \u003c\u003c \"occupation: \"\n                          \u003c\u003c (employee.occupation == data::occupation_type::developer ? \"developer\" : \"manager\")\n                          \u003c\u003c std::endl;\n                for (auto const \u0026task : employee.job)\n                {\n                    std::cout \u003c\u003c \"\\ttask name: \" \u003c\u003c task.name \u003c\u003c std::endl;\n                    std::cout \u003c\u003c \"\\ttask description: \" \u003c\u003c task.description \u003c\u003c std::endl;\n                }\n            };\n\n        data::employee employee = client.call(\"read\", employee_id);\n\n        std::cout \u003c\u003c \"about employee with id \\\"\" \u003c\u003c employee_id \u003c\u003c \"\\\"\" \u003c\u003c std::endl;\n        show_employee_info(employee);\n\n        employee.occupation = data::occupation_type::manager;\n\n        client.call(\"update\", employee_id, employee);\n        std::cout \u003c\u003c \"the employee has been promoted ...\" \u003c\u003c std::endl;\n\n        employee = client.call(\"read\", employee_id).as\u003cdata::employee\u003e();\n\n        std::cout \u003c\u003c \"new info about employee with id \\\"\" \u003c\u003c employee_id \u003c\u003c \"\\\"\" \u003c\u003c std::endl;\n        show_employee_info(employee);\n\n        client.call(\"delete\", employee_id);\n        std::cout \u003c\u003c \"the employee has been fired ...\" \u003c\u003c std::endl;\n\n        std::cout \u003c\u003c \"you can't fire an employee twice\" \u003c\u003c std::endl;\n        client.call(\"delete\", employee_id);\n    }\n    catch (std::exception const \u0026e)\n    {\n        std::cerr \u003c\u003c \"Error: \" \u003c\u003c nanorpc::core::exception::to_string(e) \u003c\u003c std::endl;\n        return EXIT_FAILURE;\n    }\n\n    return EXIT_SUCCESS;\n}\n\n```\n\n## Pure Core\n[Source code](https://github.com/tdv/nanorpc/tree/master/examples/pure_core)  \n**Description**  \nThe \"Pure Core\" example demonstrates a basic client-server application with RPC and in-memory (in one process) communication. \nIn this example 'executor' is a transport stub and you can rewrite it with your own transport implementation.  \n\n**Application**  \n```cpp\n// STD\n#include \u003ccstdlib\u003e\n#include \u003ciostream\u003e\n\n// NANORPC\n#include \u003cnanorpc/core/client.h\u003e\n#include \u003cnanorpc/core/exception.h\u003e\n#include \u003cnanorpc/core/server.h\u003e\n#include \u003cnanorpc/packer/plain_text.h\u003e\n\nint main()\n{\n    try\n    {\n        nanorpc::core::server\u003cnanorpc::packer::plain_text\u003e server;\n        server.handle(\"test\", [] (std::string const \u0026s)\n            {\n                std::cout \u003c\u003c \"Server. Method \\\"test\\\". Input: \" \u003c\u003c s \u003c\u003c std::endl;\n                return \"echo \\\"\" + s + \"\\\"\";\n            } );\n\n        auto executor = [srv = std::move(server)]\n            (nanorpc::core::type::buffer request) mutable\n            {\n                std::cout \u003c\u003c \"Dump. Request: '\"\n                          \u003c\u003c std::string{begin(request), end(request)}\n                          \u003c\u003c \"'\" \u003c\u003c std::endl;\n\n                auto response = srv.execute(std::move(request));\n\n                std::cout \u003c\u003c \"Dump. Response: '\"\n                          \u003c\u003c std::string{begin(response), end(response)}\n                          \u003c\u003c \"'\" \u003c\u003c std::endl;\n\n                return response;\n            };\n\n        nanorpc::core::client\u003cnanorpc::packer::plain_text\u003e client{std::move(executor)};\n\n        std::string response = client.call(\"test\", \"hello world !!!\");\n        std::cout \u003c\u003c \"Client. Method \\\"test\\\" Output: \" \u003c\u003c response \u003c\u003c std::endl;\n    }\n    catch (std::exception const \u0026e)\n    {\n        std::cerr \u003c\u003c \"Error: \" \u003c\u003c nanorpc::core::exception::to_string(e) \u003c\u003c std::endl;\n        return EXIT_FAILURE;\n    }\n\n    return EXIT_SUCCESS;\n}\n\n```\n\n## SSL Hello World\n[Source code](https://github.com/tdv/nanorpc/tree/master/examples/ssl_hello_world)  \n**Description**  \nThe \"SSL Hello World\" example demonstrates a basic client-server application with RPC and HTTPS communication. \nThe example is similar to 'Hello World' example with HTTPS transport. \nThe example must be executed with certificate files. For test you can generate your own certificates  \n```bash\ncd examples/ssl_hello_world/bin\nopenssl dhparam -out dh.pem 2048\nopenssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 10000 -out cert.pem \\\n    -subj \"//C=US\\ST=CA\\L=Los Angeles\\O=Beast\\CN=www.example.com\"\n```\nIn this example you should run the client and server applications from the folder with certificates.  \n\n# Benchmark with ab utility\n- create a file with the request data dump (request.txt)  \n- run the hello_world_server sample\n- run ab utility with request.txt  \n\n**Create dump (request.txt)**  \n```bash\necho '1 1 15118982290295364091 \"test\"  ' \u003erequest.txt\n```\n\n**Run ab utility**  \n```bash\nab -c 1000 -n 1000000 -r -k -p request.txt \"http://localhost:55555/api/\"\n```\n\n**Results**  \n```bash\nServer Software:        NanoRPC\nServer Hostname:        localhost\nServer Port:            55555\n\nDocument Path:          /api/\nDocument Length:        21 bytes\n\nConcurrency Level:      1000\nTime taken for tests:   29.444 seconds\nComplete requests:      1000000\nFailed requests:        0\nKeep-Alive requests:    1000000\nTotal transferred:      134000000 bytes\nTotal body sent:        192000000\nHTML transferred:       21000000 bytes\nRequests per second:    33962.98 [#/sec] (mean)\nTime per request:       29.444 [ms] (mean)\nTime per request:       0.029 [ms] (mean, across all concurrent requests)\nTransfer rate:          4444.37 [Kbytes/sec] received\n                        6368.06 kb/s sent\n                        10812.43 kb/s total\n\nConnection Times (ms)\n              min  mean[+/-sd] median   max\nConnect:        0    0   0.9      0      44\nProcessing:     0   29  15.0     28     227\nWaiting:        0   29  15.0     28     227\nTotal:          0   29  15.0     28     240\n\nPercentage of the requests served within a certain time (ms)\n  50%     28\n  66%     33\n  75%     36\n  80%     39\n  90%     48\n  95%     57\n  98%     67\n  99%     75\n 100%    240 (longest request)\n```\n\n\nTake a look on the following line from the above results  \n```bash\nRequests per second:    33962.98 [#/sec] (mean)\n```\nI think this is a good result of using nanorpc with a simple HTTP server based on boost.  \n","funding_links":[],"categories":["C++"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftdv%2Fnanorpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftdv%2Fnanorpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftdv%2Fnanorpc/lists"}