├── .github └── dependabot.yml ├── .gitignore ├── .gitmodules ├── .travis.yml ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── codecov.yml ├── examples ├── Makefile ├── chat │ ├── Makefile │ ├── README.rst │ ├── chat.proto │ ├── client │ │ ├── Makefile │ │ ├── async │ │ │ ├── Makefile │ │ │ ├── client.c │ │ │ ├── client.h │ │ │ └── main.c │ │ ├── linux │ │ │ ├── Makefile │ │ │ └── main.c │ │ └── python │ │ │ ├── Makefile │ │ │ └── main.py │ └── server │ │ ├── Makefile │ │ ├── async │ │ ├── Makefile │ │ ├── main.c │ │ ├── server.c │ │ └── server.h │ │ └── linux │ │ ├── Makefile │ │ └── main.c └── my_protocol │ ├── Makefile │ ├── README.rst │ ├── client │ ├── Makefile │ ├── async │ │ ├── Makefile │ │ ├── client.c │ │ ├── client.h │ │ └── main.c │ ├── linux │ │ ├── Makefile │ │ └── main.c │ └── python │ │ ├── Makefile │ │ └── main.py │ ├── my_protocol.proto │ └── server │ ├── Makefile │ └── linux │ ├── Makefile │ └── main.c ├── lib ├── Makefile ├── include │ └── messi.h └── src │ └── messi.c ├── messi ├── __init__.py ├── __main__.py ├── c_source │ ├── __init__.py │ └── templates │ │ ├── async │ │ ├── client.c │ │ ├── client.h │ │ ├── server.c │ │ └── server.h │ │ └── linux │ │ ├── client.c │ │ ├── client.h │ │ ├── server.c │ │ └── server.h ├── generate.py ├── py_source │ ├── __init__.py │ └── templates │ │ └── client.py └── version.py ├── requirements.txt ├── setup.py ├── tests ├── __init__.py ├── files │ ├── chat │ │ ├── async │ │ │ ├── chat_client.c │ │ │ ├── chat_client.h │ │ │ ├── chat_server.c │ │ │ └── chat_server.h │ │ ├── chat.proto │ │ ├── chat_client.py │ │ └── linux │ │ │ ├── chat_client.c │ │ │ ├── chat_client.h │ │ │ ├── chat_server.c │ │ │ └── chat_server.h │ ├── imported │ │ ├── imported.proto │ │ ├── linux │ │ │ ├── imported_client.c │ │ │ ├── imported_client.h │ │ │ ├── imported_server.c │ │ │ └── imported_server.h │ │ └── types_not_package_name.proto │ └── my_protocol │ │ ├── async │ │ ├── my_protocol_client.c │ │ ├── my_protocol_client.h │ │ ├── my_protocol_server.c │ │ └── my_protocol_server.h │ │ ├── linux │ │ ├── my_protocol_client.c │ │ ├── my_protocol_client.h │ │ ├── my_protocol_server.c │ │ └── my_protocol_server.h │ │ ├── my_protocol.proto │ │ └── my_protocol_client.py ├── test_client.py └── test_command_line.py └── tst ├── Makefile ├── async ├── Makefile ├── test_chat_client.c └── test_chat_server.c ├── linux ├── Makefile ├── test_chat_client.c └── test_chat_server.c ├── messi ├── Makefile └── test_messi.c └── test.mk /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gitsubmodule 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | *.o 8 | a.out 9 | 10 | # Distribution / packaging 11 | .Python 12 | env/ 13 | venv/ 14 | .venv/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *,cover 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | # Vim IDE 62 | *~ 63 | *.swp 64 | *.swo 65 | 66 | # IntelliJ IDEA 67 | .idea/ 68 | generated 69 | examples/*/server/*/server 70 | examples/*/client/*/client 71 | pbtools.[hc] 72 | tst/**/messi.[hc] 73 | tst/*/chat 74 | tst/*/imported 75 | tst/*/my_protocol 76 | chat_pb2.py 77 | examples/chat/client/python/chat_client.py 78 | examples/chat/client/python/messi.py 79 | examples/my_protocol/client/python/my_protocol_client.py 80 | examples/my_protocol/client/python/my_protocol_pb2.py 81 | /chat_client.py 82 | lib/libmessi.a -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3pp/async"] 2 | path = 3pp/async 3 | url = https://github.com/eerimoq/async 4 | [submodule "3pp/pbtools"] 5 | path = 3pp/pbtools 6 | url = https://github.com/eerimoq/pbtools.git 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - "3.7" 5 | - "3.8" 6 | 7 | install: 8 | - pip install coveralls 9 | - pip install -r requirements.txt 10 | 11 | script: 12 | - coverage run --source=messi setup.py test 13 | - make -s -j $(nproc) test-sdist 14 | - make -s -j $(nproc) -C tst SANITIZE=yes 15 | - make -s -j $(nproc) -C examples 16 | - make -s -j $(nproc) -C lib 17 | 18 | after_success: 19 | - coveralls 20 | - bash <(curl -s https://codecov.io/bash) -X coveragepy 21 | 22 | before_deploy: 23 | - git clean -dfx 24 | 25 | deploy: 26 | provider: pypi 27 | username: __token__ 28 | password: 29 | secure: '09TFnWvTZGSxb88+xcIncGjEmetHW1kWhOp4QEddYCtkTNdju/ou+2Aj2QLvKQUlxBKPfJpZYz+Y4ZcrIgnZkWP0galKr0e9vgp3nhl9UQkeMg6ivXgkWMhEbRNa4QFm0chcK82KaU0Ot3Wj+s5/my0ec7DejSY/E7FZ4UiQ+OUdpgH/0zCf2BQTfTQH5XPPO+/VPr2NKMWIaRkg9fBSJoqnD/ddKsKrdOHINjMfHIDdFfmDR90DaG1uQ7YE371/67HZc1JzSFqCippw5olRfk9oYiNXsKDaL8NUKCCWVHy2th4zensbgmN/3sz25mZ1HaQSZE/iCi+05ODqHpMm0Ccn0+ujsH1wv3M/b067HepNDSnUvW+gryyNe/LSCzJ0zB+ZnOyqabYqqLM13R6IWkyESz7MSFqj4JJpc+wB4Z7SS6QklzgjDSDBbxSGjst1E9PezD1siX1euT3yi4IWbwhQb6z44EwlvpsVxIdtD2xjssZsuDKE3tyEJMEsI2j0LeZRho56RFfajmNx5FwsavSVycEC50XMwQKmxNIxcGO1Eetu659VlBk5cKvi0ylPU7Ci0sBTaADBnZIWkEZWm+00qTNP0bbJK915LwKnHXnXNukPUp/l35LPb+TW9m0MtNC34Qtu3gPmbD2NykOdhhaIdMA+ibo+lO267Jr+uhI=' 30 | edge: true 31 | skip_existing: true 32 | on: 33 | tags: true 34 | distributions: "sdist bdist_wheel" 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Erik Moqvist 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include Makefile 3 | recursive-include messi/c_source *.h *.c 4 | recursive-include messi/py_source *.py 5 | recursive-include tests *.py *.h *.c *.proto -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | python3 setup.py test 3 | $(MAKE) -C tst 4 | $(MAKE) -C lib 5 | 6 | test-all: test test-sdist 7 | $(MAKE) -C examples 8 | 9 | test-sdist: 10 | rm -rf dist 11 | python3 setup.py sdist 12 | cd dist && \ 13 | mkdir test && \ 14 | cd test && \ 15 | tar xf ../*.tar.gz && \ 16 | cd messi-* && \ 17 | python3 setup.py test 18 | 19 | release-to-pypi: 20 | python3 setup.py sdist 21 | python3 setup.py bdist_wheel 22 | twine upload dist/* 23 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "tst/linux/imported" 3 | - "tst/linux/my_protocol" 4 | - "tst/linux/test_*.c" 5 | - "tst/async/test_*.c" 6 | - "tst/messi/test_*.c" 7 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(MAKE) -C chat 3 | $(MAKE) -C my_protocol 4 | -------------------------------------------------------------------------------- /examples/chat/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(MAKE) -C client 3 | $(MAKE) -C server 4 | -------------------------------------------------------------------------------- /examples/chat/README.rst: -------------------------------------------------------------------------------- 1 | About 2 | ===== 3 | 4 | Clients communcating to each other via a server. 5 | 6 | .. code-block:: text 7 | 8 | +------------------------------+ 9 | | server | 10 | +---o-----------o----------o---+ 11 | | | | 12 | | | | 13 | | | | 14 | +---o---+ +----o---+ +---o---+ 15 | | Linux | | Python | | Async | 16 | +-------+ +--------+ +-------+ 17 | 18 | Build everything. 19 | 20 | .. code-block:: text 21 | 22 | $ make -s 23 | 24 | Start the server. 25 | 26 | .. code-block:: text 27 | 28 | $ server/linux/server 29 | Server URI: tcp://127.0.0.1:6000 30 | Server started. 31 | Client connected. 34 | Number of connected clients: 2 35 | Client connected. 36 | Number of connected clients: 3 37 | 38 | Start three clients and send messages between them. Type a message and 39 | press to send it. 40 | 41 | Client implemented in C for the Linux platform: 42 | 43 | .. code-block:: text 44 | 45 | $ client/linux/client Linux 46 | Server URI: tcp://127.0.0.1:6000 47 | Connected to the server. 48 | Hello! 49 | Hello! 50 | Hi! 51 | Hola! 52 | 53 | Client implemented in Python: 54 | 55 | .. code-block:: text 56 | 57 | $ make -s -C client/python 58 | Server URI: tcp://127.0.0.1:6000 59 | Connected to the server. 60 | Hello! 61 | Hi! 62 | Hi! 63 | Hola! 64 | 65 | Client implemented in C for the Async platform: 66 | 67 | .. code-block:: text 68 | 69 | $ client/async/client Async 70 | Server URI: tcp://127.0.0.1:6000 71 | Connected to the server. 72 | Hello! 73 | Hello! 74 | Hi! 75 | Hola! 76 | Hola! 77 | -------------------------------------------------------------------------------- /examples/chat/chat.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package chat; 4 | 5 | message ClientToServer { 6 | oneof messages { 7 | ConnectReq connect_req = 1; 8 | MessageInd message_ind = 2; 9 | } 10 | } 11 | 12 | message ServerToClient { 13 | oneof messages { 14 | ConnectRsp connect_rsp = 1; 15 | MessageInd message_ind = 2; 16 | } 17 | } 18 | 19 | message ConnectReq { 20 | string user = 1; 21 | } 22 | 23 | message ConnectRsp { 24 | } 25 | 26 | message MessageInd { 27 | string user = 1; 28 | string text = 2; 29 | } 30 | -------------------------------------------------------------------------------- /examples/chat/client/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(MAKE) -C async 3 | $(MAKE) -C linux 4 | -------------------------------------------------------------------------------- /examples/chat/client/async/Makefile: -------------------------------------------------------------------------------- 1 | SRC += main.c 2 | SRC += client.c 3 | SRC += build/chat.c 4 | SRC += build/chat_client.c 5 | SRC += ../../../../3pp/pbtools/lib/src/pbtools.c 6 | SRC += ../../../../lib/src/messi.c 7 | INC += build 8 | INC += ../../../../lib/include 9 | INC += ../../../../3pp/pbtools/lib/include 10 | 11 | ASYNC_ROOT = ../../../../3pp/async 12 | INC += $(ASYNC_ROOT)/include 13 | INC += $(ASYNC_ROOT)/3pp/bitstream/include 14 | INC += $(ASYNC_ROOT)/3pp/humanfriendly/include 15 | INC += $(ASYNC_ROOT)/3pp/monolinux-c-library/include 16 | INC += $(ASYNC_ROOT)/3pp/mbedtls/include 17 | INC += $(ASYNC_ROOT)/3pp/mbedtls/crypto/include 18 | SRC += $(ASYNC_ROOT)/src/core/async_core.c 19 | SRC += $(ASYNC_ROOT)/src/core/async_timer.c 20 | SRC += $(ASYNC_ROOT)/src/core/async_channel.c 21 | SRC += $(ASYNC_ROOT)/src/core/async_tcp_client.c 22 | SRC += $(ASYNC_ROOT)/src/core/async_runtime_null.c 23 | SRC += $(ASYNC_ROOT)/src/modules/async_stcp_client.c 24 | SRC += $(ASYNC_ROOT)/src/modules/async_ssl.c 25 | SRC += $(ASYNC_ROOT)/src/modules/async_shell.c 26 | SRC += $(ASYNC_ROOT)/src/modules/async_mqtt_client.c 27 | SRC += $(ASYNC_ROOT)/src/runtimes/async_runtime.c 28 | SRC += $(ASYNC_ROOT)/src/runtimes/async_runtime_linux.c 29 | SRC += $(ASYNC_ROOT)/src/utils/async_utils_linux.c 30 | SRC += $(ASYNC_ROOT)/3pp/bitstream/src/bitstream.c 31 | SRC += $(ASYNC_ROOT)/3pp/humanfriendly/src/hf.c 32 | SRC += $(ASYNC_ROOT)/3pp/monolinux-c-library/src/ml.c 33 | SRC += $(ASYNC_ROOT)/3pp/monolinux-c-library/src/ml_bus.c 34 | SRC += $(ASYNC_ROOT)/3pp/monolinux-c-library/src/ml_log_object.c 35 | SRC += $(ASYNC_ROOT)/3pp/monolinux-c-library/src/ml_libc.c 36 | SRC += $(ASYNC_ROOT)/3pp/monolinux-c-library/src/ml_message.c 37 | SRC += $(ASYNC_ROOT)/3pp/monolinux-c-library/src/ml_queue.c 38 | SRC += $(ASYNC_ROOT)/3pp/monolinux-c-library/src/ml_timer.c 39 | SRC += $(ASYNC_ROOT)/3pp/monolinux-c-library/src/ml_worker_pool.c 40 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/aes.c 41 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/aesni.c 42 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/arc4.c 43 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/aria.c 44 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/asn1parse.c 45 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/asn1write.c 46 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/base64.c 47 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/bignum.c 48 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/blowfish.c 49 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/camellia.c 50 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ccm.c 51 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/certs.c 52 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/chacha20.c 53 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/chachapoly.c 54 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/cipher.c 55 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/cipher_wrap.c 56 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/cmac.c 57 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ctr_drbg.c 58 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/debug.c 59 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/des.c 60 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/dhm.c 61 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ecdh.c 62 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ecdsa.c 63 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ecjpake.c 64 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ecp.c 65 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ecp_curves.c 66 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/entropy.c 67 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/entropy_poll.c 68 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/error.c 69 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/gcm.c 70 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/havege.c 71 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/hkdf.c 72 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/hmac_drbg.c 73 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/md2.c 74 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/md4.c 75 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/md5.c 76 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/md.c 77 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/md_wrap.c 78 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/memory_buffer_alloc.c 79 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/net_sockets.c 80 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/nist_kw.c 81 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/oid.c 82 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/padlock.c 83 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/pem.c 84 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/pk.c 85 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/pkcs11.c 86 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/pkcs12.c 87 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/pkcs5.c 88 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/pkparse.c 89 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/pk_wrap.c 90 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/pkwrite.c 91 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/platform.c 92 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/platform_util.c 93 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/poly1305.c 94 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ripemd160.c 95 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/rsa.c 96 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/rsa_internal.c 97 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/sha1.c 98 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/sha256.c 99 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/sha512.c 100 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ssl_cache.c 101 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ssl_ciphersuites.c 102 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ssl_cli.c 103 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ssl_cookie.c 104 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ssl_srv.c 105 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ssl_ticket.c 106 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ssl_tls.c 107 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/threading.c 108 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/timing.c 109 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/version.c 110 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/version_features.c 111 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/x509.c 112 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/x509_create.c 113 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/x509_crl.c 114 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/x509_crt.c 115 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/x509_csr.c 116 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/x509write_crt.c 117 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/x509write_csr.c 118 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/xtea.c 119 | 120 | CFLAGS += -Wall 121 | CFLAGS += -Wextra 122 | CFLAGS += -Werror 123 | CFLAGS += $(INC:%=-I%) 124 | CFLAGS += -D_GNU_SOURCE 125 | 126 | all: 127 | mkdir -p build 128 | PYTHONPATH=../../../.. \ 129 | python3 -m messi generate_c_source \ 130 | -o build -p async -s client ../../chat.proto 131 | gcc $(CFLAGS) $(SRC) -lpthread -o client 132 | -------------------------------------------------------------------------------- /examples/chat/client/async/client.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | * 26 | * This file is part of the Messi project. 27 | */ 28 | 29 | #include 30 | #include "client.h" 31 | 32 | #define to_client(chat_client_p) \ 33 | async_container_of(chat_client_p, struct client_t, client) 34 | 35 | static void on_connected(struct chat_client_t *self_p) 36 | { 37 | struct chat_connect_req_t *message_p; 38 | 39 | message_p = chat_client_init_connect_req(self_p); 40 | message_p->user_p = (char *)to_client(self_p)->user_p; 41 | chat_client_send(self_p); 42 | } 43 | 44 | static void on_disconnected(struct chat_client_t *self_p, 45 | enum messi_disconnect_reason_t disconnect_reason) 46 | { 47 | printf("Disconnected from the server (reason: %s).\n", 48 | messi_disconnect_reason_string(disconnect_reason)); 49 | 50 | to_client(self_p)->connected = false; 51 | } 52 | 53 | static void on_connect_rsp(struct chat_client_t *self_p, 54 | struct chat_connect_rsp_t *message_p) 55 | { 56 | (void)message_p; 57 | 58 | printf("Connected to the server.\n"); 59 | 60 | to_client(self_p)->connected = true; 61 | } 62 | 63 | static void on_message_ind(struct chat_client_t *self_p, 64 | struct chat_message_ind_t *message_p) 65 | { 66 | (void)self_p; 67 | 68 | printf("<%s> %s\n", message_p->user_p, message_p->text_p); 69 | } 70 | 71 | static void send_message(struct client_t *self_p) 72 | { 73 | struct chat_message_ind_t *message_p; 74 | 75 | message_p = chat_client_init_message_ind(&self_p->client); 76 | message_p->user_p = (char *)self_p->user_p; 77 | message_p->text_p = &self_p->line.buf[0]; 78 | chat_client_send(&self_p->client); 79 | } 80 | 81 | void client_init(struct client_t *self_p, 82 | const char *user_p, 83 | const char *server_uri_p, 84 | struct async_t *async_p) 85 | { 86 | self_p->user_p = user_p; 87 | self_p->line.length = 0; 88 | self_p->connected = false; 89 | chat_client_init(&self_p->client, 90 | server_uri_p, 91 | &self_p->encoded_in[0], 92 | sizeof(self_p->encoded_in), 93 | &self_p->workspace_in[0], 94 | sizeof(self_p->workspace_in), 95 | &self_p->encoded_out[0], 96 | sizeof(self_p->encoded_out), 97 | &self_p->workspace_out[0], 98 | sizeof(self_p->workspace_out), 99 | on_connected, 100 | on_disconnected, 101 | on_connect_rsp, 102 | on_message_ind, 103 | async_p); 104 | chat_client_start(&self_p->client); 105 | } 106 | 107 | void client_user_input(struct client_t *self_p, void *arg_p) 108 | { 109 | char *data_p; 110 | 111 | data_p = (char *)arg_p; 112 | 113 | if (!self_p->connected) { 114 | free(data_p); 115 | return; 116 | } 117 | 118 | if (self_p->line.length == (sizeof(self_p->line.buf) - 1)) { 119 | self_p->line.length = 0; 120 | } 121 | 122 | self_p->line.buf[self_p->line.length] = *data_p; 123 | 124 | if (self_p->line.buf[self_p->line.length] == '\n') { 125 | self_p->line.buf[self_p->line.length] = '\0'; 126 | send_message(self_p); 127 | self_p->line.length = 0; 128 | } else { 129 | self_p->line.length++; 130 | } 131 | 132 | free(data_p); 133 | } 134 | -------------------------------------------------------------------------------- /examples/chat/client/async/client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | * 26 | * This file is part of the Messi project. 27 | */ 28 | 29 | #ifndef CLIENT_H 30 | #define CLIENT_H 31 | 32 | #include "async.h" 33 | #include "chat_client.h" 34 | 35 | struct client_t { 36 | const char *user_p; 37 | struct { 38 | char buf[128]; 39 | size_t length; 40 | } line; 41 | bool connected; 42 | struct chat_client_t client; 43 | uint8_t encoded_in[128]; 44 | uint8_t encoded_out[128]; 45 | uint8_t workspace_in[128]; 46 | uint8_t workspace_out[128]; 47 | }; 48 | 49 | void client_init(struct client_t *self_p, 50 | const char *user_p, 51 | const char *server_uri_p, 52 | struct async_t *async_p); 53 | 54 | void client_user_input(struct client_t *obj_p, void *arg_p); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /examples/chat/client/async/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | * 26 | * This file is part of the Messi project. 27 | */ 28 | 29 | #include 30 | #include 31 | #include "async.h" 32 | #include "client.h" 33 | 34 | static struct async_t async; 35 | static struct client_t client; 36 | 37 | static void parse_args(int argc, 38 | const char *argv[], 39 | const char **user_pp) 40 | { 41 | if (argc != 2) { 42 | printf("usage: %s \n", argv[0]); 43 | exit(1); 44 | } 45 | 46 | *user_pp = argv[1]; 47 | } 48 | 49 | static void *forward_stdin_to_client_main() 50 | { 51 | char *data_p; 52 | ssize_t size; 53 | 54 | while (true) { 55 | data_p = malloc(1); 56 | 57 | if (data_p == NULL) { 58 | exit(1); 59 | } 60 | 61 | size = read(STDIN_FILENO, data_p, 1); 62 | 63 | if (size != 1) { 64 | break; 65 | } 66 | 67 | async_call_threadsafe(&async, 68 | (async_func_t)client_user_input, 69 | &client, 70 | data_p); 71 | } 72 | 73 | return (NULL); 74 | } 75 | 76 | int main(int argc, const char *argv[]) 77 | { 78 | const char *user_p; 79 | pthread_t user_input_pthread; 80 | 81 | parse_args(argc, argv, &user_p); 82 | 83 | async_init(&async); 84 | async_set_runtime(&async, async_runtime_create()); 85 | client_init(&client, user_p, "tcp://127.0.0.1:6000", &async); 86 | pthread_create(&user_input_pthread, NULL, forward_stdin_to_client_main, NULL); 87 | async_run_forever(&async); 88 | 89 | return (1); 90 | } 91 | -------------------------------------------------------------------------------- /examples/chat/client/linux/Makefile: -------------------------------------------------------------------------------- 1 | SRC += main.c 2 | SRC += build/chat.c 3 | SRC += build/chat_client.c 4 | SRC += ../../../../3pp/pbtools/lib/src/pbtools.c 5 | SRC += ../../../../lib/src/messi.c 6 | CFLAGS += -Wall 7 | CFLAGS += -Wextra 8 | CFLAGS += -Werror 9 | CFLAGS += -I build 10 | CFLAGS += -I ../../../../lib/include 11 | CFLAGS += -I ../../../../3pp/pbtools/lib/include 12 | 13 | all: 14 | mkdir -p build 15 | PYTHONPATH=../../../.. \ 16 | python3 -m messi generate_c_source \ 17 | -o build -p linux -s client ../../chat.proto 18 | gcc $(CFLAGS) $(SRC) -o client 19 | -------------------------------------------------------------------------------- /examples/chat/client/linux/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | * 26 | * This file is part of the Messi project. 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "chat_client.h" 34 | 35 | static int line_length; 36 | static char line_buf[128]; 37 | static bool connected = false; 38 | static const char *user_p; 39 | 40 | static void on_connected(struct chat_client_t *self_p) 41 | { 42 | struct chat_connect_req_t *message_p; 43 | 44 | message_p = chat_client_init_connect_req(self_p); 45 | message_p->user_p = (char *)user_p; 46 | chat_client_send(self_p); 47 | } 48 | 49 | static void on_disconnected(struct chat_client_t *self_p, 50 | enum messi_disconnect_reason_t disconnect_reason) 51 | { 52 | (void)self_p; 53 | 54 | printf("Disconnected from the server (reason: %s).\n", 55 | messi_disconnect_reason_string(disconnect_reason)); 56 | 57 | line_length = 0; 58 | connected = false; 59 | } 60 | 61 | static void on_connect_rsp(struct chat_client_t *self_p, 62 | struct chat_connect_rsp_t *message_p) 63 | { 64 | (void)self_p; 65 | (void)message_p; 66 | 67 | printf("Connected to the server.\n"); 68 | 69 | connected = true; 70 | } 71 | 72 | static void on_message_ind(struct chat_client_t *self_p, 73 | struct chat_message_ind_t *message_p) 74 | { 75 | (void)self_p; 76 | 77 | printf("<%s> %s\n", message_p->user_p, message_p->text_p); 78 | } 79 | 80 | static void send_message_ind(struct chat_client_t *self_p) 81 | { 82 | struct chat_message_ind_t *message_p; 83 | 84 | message_p = chat_client_init_message_ind(self_p); 85 | message_p->user_p = (char *)user_p; 86 | message_p->text_p = &line_buf[0]; 87 | chat_client_send(self_p); 88 | } 89 | 90 | static void user_input(struct chat_client_t *self_p) 91 | { 92 | if (line_length == (sizeof(line_buf) - 1)) { 93 | line_length = 0; 94 | } 95 | 96 | read(STDIN_FILENO, &line_buf[line_length], 1); 97 | 98 | if (!connected) { 99 | return; 100 | } 101 | 102 | if (line_buf[line_length] == '\n') { 103 | line_buf[line_length] = '\0'; 104 | send_message_ind(self_p); 105 | line_length = 0; 106 | } else { 107 | line_length++; 108 | } 109 | } 110 | 111 | static void parse_args(int argc, 112 | const char *argv[], 113 | const char **user_pp, 114 | const char **uri_pp) 115 | { 116 | if (argc < 2) { 117 | printf("usage: %s []\n", argv[0]); 118 | exit(1); 119 | } 120 | 121 | *user_pp = argv[1]; 122 | 123 | if (argc == 3) { 124 | *uri_pp = argv[2]; 125 | } else { 126 | *uri_pp = "tcp://127.0.0.1:6000"; 127 | } 128 | } 129 | 130 | int main(int argc, const char *argv[]) 131 | { 132 | struct chat_client_t client; 133 | const char *uri_p; 134 | int epoll_fd; 135 | struct epoll_event event; 136 | int res; 137 | uint8_t encoded_in[128]; 138 | uint8_t encoded_out[128]; 139 | uint8_t workspace_in[128]; 140 | uint8_t workspace_out[128]; 141 | 142 | parse_args(argc, argv, &user_p, &uri_p); 143 | 144 | printf("Server URI: %s\n", uri_p); 145 | 146 | epoll_fd = epoll_create1(0); 147 | 148 | if (epoll_fd == -1) { 149 | return (1); 150 | } 151 | 152 | event.data.fd = STDIN_FILENO; 153 | event.events = EPOLLIN; 154 | 155 | res = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, STDIN_FILENO, &event); 156 | 157 | if (res != 0) { 158 | return (1); 159 | } 160 | 161 | line_length = 0; 162 | 163 | res = chat_client_init(&client, 164 | uri_p, 165 | &encoded_in[0], 166 | sizeof(encoded_in), 167 | &workspace_in[0], 168 | sizeof(workspace_in), 169 | &encoded_out[0], 170 | sizeof(encoded_out), 171 | &workspace_out[0], 172 | sizeof(workspace_out), 173 | on_connected, 174 | on_disconnected, 175 | on_connect_rsp, 176 | on_message_ind, 177 | epoll_fd, 178 | NULL); 179 | 180 | if (res != 0) { 181 | printf("Init failed.\n"); 182 | 183 | return (1); 184 | } 185 | 186 | chat_client_start(&client); 187 | 188 | while (true) { 189 | res = epoll_wait(epoll_fd, &event, 1, -1); 190 | 191 | if (res != 1) { 192 | break; 193 | } 194 | 195 | if (event.data.fd == STDIN_FILENO) { 196 | user_input(&client); 197 | } else { 198 | chat_client_process(&client, event.data.fd, event.events); 199 | } 200 | } 201 | 202 | return (0); 203 | } 204 | -------------------------------------------------------------------------------- /examples/chat/client/python/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | PYTHONPATH=../../../.. \ 3 | python3 -m messi generate_py_source -I ../.. ../../chat.proto 4 | python3 main.py Python 5 | -------------------------------------------------------------------------------- /examples/chat/client/python/main.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import argparse 3 | import asyncio 4 | import logging 5 | 6 | from chat_client import ChatClient 7 | 8 | 9 | class Client(ChatClient): 10 | 11 | def __init__(self, user, uri): 12 | super().__init__(uri) 13 | self._user = user 14 | 15 | async def on_connected(self): 16 | message = self.init_connect_req() 17 | message.user = self._user 18 | self.send() 19 | 20 | async def on_disconnected(self): 21 | print("Disconnected from the server.") 22 | 23 | async def on_connect_rsp(self, message): 24 | print("Connected to the server."); 25 | 26 | async def on_message_ind(self, message): 27 | print(f"<{message.user}> {message.text}"); 28 | 29 | async def on_input(self, text): 30 | message = self.init_message_ind() 31 | message.user = self._user 32 | message.text = text 33 | self.send() 34 | 35 | 36 | class ClientThread(threading.Thread): 37 | 38 | def __init__(self, loop, client): 39 | super().__init__() 40 | self._loop = loop 41 | self._client = client 42 | self.daemon = True 43 | 44 | def run(self): 45 | asyncio.set_event_loop(self._loop) 46 | self._loop.run_until_complete(self._start()) 47 | self._loop.run_forever() 48 | 49 | async def _start(self): 50 | self._client.start() 51 | 52 | 53 | def main(): 54 | logging.basicConfig(level=logging.INFO) 55 | 56 | parser = argparse.ArgumentParser() 57 | parser.add_argument('--uri', default='tcp://127.0.0.1:6000') 58 | parser.add_argument('user') 59 | 60 | args = parser.parse_args() 61 | 62 | print(f"Server URI: {args.uri}") 63 | 64 | loop = asyncio.new_event_loop() 65 | client = Client(args.user, args.uri) 66 | client_thread = ClientThread(loop, client) 67 | client_thread.start() 68 | 69 | while True: 70 | text = input('') 71 | asyncio.run_coroutine_threadsafe(client.on_input(text), loop) 72 | 73 | 74 | if __name__ == '__main__': 75 | main() 76 | -------------------------------------------------------------------------------- /examples/chat/server/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(MAKE) -C async 3 | $(MAKE) -C linux 4 | -------------------------------------------------------------------------------- /examples/chat/server/async/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | mkdir -p generated 3 | PYTHONPATH=../../../.. \ 4 | python3 -m messi generate_c_source \ 5 | -o generated -p async -s server ../../chat.proto 6 | # gcc -I build build/chat.c server.c main.c -o server 7 | -------------------------------------------------------------------------------- /examples/chat/server/async/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | * 26 | * This file is part of the Messi project. 27 | */ 28 | 29 | #include "async.h" 30 | #include "server.h" 31 | 32 | int main() 33 | { 34 | struct async_t async; 35 | struct server_t server; 36 | 37 | async_init(&async); 38 | async_set_runtime(&async, async_runtime_create()); 39 | server_init(&server, "tcp://127.0.0.1:6000", &async); 40 | async_run_forever(&async); 41 | 42 | return (0); 43 | } 44 | -------------------------------------------------------------------------------- /examples/chat/server/async/server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | * 26 | * This file is part of the Messi project. 27 | */ 28 | 29 | #include 30 | #include "server.h" 31 | 32 | static void on_client_disconnected(struct chat_server_t *self_p, 33 | struct chat_server_client_t *client_p) 34 | { 35 | (void)self_p; 36 | (void)client_p; 37 | 38 | number_of_connected_clients--; 39 | 40 | printf("Number of connected clients: %d\n", number_of_connected_clients); 41 | } 42 | 43 | static void on_connect_req(struct chat_server_t *self_p, 44 | struct chat_connect_req_t *message_p) 45 | { 46 | printf("Client <%s> connected.\n", message_p->user_p); 47 | 48 | chat_server_init_connect_rsp(self_p); 49 | chat_server_send(self_p); 50 | } 51 | 52 | static void on_message_ind(struct chat_server_t *self_p, 53 | struct chat_message_ind_t *message_in_p) 54 | { 55 | struct chat_message_ind_t *message_p; 56 | 57 | message_p = chat_server_init_message_ind(&self_p->server); 58 | message_p->user_p = message_in_p->user_p; 59 | message_p->text_p = message_in_p->text_p; 60 | chat_server_broadcast(&self_p->server); 61 | } 62 | 63 | void server_init(struct server_t *self_p, 64 | const char *uri_p, 65 | struct async_t *async_p) 66 | { 67 | chat_server_init(&self_p->server, 68 | uri_p, 69 | &self_p->clients[0], 70 | 10, 71 | &self_p->clients_input_buffers[0][0], 72 | sizeof(self_p->clients_input_buffers[0]), 73 | &self_p->message[0], 74 | sizeof(self_p->message), 75 | &self_p->workspace_in[0], 76 | sizeof(self_p->workspace_in), 77 | &self_p->workspace_out[0], 78 | sizeof(self_p->workspace_out), 79 | NULL, 80 | on_client_disconnected, 81 | on_connect_req, 82 | on_message_ind, 83 | self_p, 84 | async_p); 85 | chat_server_start(&self_p->server); 86 | 87 | printf("Server started.\n"); 88 | } 89 | -------------------------------------------------------------------------------- /examples/chat/server/async/server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | * 26 | * This file is part of the Messi project. 27 | */ 28 | 29 | #ifndef SERVER_H 30 | #define SERVER_H 31 | 32 | #include "async.h" 33 | #include "chat_server.h" 34 | 35 | struct server_t { 36 | struct chat_server_t server; 37 | struct chat_server_client_t clients[10]; 38 | int number_of_connected_clients; 39 | }; 40 | 41 | void server_init(struct server_t *self_p, 42 | const char *uri_p, 43 | struct async_t *async_p); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /examples/chat/server/linux/Makefile: -------------------------------------------------------------------------------- 1 | SRC += main.c 2 | SRC += build/chat.c 3 | SRC += build/chat_server.c 4 | SRC += ../../../../3pp/pbtools/lib/src/pbtools.c 5 | SRC += ../../../../lib/src/messi.c 6 | CFLAGS += -Wall 7 | CFLAGS += -Wextra 8 | CFLAGS += -Werror 9 | CFLAGS += -I build 10 | CFLAGS += -I ../../../../lib/include 11 | CFLAGS += -I ../../../../3pp/pbtools/lib/include 12 | 13 | all: 14 | mkdir -p build 15 | PYTHONPATH=../../../.. \ 16 | python3 -m messi generate_c_source \ 17 | -o build -p linux -s server ../../chat.proto 18 | gcc $(CFLAGS) $(SRC) -o server 19 | -------------------------------------------------------------------------------- /examples/chat/server/linux/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | * 26 | * This file is part of the Messi project. 27 | */ 28 | 29 | #include 30 | #include 31 | #include "chat_server.h" 32 | 33 | static int number_of_connected_clients = 0; 34 | 35 | static void on_client_disconnected(struct chat_server_t *self_p, 36 | struct chat_server_client_t *client_p) 37 | { 38 | (void)self_p; 39 | (void)client_p; 40 | 41 | number_of_connected_clients--; 42 | 43 | printf("Number of connected clients: %d\n", number_of_connected_clients); 44 | } 45 | 46 | static void on_connect_req(struct chat_server_t *self_p, 47 | struct chat_server_client_t *client_p, 48 | struct chat_connect_req_t *message_p) 49 | { 50 | (void)client_p; 51 | 52 | number_of_connected_clients++; 53 | 54 | printf("Client <%s> connected.\n", message_p->user_p); 55 | printf("Number of connected clients: %d\n", number_of_connected_clients); 56 | 57 | chat_server_init_connect_rsp(self_p); 58 | chat_server_reply(self_p); 59 | } 60 | 61 | static void on_message_ind(struct chat_server_t *self_p, 62 | struct chat_server_client_t *client_p, 63 | struct chat_message_ind_t *message_in_p) 64 | { 65 | (void)client_p; 66 | 67 | struct chat_message_ind_t *message_p; 68 | 69 | message_p = chat_server_init_message_ind(self_p); 70 | message_p->user_p = message_in_p->user_p; 71 | message_p->text_p = message_in_p->text_p; 72 | chat_server_broadcast(self_p); 73 | } 74 | 75 | static void parse_args(int argc, 76 | const char *argv[], 77 | const char **uri_pp) 78 | { 79 | if (argc == 2) { 80 | *uri_pp = argv[1]; 81 | } else { 82 | *uri_pp = "tcp://127.0.0.1:6000"; 83 | } 84 | } 85 | 86 | int main(int argc, const char *argv[]) 87 | { 88 | struct chat_server_t server; 89 | struct chat_server_client_t clients[10]; 90 | uint8_t clients_input_buffers[10][128]; 91 | uint8_t message[128]; 92 | uint8_t workspace_in[128]; 93 | uint8_t workspace_out[128]; 94 | int epoll_fd; 95 | struct epoll_event event; 96 | int res; 97 | const char *uri_p; 98 | 99 | parse_args(argc, argv, &uri_p); 100 | 101 | printf("Server URI: %s\n", uri_p); 102 | 103 | epoll_fd = epoll_create1(0); 104 | 105 | if (epoll_fd == -1) { 106 | return (1); 107 | } 108 | 109 | res = chat_server_init(&server, 110 | uri_p, 111 | &clients[0], 112 | 10, 113 | &clients_input_buffers[0][0], 114 | sizeof(clients_input_buffers[0]), 115 | &message[0], 116 | sizeof(message), 117 | &workspace_in[0], 118 | sizeof(workspace_in), 119 | &workspace_out[0], 120 | sizeof(workspace_out), 121 | NULL, 122 | on_client_disconnected, 123 | on_connect_req, 124 | on_message_ind, 125 | epoll_fd, 126 | NULL); 127 | 128 | if (res != 0) { 129 | printf("Init failed.\n"); 130 | 131 | return (1); 132 | } 133 | 134 | res = chat_server_start(&server); 135 | 136 | if (res != 0) { 137 | printf("Start failed.\n"); 138 | 139 | return (1); 140 | } 141 | 142 | printf("Server started.\n"); 143 | 144 | while (true) { 145 | res = epoll_wait(epoll_fd, &event, 1, -1); 146 | 147 | if (res != 1) { 148 | break; 149 | } 150 | 151 | chat_server_process(&server, event.data.fd, event.events); 152 | } 153 | 154 | return (1); 155 | } 156 | -------------------------------------------------------------------------------- /examples/my_protocol/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(MAKE) -C client 3 | $(MAKE) -C server 4 | -------------------------------------------------------------------------------- /examples/my_protocol/README.rst: -------------------------------------------------------------------------------- 1 | About 2 | ===== 3 | 4 | Similar to the my_protocol example in ../../README.rst. 5 | 6 | Sequence diagram of connection setup and user messages sent in this 7 | example. 8 | 9 | .. code-block:: text 10 | 11 | +--------+ +--------+ 12 | | client | | server | 13 | +--------+ +--------+ 14 | | TCP connect | 15 | |========================================>| 16 | on_connected() | | on_client_connected() 17 | send() | FooReq | 18 | |========================================>| 19 | | FooRsp | reply() 20 | |<========================================| 21 | send() | BarInd | 22 | |========================================>| 23 | send() | BarInd | 24 | |========================================>| 25 | | FieReq | reply() 26 | |<========================================| 27 | send() | FieRsp | 28 | |========================================>| 29 | | TCP disconnect | disconnect() 30 | |<========================================| 31 | on_disconnected() | | exit() 32 | exit() | | 33 | 34 | Build everything. 35 | 36 | .. code-block:: text 37 | 38 | $ make -s 39 | 40 | Start the server. 41 | 42 | .. code-block:: text 43 | 44 | $ server/linux/server 45 | Server started. 46 | Got FooReq. Sending FooRsp. 47 | Got BarInd. 48 | Got BarInd. Sending FieReq. 49 | Got FieRsp. Disconnecting the client and exiting. 50 | 51 | Start one of the clients below. 52 | 53 | C Linux: 54 | 55 | .. code-block:: text 56 | 57 | $ client/linux/client 58 | Connected. Sending FooReq. 59 | Got FooRsp. Sending BarInd twice. 60 | Got FieReq. Sending FieRsp. 61 | Disconnected. Exiting. 62 | 63 | C Async: 64 | 65 | .. code-block:: text 66 | 67 | $ client/async/client 68 | Connected. Sending FooReq. 69 | Got FooRsp. Sending BarInd twice. 70 | Got FieReq. Sending FieRsp. 71 | Disconnected. Exiting. 72 | 73 | Python: 74 | 75 | .. code-block:: text 76 | 77 | $ make -C client/python 78 | Connected. Sending FooReq. 79 | Got FooRsp. Sending BarInd twice. 80 | Got FieReq. Sending FieRsp. 81 | Disconnected. Exiting. 82 | -------------------------------------------------------------------------------- /examples/my_protocol/client/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(MAKE) -C linux 3 | $(MAKE) -C async 4 | -------------------------------------------------------------------------------- /examples/my_protocol/client/async/Makefile: -------------------------------------------------------------------------------- 1 | SRC += main.c 2 | SRC += client.c 3 | SRC += build/my_protocol.c 4 | SRC += build/my_protocol_client.c 5 | SRC += ../../../../3pp/pbtools/lib/src/pbtools.c 6 | SRC += ../../../../lib/src/messi.c 7 | INC += build 8 | INC += ../../../../lib/include 9 | INC += ../../../../3pp/pbtools/lib/include 10 | 11 | ASYNC_ROOT = ../../../../3pp/async 12 | INC += $(ASYNC_ROOT)/include 13 | INC += $(ASYNC_ROOT)/3pp/bitstream/include 14 | INC += $(ASYNC_ROOT)/3pp/humanfriendly/include 15 | INC += $(ASYNC_ROOT)/3pp/monolinux-c-library/include 16 | INC += $(ASYNC_ROOT)/3pp/mbedtls/include 17 | INC += $(ASYNC_ROOT)/3pp/mbedtls/crypto/include 18 | SRC += $(ASYNC_ROOT)/src/core/async_core.c 19 | SRC += $(ASYNC_ROOT)/src/core/async_timer.c 20 | SRC += $(ASYNC_ROOT)/src/core/async_channel.c 21 | SRC += $(ASYNC_ROOT)/src/core/async_tcp_client.c 22 | SRC += $(ASYNC_ROOT)/src/core/async_runtime_null.c 23 | SRC += $(ASYNC_ROOT)/src/modules/async_stcp_client.c 24 | SRC += $(ASYNC_ROOT)/src/modules/async_ssl.c 25 | SRC += $(ASYNC_ROOT)/src/modules/async_shell.c 26 | SRC += $(ASYNC_ROOT)/src/modules/async_mqtt_client.c 27 | SRC += $(ASYNC_ROOT)/src/runtimes/async_runtime.c 28 | SRC += $(ASYNC_ROOT)/src/runtimes/async_runtime_linux.c 29 | SRC += $(ASYNC_ROOT)/src/utils/async_utils_linux.c 30 | SRC += $(ASYNC_ROOT)/3pp/bitstream/src/bitstream.c 31 | SRC += $(ASYNC_ROOT)/3pp/humanfriendly/src/hf.c 32 | SRC += $(ASYNC_ROOT)/3pp/monolinux-c-library/src/ml.c 33 | SRC += $(ASYNC_ROOT)/3pp/monolinux-c-library/src/ml_bus.c 34 | SRC += $(ASYNC_ROOT)/3pp/monolinux-c-library/src/ml_log_object.c 35 | SRC += $(ASYNC_ROOT)/3pp/monolinux-c-library/src/ml_libc.c 36 | SRC += $(ASYNC_ROOT)/3pp/monolinux-c-library/src/ml_message.c 37 | SRC += $(ASYNC_ROOT)/3pp/monolinux-c-library/src/ml_queue.c 38 | SRC += $(ASYNC_ROOT)/3pp/monolinux-c-library/src/ml_timer.c 39 | SRC += $(ASYNC_ROOT)/3pp/monolinux-c-library/src/ml_worker_pool.c 40 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/aes.c 41 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/aesni.c 42 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/arc4.c 43 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/aria.c 44 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/asn1parse.c 45 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/asn1write.c 46 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/base64.c 47 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/bignum.c 48 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/blowfish.c 49 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/camellia.c 50 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ccm.c 51 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/certs.c 52 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/chacha20.c 53 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/chachapoly.c 54 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/cipher.c 55 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/cipher_wrap.c 56 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/cmac.c 57 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ctr_drbg.c 58 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/debug.c 59 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/des.c 60 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/dhm.c 61 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ecdh.c 62 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ecdsa.c 63 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ecjpake.c 64 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ecp.c 65 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ecp_curves.c 66 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/entropy.c 67 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/entropy_poll.c 68 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/error.c 69 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/gcm.c 70 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/havege.c 71 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/hkdf.c 72 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/hmac_drbg.c 73 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/md2.c 74 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/md4.c 75 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/md5.c 76 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/md.c 77 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/md_wrap.c 78 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/memory_buffer_alloc.c 79 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/net_sockets.c 80 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/nist_kw.c 81 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/oid.c 82 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/padlock.c 83 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/pem.c 84 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/pk.c 85 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/pkcs11.c 86 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/pkcs12.c 87 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/pkcs5.c 88 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/pkparse.c 89 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/pk_wrap.c 90 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/pkwrite.c 91 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/platform.c 92 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/platform_util.c 93 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/poly1305.c 94 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ripemd160.c 95 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/rsa.c 96 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/rsa_internal.c 97 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/sha1.c 98 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/sha256.c 99 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/sha512.c 100 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ssl_cache.c 101 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ssl_ciphersuites.c 102 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ssl_cli.c 103 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ssl_cookie.c 104 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ssl_srv.c 105 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ssl_ticket.c 106 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/ssl_tls.c 107 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/threading.c 108 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/timing.c 109 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/version.c 110 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/version_features.c 111 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/x509.c 112 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/x509_create.c 113 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/x509_crl.c 114 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/x509_crt.c 115 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/x509_csr.c 116 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/x509write_crt.c 117 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/x509write_csr.c 118 | SRC += $(ASYNC_ROOT)/3pp/mbedtls/library/xtea.c 119 | 120 | CFLAGS += -Wall 121 | CFLAGS += -Wextra 122 | CFLAGS += -Werror 123 | CFLAGS += $(INC:%=-I%) 124 | CFLAGS += -D_GNU_SOURCE 125 | 126 | all: 127 | mkdir -p build 128 | PYTHONPATH=../../../.. \ 129 | python3 -m messi generate_c_source \ 130 | -o build -p async ../../my_protocol.proto 131 | gcc $(CFLAGS) $(SRC) -lpthread -o client 132 | -------------------------------------------------------------------------------- /examples/my_protocol/client/async/client.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | * 26 | * This file is part of the Messi project. 27 | */ 28 | 29 | #include 30 | #include "client.h" 31 | 32 | #define to_client(my_protocol_client_p) \ 33 | async_container_of(my_protocol_client_p, struct client_t, client) 34 | 35 | static void on_connected(struct my_protocol_client_t *self_p) 36 | { 37 | printf("Connected. Sending FooReq.\n"); 38 | 39 | my_protocol_client_init_foo_req(self_p); 40 | my_protocol_client_send(self_p); 41 | } 42 | 43 | static void on_disconnected(struct my_protocol_client_t *self_p, 44 | enum messi_disconnect_reason_t disconnect_reason) 45 | { 46 | (void)self_p; 47 | 48 | printf("Disconnected (reason: %s). Exiting.\n", 49 | messi_disconnect_reason_string(disconnect_reason)); 50 | 51 | exit(0); 52 | 53 | } 54 | 55 | static void on_foo_rsp(struct my_protocol_client_t *self_p, 56 | struct my_protocol_foo_rsp_t *message_p) 57 | { 58 | (void)message_p; 59 | 60 | printf("Got FooRsp. Sending BarInd twice.\n"); 61 | 62 | my_protocol_client_init_bar_ind(self_p); 63 | my_protocol_client_send(self_p); 64 | my_protocol_client_send(self_p); 65 | } 66 | 67 | static void on_fie_req(struct my_protocol_client_t *self_p, 68 | struct my_protocol_fie_req_t *message_p) 69 | { 70 | (void)message_p; 71 | 72 | printf("Got FieReq. Sending FieRsp.\n"); 73 | 74 | my_protocol_client_init_fie_rsp(self_p); 75 | my_protocol_client_send(self_p); 76 | } 77 | 78 | void client_init(struct client_t *self_p, struct async_t *async_p) 79 | { 80 | my_protocol_client_init(&self_p->client, 81 | "tcp://127.0.0.1:7840", 82 | &self_p->encoded_in[0], 83 | sizeof(self_p->encoded_in), 84 | &self_p->workspace_in[0], 85 | sizeof(self_p->workspace_in), 86 | &self_p->encoded_out[0], 87 | sizeof(self_p->encoded_out), 88 | &self_p->workspace_out[0], 89 | sizeof(self_p->workspace_out), 90 | on_connected, 91 | on_disconnected, 92 | on_foo_rsp, 93 | on_fie_req, 94 | async_p); 95 | my_protocol_client_start(&self_p->client); 96 | } 97 | -------------------------------------------------------------------------------- /examples/my_protocol/client/async/client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | * 26 | * This file is part of the Messi project. 27 | */ 28 | 29 | #ifndef CLIENT_H 30 | #define CLIENT_H 31 | 32 | #include "async.h" 33 | #include "my_protocol_client.h" 34 | 35 | struct client_t { 36 | struct my_protocol_client_t client; 37 | uint8_t encoded_in[128]; 38 | uint8_t encoded_out[128]; 39 | uint8_t workspace_in[128]; 40 | uint8_t workspace_out[128]; 41 | }; 42 | 43 | void client_init(struct client_t *self_p, struct async_t *async_p); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /examples/my_protocol/client/async/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | * 26 | * This file is part of the Messi project. 27 | */ 28 | 29 | #include "async.h" 30 | #include "client.h" 31 | 32 | int main() 33 | { 34 | struct async_t async; 35 | struct client_t client; 36 | 37 | async_init(&async); 38 | async_set_runtime(&async, async_runtime_create()); 39 | client_init(&client, &async); 40 | async_run_forever(&async); 41 | 42 | return (1); 43 | } 44 | -------------------------------------------------------------------------------- /examples/my_protocol/client/linux/Makefile: -------------------------------------------------------------------------------- 1 | SRC += main.c 2 | SRC += build/my_protocol.c 3 | SRC += build/my_protocol_client.c 4 | SRC += ../../../../3pp/pbtools/lib/src/pbtools.c 5 | SRC += ../../../../lib/src/messi.c 6 | CFLAGS += -Wall 7 | CFLAGS += -Wextra 8 | CFLAGS += -Werror 9 | CFLAGS += -I build 10 | CFLAGS += -I ../../../../lib/include 11 | CFLAGS += -I ../../../../3pp/pbtools/lib/include 12 | 13 | all: 14 | mkdir -p build 15 | PYTHONPATH=../../../.. \ 16 | python3 -m messi generate_c_source \ 17 | -o build -p linux ../../my_protocol.proto 18 | gcc $(CFLAGS) $(SRC) -o client 19 | -------------------------------------------------------------------------------- /examples/my_protocol/client/linux/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | * 26 | * This file is part of the Messi project. 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include "my_protocol_client.h" 33 | 34 | static void on_connected(struct my_protocol_client_t *self_p) 35 | { 36 | printf("Connected. Sending FooReq.\n"); 37 | 38 | my_protocol_client_init_foo_req(self_p); 39 | my_protocol_client_send(self_p); 40 | } 41 | 42 | static void on_disconnected(struct my_protocol_client_t *self_p, 43 | enum messi_disconnect_reason_t disconnect_reason) 44 | { 45 | (void)self_p; 46 | 47 | printf("Disconnected (reason: %s). Exiting.\n", 48 | messi_disconnect_reason_string(disconnect_reason)); 49 | 50 | exit(0); 51 | 52 | } 53 | 54 | static void on_foo_rsp(struct my_protocol_client_t *self_p, 55 | struct my_protocol_foo_rsp_t *message_p) 56 | { 57 | (void)message_p; 58 | 59 | printf("Got FooRsp. Sending BarInd twice.\n"); 60 | 61 | my_protocol_client_init_bar_ind(self_p); 62 | my_protocol_client_send(self_p); 63 | my_protocol_client_send(self_p); 64 | } 65 | 66 | static void on_fie_req(struct my_protocol_client_t *self_p, 67 | struct my_protocol_fie_req_t *message_p) 68 | { 69 | (void)message_p; 70 | 71 | printf("Got FieReq. Sending FieRsp.\n"); 72 | 73 | my_protocol_client_init_fie_rsp(self_p); 74 | my_protocol_client_send(self_p); 75 | } 76 | 77 | int main() 78 | { 79 | struct my_protocol_client_t client; 80 | int epoll_fd; 81 | struct epoll_event event; 82 | int res; 83 | uint8_t encoded_in[128]; 84 | uint8_t encoded_out[128]; 85 | uint8_t workspace_in[128]; 86 | uint8_t workspace_out[128]; 87 | 88 | epoll_fd = epoll_create1(0); 89 | 90 | if (epoll_fd == -1) { 91 | return (1); 92 | } 93 | 94 | res = my_protocol_client_init(&client, 95 | "tcp://127.0.0.1:7840", 96 | &encoded_in[0], 97 | sizeof(encoded_in), 98 | &workspace_in[0], 99 | sizeof(workspace_in), 100 | &encoded_out[0], 101 | sizeof(encoded_out), 102 | &workspace_out[0], 103 | sizeof(workspace_out), 104 | on_connected, 105 | on_disconnected, 106 | on_foo_rsp, 107 | on_fie_req, 108 | epoll_fd, 109 | NULL); 110 | 111 | if (res != 0) { 112 | printf("Init failed.\n"); 113 | 114 | return (1); 115 | } 116 | 117 | my_protocol_client_start(&client); 118 | 119 | while (true) { 120 | res = epoll_wait(epoll_fd, &event, 1, -1); 121 | 122 | if (res != 1) { 123 | break; 124 | } 125 | 126 | my_protocol_client_process(&client, event.data.fd, event.events); 127 | } 128 | 129 | return (0); 130 | } 131 | -------------------------------------------------------------------------------- /examples/my_protocol/client/python/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | PYTHONPATH=../../../.. \ 3 | python3 -m messi generate_py_source -I ../.. ../../my_protocol.proto 4 | python3 main.py 5 | -------------------------------------------------------------------------------- /examples/my_protocol/client/python/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import asyncio 3 | 4 | from my_protocol_client import MyProtocolClient 5 | 6 | 7 | class Client(MyProtocolClient): 8 | 9 | async def on_connected(self): 10 | print("Connected. Sending FooReq.") 11 | 12 | self.init_foo_req() 13 | self.send() 14 | 15 | async def on_disconnected(self): 16 | print("Disconnected. Exiting.") 17 | 18 | sys.exit(0) 19 | 20 | async def on_foo_rsp(self, message): 21 | print("Got FooRsp. Sending BarInd twice.") 22 | 23 | self.init_bar_ind() 24 | self.send() 25 | self.send() 26 | 27 | async def on_fie_req(self, message): 28 | print("Got FieReq. Sending FieRsp.") 29 | 30 | self.init_fie_rsp() 31 | self.send() 32 | 33 | 34 | async def main(): 35 | client = Client('tcp://127.0.0.1:7840') 36 | client.start() 37 | 38 | while True: 39 | await asyncio.sleep(10) 40 | 41 | 42 | if __name__ == '__main__': 43 | asyncio.run(main()) 44 | -------------------------------------------------------------------------------- /examples/my_protocol/my_protocol.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | // The protocol name. 4 | package my_protocol; 5 | 6 | // Messages sent from client to server. 7 | message ClientToServer { 8 | oneof messages { 9 | FooReq foo_req = 1; 10 | BarInd bar_ind = 2; 11 | FieRsp fie_rsp = 3; 12 | } 13 | } 14 | 15 | // Messages sent from server to client. 16 | message ServerToClient { 17 | oneof messages { 18 | FooRsp foo_rsp = 1; 19 | FieReq fie_req = 2; 20 | } 21 | } 22 | 23 | // Message definitions. 24 | message FooReq { 25 | } 26 | 27 | message FooRsp { 28 | } 29 | 30 | message BarInd { 31 | } 32 | 33 | message FieReq { 34 | } 35 | 36 | message FieRsp { 37 | } 38 | -------------------------------------------------------------------------------- /examples/my_protocol/server/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(MAKE) -C linux 3 | -------------------------------------------------------------------------------- /examples/my_protocol/server/linux/Makefile: -------------------------------------------------------------------------------- 1 | SRC += main.c 2 | SRC += build/my_protocol.c 3 | SRC += build/my_protocol_server.c 4 | SRC += ../../../../3pp/pbtools/lib/src/pbtools.c 5 | SRC += ../../../../lib/src/messi.c 6 | CFLAGS += -Wall 7 | CFLAGS += -Wextra 8 | CFLAGS += -Werror 9 | CFLAGS += -I build 10 | CFLAGS += -I ../../../../lib/include 11 | CFLAGS += -I ../../../../3pp/pbtools/lib/include 12 | 13 | all: 14 | mkdir -p build 15 | PYTHONPATH=../../../.. \ 16 | python3 -m messi generate_c_source \ 17 | -o build -p linux ../../my_protocol.proto 18 | gcc $(CFLAGS) $(SRC) -o server 19 | -------------------------------------------------------------------------------- /examples/my_protocol/server/linux/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | * 26 | * This file is part of the Messi project. 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include "my_protocol_server.h" 33 | 34 | static void on_foo_req(struct my_protocol_server_t *self_p, 35 | struct my_protocol_server_client_t *client_p, 36 | struct my_protocol_foo_req_t *message_p) 37 | { 38 | (void)client_p; 39 | (void)message_p; 40 | 41 | printf("Got FooReq. Sending FooRsp.\n"); 42 | 43 | my_protocol_server_init_foo_rsp(self_p); 44 | my_protocol_server_reply(self_p); 45 | } 46 | 47 | static void on_bar_ind(struct my_protocol_server_t *self_p, 48 | struct my_protocol_server_client_t *client_p, 49 | struct my_protocol_bar_ind_t *message_p) 50 | { 51 | (void)client_p; 52 | (void)message_p; 53 | 54 | static int count = 0; 55 | 56 | count++; 57 | 58 | if (count < 2) { 59 | printf("Got BarInd.\n"); 60 | } else { 61 | printf("Got BarInd. Sending FieReq.\n"); 62 | my_protocol_server_init_fie_req(self_p); 63 | my_protocol_server_reply(self_p); 64 | } 65 | } 66 | 67 | static void on_fie_rsp(struct my_protocol_server_t *self_p, 68 | struct my_protocol_server_client_t *client_p, 69 | struct my_protocol_fie_rsp_t *message_p) 70 | { 71 | (void)client_p; 72 | (void)message_p; 73 | 74 | printf("Got FieRsp. Disconnecting the client and exiting.\n"); 75 | 76 | my_protocol_server_disconnect(self_p, NULL); 77 | exit(0); 78 | } 79 | 80 | int main() 81 | { 82 | struct my_protocol_server_t server; 83 | struct my_protocol_server_client_t clients[1]; 84 | uint8_t clients_input_buffers[1][128]; 85 | uint8_t message[128]; 86 | uint8_t workspace_in[128]; 87 | uint8_t workspace_out[128]; 88 | int epoll_fd; 89 | struct epoll_event event; 90 | int res; 91 | 92 | epoll_fd = epoll_create1(0); 93 | 94 | if (epoll_fd == -1) { 95 | return (1); 96 | } 97 | 98 | res = my_protocol_server_init(&server, 99 | "tcp://127.0.0.1:7840", 100 | &clients[0], 101 | 1, 102 | &clients_input_buffers[0][0], 103 | sizeof(clients_input_buffers[0]), 104 | &message[0], 105 | sizeof(message), 106 | &workspace_in[0], 107 | sizeof(workspace_in), 108 | &workspace_out[0], 109 | sizeof(workspace_out), 110 | NULL, 111 | NULL, 112 | on_foo_req, 113 | on_bar_ind, 114 | on_fie_rsp, 115 | epoll_fd, 116 | NULL); 117 | 118 | if (res != 0) { 119 | printf("Init failed.\n"); 120 | 121 | return (1); 122 | } 123 | 124 | res = my_protocol_server_start(&server); 125 | 126 | if (res != 0) { 127 | printf("Start failed.\n"); 128 | 129 | return (1); 130 | } 131 | 132 | printf("Server started.\n"); 133 | 134 | while (true) { 135 | res = epoll_wait(epoll_fd, &event, 1, -1); 136 | 137 | if (res != 1) { 138 | break; 139 | } 140 | 141 | my_protocol_server_process(&server, event.data.fd, event.events); 142 | } 143 | 144 | return (1); 145 | } 146 | -------------------------------------------------------------------------------- /lib/Makefile: -------------------------------------------------------------------------------- 1 | CC = $(CROSS_COMPILE)gcc 2 | AR = $(CROSS_COMPILE)ar 3 | 4 | LIBRARY = libmessi.a 5 | PREFIX ?= /usr/local 6 | 7 | .PHONY: library install clean 8 | 9 | library: $(LIBRARY) 10 | 11 | install: 12 | find include -type f -exec install -Dm 644 "{}" "$(PREFIX)/{}" \; 13 | mkdir -p $(PREFIX)/lib 14 | install -m 644 $(LIBRARY) $(PREFIX)/lib 15 | 16 | clean: 17 | rm $(LIBRARY) 18 | 19 | $(LIBRARY): 20 | $(CC) $(CFLAGS_EXTRA) -Wall -O2 -Iinclude src/messi.c -c -o messi.o 21 | $(AR) cr $(LIBRARY) messi.o 22 | -------------------------------------------------------------------------------- /lib/include/messi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /* This file was generated by Messi. */ 28 | 29 | #ifndef MESSI_H 30 | #define MESSI_H 31 | 32 | #include 33 | #include 34 | 35 | /* Message types. */ 36 | #define MESSI_MESSAGE_TYPE_CLIENT_TO_SERVER_USER 1 37 | #define MESSI_MESSAGE_TYPE_SERVER_TO_CLIENT_USER 2 38 | #define MESSI_MESSAGE_TYPE_PING 3 39 | #define MESSI_MESSAGE_TYPE_PONG 4 40 | 41 | typedef int (*messi_epoll_ctl_t)(int epoll_fd, int op, int fd, uint32_t events); 42 | 43 | enum messi_disconnect_reason_t { 44 | messi_disconnect_reason_message_encode_error_t = 0, 45 | messi_disconnect_reason_message_decode_error_t, 46 | messi_disconnect_reason_connection_closed_t, 47 | messi_disconnect_reason_keep_alive_timeout_t, 48 | messi_disconnect_reason_general_error_t, 49 | messi_disconnect_reason_message_too_big_t 50 | }; 51 | 52 | struct messi_buffer_t { 53 | uint8_t *buf_p; 54 | size_t size; 55 | }; 56 | 57 | struct messi_header_t { 58 | uint8_t type; 59 | uint8_t size[3]; 60 | } __attribute__ ((packed)); 61 | 62 | static inline void messi_header_set_size(struct messi_header_t *header_p, 63 | uint32_t size) 64 | { 65 | header_p->size[0] = (size >> 16); 66 | header_p->size[1] = (size >> 8); 67 | header_p->size[2] = (size >> 0); 68 | } 69 | 70 | static inline uint32_t messi_header_get_size(struct messi_header_t *header_p) 71 | { 72 | return (((uint32_t)header_p->size[0] << 16) 73 | | ((uint32_t)header_p->size[1] << 8) 74 | | ((uint32_t)header_p->size[2] << 0)); 75 | } 76 | 77 | void messi_header_create(struct messi_header_t *header_p, 78 | uint8_t message_type, 79 | uint32_t size); 80 | 81 | int messi_epoll_ctl_default(int epoll_fd, int op, int fd, uint32_t events); 82 | 83 | int messi_make_non_blocking(int fd); 84 | 85 | /** 86 | * Parse tcp://:. Returns zero(0) if successful. 87 | */ 88 | int messi_parse_tcp_uri(const char *uri_p, 89 | char *host_p, 90 | size_t host_size, 91 | int *port_p); 92 | 93 | /** 94 | * Get the string for given disconnect reason. 95 | */ 96 | const char *messi_disconnect_reason_string( 97 | enum messi_disconnect_reason_t disconnect_reason); 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /lib/src/messi.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /* This file was generated by Messi. */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "messi.h" 34 | 35 | void messi_header_create(struct messi_header_t *header_p, 36 | uint8_t message_type, 37 | uint32_t size) 38 | { 39 | header_p->type = message_type; 40 | messi_header_set_size(header_p, size); 41 | } 42 | 43 | int messi_epoll_ctl_default(int epoll_fd, int op, int fd, uint32_t events) 44 | { 45 | struct epoll_event event; 46 | 47 | event.data.fd = fd; 48 | event.events = events; 49 | 50 | return (epoll_ctl(epoll_fd, op, fd, &event)); 51 | } 52 | 53 | int messi_make_non_blocking(int fd) 54 | { 55 | return (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK)); 56 | } 57 | 58 | int messi_parse_tcp_uri(const char *uri_p, 59 | char *host_p, 60 | size_t host_size, 61 | int *port_p) 62 | { 63 | const char *colon_p; 64 | size_t size; 65 | 66 | if (strncmp(uri_p, "tcp://", 6) != 0) { 67 | return (-1); 68 | } 69 | 70 | uri_p += 6; 71 | 72 | /* Host. */ 73 | colon_p = strchr(uri_p, ':'); 74 | 75 | if (colon_p == NULL) { 76 | return (-1); 77 | } 78 | 79 | size = (colon_p - uri_p); 80 | 81 | if ((size + 1) > host_size) { 82 | return (-1); 83 | } 84 | 85 | strncpy(host_p, uri_p, size); 86 | host_p[size] = '\0'; 87 | 88 | /* Port. */ 89 | *port_p = atoi(&colon_p[1]); 90 | 91 | return (0); 92 | } 93 | 94 | const char *messi_disconnect_reason_string( 95 | enum messi_disconnect_reason_t disconnect_reason) 96 | { 97 | const char *res_p; 98 | 99 | switch (disconnect_reason) { 100 | 101 | case messi_disconnect_reason_message_encode_error_t: 102 | res_p = "Message encode error."; 103 | break; 104 | 105 | case messi_disconnect_reason_message_decode_error_t: 106 | res_p = "Message decode error."; 107 | break; 108 | 109 | case messi_disconnect_reason_connection_closed_t: 110 | res_p = "Connection closed."; 111 | break; 112 | 113 | case messi_disconnect_reason_keep_alive_timeout_t: 114 | res_p = "Keep alive timeout."; 115 | break; 116 | 117 | case messi_disconnect_reason_general_error_t: 118 | res_p = "General error."; 119 | break; 120 | 121 | case messi_disconnect_reason_message_too_big_t: 122 | res_p = "Message too big."; 123 | break; 124 | 125 | default: 126 | res_p = "*** Unknown ***"; 127 | break; 128 | } 129 | 130 | return (res_p); 131 | } 132 | -------------------------------------------------------------------------------- /messi/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import argparse 3 | 4 | from .version import __version__ 5 | from . import c_source 6 | from . import py_source 7 | 8 | 9 | def do_generate_c_source(args): 10 | c_source.generate_files(args.infiles, 11 | args.platform, 12 | args.side, 13 | args.import_path, 14 | args.output_directory) 15 | 16 | 17 | def do_generate_py_source(args): 18 | py_source.generate_files(args.infiles, 19 | args.side, 20 | args.import_path, 21 | args.output_directory) 22 | 23 | 24 | def main(): 25 | parser = argparse.ArgumentParser(description='Messi command line utility') 26 | parser.add_argument('-d', '--debug', action='store_true') 27 | parser.add_argument('--version', 28 | action='version', 29 | version=__version__, 30 | help='Print version information and exit.') 31 | 32 | # Python 3 workaround for help output if subcommand is missing. 33 | subparsers = parser.add_subparsers(dest='one of the above') 34 | subparsers.required = True 35 | 36 | # C generate subparser. 37 | subparser = subparsers.add_parser('generate_c_source', 38 | help='Generate C source code.') 39 | subparser.add_argument( 40 | '-p', '--platform', 41 | choices=('linux', 'async'), 42 | default='linux', 43 | help='Platform to generate code for (default: %(default)s).') 44 | subparser.add_argument( 45 | '-s', '--side', 46 | choices=('both', 'client', 'server'), 47 | default='both', 48 | help='Side to generate code for (default: %(default)s).') 49 | subparser.add_argument('-I', '--import-path', 50 | action='append', 51 | default=[], 52 | help='Path(s) where to search for imports.') 53 | subparser.add_argument('-o', '--output-directory', 54 | default='.', 55 | help='Output directory (default: %(default)s).') 56 | subparser.add_argument('infiles', 57 | nargs='+', 58 | help='Input protobuf file(s).') 59 | subparser.set_defaults(func=do_generate_c_source) 60 | 61 | # Python generate subparser. 62 | subparser = subparsers.add_parser('generate_py_source', 63 | help='Generate Python source code.') 64 | subparser.add_argument( 65 | '-s', '--side', 66 | choices=('both', 'client', 'server'), 67 | default='both', 68 | help='Side to generate code for (default: %(default)s).') 69 | subparser.add_argument('-I', '--import-path', 70 | action='append', 71 | default=[], 72 | help='Path(s) where to search for imports.') 73 | subparser.add_argument('-o', '--output-directory', 74 | default='.', 75 | help='Output directory (default: %(default)s).') 76 | subparser.add_argument('infiles', 77 | nargs='+', 78 | help='Input protobuf file(s).') 79 | subparser.set_defaults(func=do_generate_py_source) 80 | 81 | args = parser.parse_args() 82 | 83 | if args.debug: 84 | args.func(args) 85 | else: 86 | try: 87 | args.func(args) 88 | except BaseException as e: 89 | sys.exit(f'error: {e}') 90 | -------------------------------------------------------------------------------- /messi/__main__.py: -------------------------------------------------------------------------------- 1 | # Execute as "python -m messi" 2 | 3 | from . import main 4 | 5 | main() 6 | -------------------------------------------------------------------------------- /messi/c_source/templates/async/client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /* This file was generated by Messi. */ 28 | 29 | #ifndef NAME_UPPER_CLIENT_H 30 | #define NAME_UPPER_CLIENT_H 31 | 32 | #include 33 | #include "messi.h" 34 | #include "NAME.h" 35 | #include "async.h" 36 | 37 | struct NAME_client_t; 38 | 39 | typedef void (*NAME_client_on_connected_t)(struct NAME_client_t *self_p); 40 | 41 | typedef void (*NAME_client_on_disconnected_t)( 42 | struct NAME_client_t *self_p, 43 | enum messi_disconnect_reason_t disconnect_reason); 44 | 45 | ON_MESSAGE_TYPEDEFS 46 | enum NAME_client_input_state_t { 47 | NAME_client_input_state_header_t = 0, 48 | NAME_client_input_state_payload_t 49 | }; 50 | 51 | struct NAME_client_t { 52 | struct { 53 | char address[16]; 54 | int port; 55 | } server; 56 | bool is_connected; 57 | enum messi_disconnect_reason_t disconnect_reason; 58 | NAME_client_on_connected_t on_connected; 59 | NAME_client_on_disconnected_t on_disconnected; 60 | struct async_t *async_p; 61 | ON_MESSAGE_MEMBERS 62 | struct async_stcp_client_t stcp; 63 | struct async_timer_t keep_alive_timer; 64 | struct async_timer_t reconnect_timer; 65 | bool pong_received; 66 | struct { 67 | struct NAME_server_to_client_t *message_p; 68 | struct messi_buffer_t workspace; 69 | struct { 70 | struct messi_buffer_t data; 71 | size_t size; 72 | size_t left; 73 | enum NAME_client_input_state_t state; 74 | } encoded; 75 | } input; 76 | struct { 77 | struct NAME_client_to_server_t *message_p; 78 | struct messi_buffer_t workspace; 79 | struct messi_buffer_t encoded; 80 | } output; 81 | }; 82 | 83 | /** 84 | * Initialize given client. 85 | */ 86 | int NAME_client_init( 87 | struct NAME_client_t *self_p, 88 | const char *server_uri_p, 89 | uint8_t *encoded_in_buf_p, 90 | size_t encoded_in_size, 91 | uint8_t *workspace_in_buf_p, 92 | size_t workspace_in_size, 93 | uint8_t *encoded_out_buf_p, 94 | size_t encoded_out_size, 95 | uint8_t *workspace_out_buf_p, 96 | size_t workspace_out_size, 97 | NAME_client_on_connected_t on_connected, 98 | NAME_client_on_disconnected_t on_disconnected, 99 | ON_MESSAGE_PARAMS 100 | struct async_t *async_p); 101 | 102 | /** 103 | * Connect to the server. The connected callback is called once 104 | * connected. Automatic reconnect if disconnected. 105 | */ 106 | void NAME_client_start(struct NAME_client_t *self_p); 107 | 108 | /** 109 | * Disconnect from the server. Call start to connect again. 110 | */ 111 | void NAME_client_stop(struct NAME_client_t *self_p); 112 | 113 | /** 114 | * Send prepared message the server. 115 | */ 116 | void NAME_client_send(struct NAME_client_t *self_p); 117 | 118 | INIT_MESSAGES 119 | #endif 120 | -------------------------------------------------------------------------------- /messi/c_source/templates/async/server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /* This file was generated by Messi. */ 28 | 29 | #ifndef NAME_UPPER_SERVER_H 30 | #define NAME_UPPER_SERVER_H 31 | 32 | #include 33 | #include "async.h" 34 | #include "messi.h" 35 | #include "NAME.h" 36 | 37 | struct NAME_server_t; 38 | struct NAME_server_client_t; 39 | 40 | ON_MESSAGE_TYPEDEFS 41 | enum NAME_server_client_input_state_t { 42 | NAME_server_client_input_state_header_t = 0, 43 | NAME_server_client_input_state_payload_t 44 | }; 45 | 46 | typedef void (*NAME_server_on_client_connected_t)( 47 | struct NAME_server_t *self_p, 48 | struct NAME_server_client_t *client_p); 49 | 50 | typedef void (*NAME_server_on_client_disconnected_t)( 51 | struct NAME_server_t *self_p, 52 | struct NAME_server_client_t *client_p); 53 | 54 | struct NAME_server_t { 55 | struct { 56 | char address[16]; 57 | int port; 58 | } server; 59 | NAME_server_on_client_connected_t on_client_connected; 60 | NAME_server_on_client_disconnected_t on_client_disconnected; 61 | ON_MESSAGE_MEMBERS 62 | struct async_stcp_server_t stcp; 63 | struct NAME_server_client_t *current_client_p; 64 | struct { 65 | struct NAME_server_client_t *connected_list_p; 66 | struct NAME_server_client_t *free_list_p; 67 | struct NAME_server_client_t *pending_disconnect_list_p; 68 | size_t input_buffer_size; 69 | } clients; 70 | struct { 71 | struct NAME_client_to_server_t *message_p; 72 | struct messi_buffer_t workspace; 73 | } input; 74 | struct { 75 | struct NAME_server_to_client_t *message_p; 76 | struct messi_buffer_t workspace; 77 | struct messi_buffer_t encoded; 78 | } output; 79 | struct async_t *async_p; 80 | }; 81 | 82 | struct NAME_server_client_t { 83 | struct NAME_server_t *server_p; 84 | struct async_stcp_server_client_t stcp; 85 | struct async_timer_t keep_alive_timer; 86 | struct { 87 | enum NAME_server_client_input_state_t state; 88 | struct messi_buffer_t data; 89 | size_t size; 90 | size_t left; 91 | } input; 92 | struct NAME_server_client_t *next_p; 93 | struct NAME_server_client_t *prev_p; 94 | }; 95 | 96 | /** 97 | * Initialize given server. 98 | */ 99 | int NAME_server_init( 100 | struct NAME_server_t *self_p, 101 | const char *server_uri_p, 102 | struct NAME_server_client_t *clients_p, 103 | int clients_max, 104 | uint8_t *clients_input_bufs_p, 105 | size_t client_input_size, 106 | uint8_t *message_buf_p, 107 | size_t message_size, 108 | uint8_t *workspace_in_buf_p, 109 | size_t workspace_in_size, 110 | uint8_t *workspace_out_buf_p, 111 | size_t workspace_out_size, 112 | NAME_server_on_client_connected_t on_connected, 113 | NAME_server_on_client_disconnected_t on_disconnected, 114 | ON_MESSAGE_PARAMS 115 | struct async_t *async_p); 116 | 117 | /** 118 | * Start serving clients. 119 | */ 120 | void NAME_server_start(struct NAME_server_t *self_p); 121 | 122 | /** 123 | * Stop serving clients. 124 | */ 125 | void NAME_server_stop(struct NAME_server_t *self_p); 126 | 127 | /** 128 | * Send prepared message to given client. 129 | */ 130 | void NAME_server_send( 131 | struct NAME_server_t *self_p, 132 | struct NAME_server_client_t *client_p); 133 | 134 | /** 135 | * Send prepared message to current client. 136 | */ 137 | void NAME_server_reply(struct NAME_server_t *self_p); 138 | 139 | /** 140 | * Broadcast prepared message to all clients. 141 | */ 142 | void NAME_server_broadcast(struct NAME_server_t *self_p); 143 | 144 | /** 145 | * Disconnect given client. If given client is NULL, the currect 146 | * client is disconnected. 147 | */ 148 | void NAME_server_disconnect( 149 | struct NAME_server_t *self_p, 150 | struct NAME_server_client_t *client_p); 151 | 152 | INIT_MESSAGES 153 | #endif 154 | -------------------------------------------------------------------------------- /messi/c_source/templates/linux/client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /* This file was generated by Messi. */ 28 | 29 | #ifndef NAME_UPPER_CLIENT_H 30 | #define NAME_UPPER_CLIENT_H 31 | 32 | #include 33 | #include "messi.h" 34 | #include "NAME.h" 35 | 36 | struct NAME_client_t; 37 | 38 | typedef void (*NAME_client_on_connected_t)(struct NAME_client_t *self_p); 39 | 40 | typedef void (*NAME_client_on_disconnected_t)( 41 | struct NAME_client_t *self_p, 42 | enum messi_disconnect_reason_t disconnect_reason); 43 | 44 | ON_MESSAGE_TYPEDEFS 45 | enum NAME_client_input_state_t { 46 | NAME_client_input_state_header_t = 0, 47 | NAME_client_input_state_payload_t 48 | }; 49 | 50 | struct NAME_client_t { 51 | struct { 52 | char address[16]; 53 | int port; 54 | } server; 55 | enum messi_disconnect_reason_t disconnect_reason; 56 | NAME_client_on_connected_t on_connected; 57 | NAME_client_on_disconnected_t on_disconnected; 58 | ON_MESSAGE_MEMBERS 59 | int epoll_fd; 60 | messi_epoll_ctl_t epoll_ctl; 61 | int server_fd; 62 | int keep_alive_timer_fd; 63 | int reconnect_timer_fd; 64 | bool pong_received; 65 | bool pending_disconnect; 66 | struct { 67 | struct NAME_server_to_client_t *message_p; 68 | struct messi_buffer_t workspace; 69 | struct { 70 | struct messi_buffer_t data; 71 | size_t size; 72 | size_t left; 73 | enum NAME_client_input_state_t state; 74 | } encoded; 75 | } input; 76 | struct { 77 | struct NAME_client_to_server_t *message_p; 78 | struct messi_buffer_t workspace; 79 | struct messi_buffer_t encoded; 80 | } output; 81 | }; 82 | 83 | /** 84 | * Initialize given client. 85 | */ 86 | int NAME_client_init( 87 | struct NAME_client_t *self_p, 88 | const char *server_uri_p, 89 | uint8_t *encoded_in_buf_p, 90 | size_t encoded_in_size, 91 | uint8_t *workspace_in_buf_p, 92 | size_t workspace_in_size, 93 | uint8_t *encoded_out_buf_p, 94 | size_t encoded_out_size, 95 | uint8_t *workspace_out_buf_p, 96 | size_t workspace_out_size, 97 | NAME_client_on_connected_t on_connected, 98 | NAME_client_on_disconnected_t on_disconnected, 99 | ON_MESSAGE_PARAMS 100 | int epoll_fd, 101 | messi_epoll_ctl_t epoll_ctl); 102 | 103 | /** 104 | * Connect to the server. The connected callback is called once 105 | * connected. Automatic reconnect if disconnected. 106 | */ 107 | void NAME_client_start(struct NAME_client_t *self_p); 108 | 109 | /** 110 | * Disconnect from the server. Call start to connect again. 111 | */ 112 | void NAME_client_stop(struct NAME_client_t *self_p); 113 | 114 | /** 115 | * Process all pending events on given file descriptor (if it belongs 116 | * to given client). 117 | */ 118 | void NAME_client_process( 119 | struct NAME_client_t *self_p, 120 | int fd, 121 | uint32_t events); 122 | 123 | /** 124 | * Send prepared message the server. 125 | */ 126 | void NAME_client_send(struct NAME_client_t *self_p); 127 | 128 | INIT_MESSAGES 129 | #endif 130 | -------------------------------------------------------------------------------- /messi/c_source/templates/linux/server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /* This file was generated by Messi. */ 28 | 29 | #ifndef NAME_UPPER_SERVER_H 30 | #define NAME_UPPER_SERVER_H 31 | 32 | #include 33 | #include "messi.h" 34 | #include "NAME.h" 35 | 36 | struct NAME_server_t; 37 | struct NAME_server_client_t; 38 | 39 | ON_MESSAGE_TYPEDEFS 40 | enum NAME_server_client_input_state_t { 41 | NAME_server_client_input_state_header_t = 0, 42 | NAME_server_client_input_state_payload_t 43 | }; 44 | 45 | typedef void (*NAME_server_on_client_connected_t)( 46 | struct NAME_server_t *self_p, 47 | struct NAME_server_client_t *client_p); 48 | 49 | typedef void (*NAME_server_on_client_disconnected_t)( 50 | struct NAME_server_t *self_p, 51 | struct NAME_server_client_t *client_p); 52 | 53 | struct NAME_server_t { 54 | struct { 55 | char address[16]; 56 | int port; 57 | } server; 58 | NAME_server_on_client_connected_t on_client_connected; 59 | NAME_server_on_client_disconnected_t on_client_disconnected; 60 | ON_MESSAGE_MEMBERS 61 | int epoll_fd; 62 | messi_epoll_ctl_t epoll_ctl; 63 | int listener_fd; 64 | struct NAME_server_client_t *current_client_p; 65 | struct { 66 | struct NAME_server_client_t *connected_list_p; 67 | struct NAME_server_client_t *free_list_p; 68 | struct NAME_server_client_t *pending_disconnect_list_p; 69 | size_t input_buffer_size; 70 | } clients; 71 | struct { 72 | struct NAME_client_to_server_t *message_p; 73 | struct messi_buffer_t workspace; 74 | } input; 75 | struct { 76 | struct NAME_server_to_client_t *message_p; 77 | struct messi_buffer_t workspace; 78 | struct messi_buffer_t encoded; 79 | } output; 80 | }; 81 | 82 | struct NAME_server_client_output_item_t { 83 | size_t offset; 84 | size_t size; 85 | struct NAME_server_client_output_item_t *next_p; 86 | uint8_t data[1]; 87 | }; 88 | 89 | struct NAME_server_client_t { 90 | int client_fd; 91 | int keep_alive_timer_fd; 92 | struct { 93 | enum NAME_server_client_input_state_t state; 94 | struct messi_buffer_t data; 95 | size_t size; 96 | size_t left; 97 | } input; 98 | struct { 99 | struct NAME_server_client_output_item_t *head_p; 100 | struct NAME_server_client_output_item_t *tail_p; 101 | } output; 102 | struct NAME_server_client_t *next_p; 103 | struct NAME_server_client_t *prev_p; 104 | }; 105 | 106 | /** 107 | * Initialize given server. 108 | */ 109 | int NAME_server_init( 110 | struct NAME_server_t *self_p, 111 | const char *server_uri_p, 112 | struct NAME_server_client_t *clients_p, 113 | int clients_max, 114 | uint8_t *clients_input_bufs_p, 115 | size_t client_input_size, 116 | uint8_t *message_buf_p, 117 | size_t message_size, 118 | uint8_t *workspace_in_buf_p, 119 | size_t workspace_in_size, 120 | uint8_t *workspace_out_buf_p, 121 | size_t workspace_out_size, 122 | NAME_server_on_client_connected_t on_client_connected, 123 | NAME_server_on_client_disconnected_t on_client_disconnected, 124 | ON_MESSAGE_PARAMS 125 | int epoll_fd, 126 | messi_epoll_ctl_t epoll_ctl); 127 | 128 | /** 129 | * Start serving clients. 130 | */ 131 | int NAME_server_start(struct NAME_server_t *self_p); 132 | 133 | /** 134 | * Stop serving clients. 135 | */ 136 | void NAME_server_stop(struct NAME_server_t *self_p); 137 | 138 | /** 139 | * Process any pending events on given file descriptor if it belongs 140 | * to given server. 141 | */ 142 | void NAME_server_process(struct NAME_server_t *self_p, int fd, uint32_t events); 143 | 144 | /** 145 | * Send prepared message to given client. 146 | */ 147 | void NAME_server_send( 148 | struct NAME_server_t *self_p, 149 | struct NAME_server_client_t *client_p); 150 | 151 | /** 152 | * Send prepared message to current client. 153 | */ 154 | void NAME_server_reply(struct NAME_server_t *self_p); 155 | 156 | /** 157 | * Broadcast prepared message to all clients. 158 | */ 159 | void NAME_server_broadcast(struct NAME_server_t *self_p); 160 | 161 | /** 162 | * Disconnect given client. If given client is NULL, the currect 163 | * client is disconnected. 164 | */ 165 | void NAME_server_disconnect( 166 | struct NAME_server_t *self_p, 167 | struct NAME_server_client_t *client_p); 168 | 169 | INIT_MESSAGES 170 | #endif 171 | -------------------------------------------------------------------------------- /messi/generate.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pbtools.parser import parse_file 3 | from pbtools.parser import camel_to_snake_case 4 | 5 | 6 | def make_format(mo): 7 | value = mo.group(0) 8 | 9 | if value in '{}': 10 | return 2 * value 11 | else: 12 | return f'{{{value.lower()}}}' 13 | 14 | 15 | def get_messages(message): 16 | if len(message.oneofs) != 1 or message.messages: 17 | raise Exception( 18 | f'The {message.name} message does not contain exactly ' 19 | f'one oneof.') 20 | 21 | oneof = message.oneofs[0] 22 | 23 | if oneof.name != 'messages': 24 | raise Exception( 25 | f'The oneof in {message.name} must be called messages, not ' 26 | f'{oneof.name}.') 27 | 28 | return oneof.fields 29 | 30 | 31 | class Generator: 32 | 33 | RE_TEMPLATE_TO_FORMAT = None 34 | 35 | def __init__(self, filename, side, import_path, output_directory): 36 | parsed = parse_file(filename, import_path) 37 | basename = os.path.basename(filename) 38 | self.name = camel_to_snake_case(os.path.splitext(basename)[0]) 39 | self.client_side = False 40 | self.server_side = False 41 | 42 | if side == 'both': 43 | self.client_side = True 44 | self.server_side = True 45 | elif side == 'client': 46 | self.client_side = True 47 | elif side == 'server': 48 | self.server_side = True 49 | 50 | self.output_directory = output_directory 51 | self.client_to_server_messages = [] 52 | self.server_to_client_messages = [] 53 | 54 | for message in parsed.messages: 55 | if message.name == 'ClientToServer': 56 | self.client_to_server_messages = get_messages(message) 57 | elif message.name == 'ServerToClient': 58 | self.server_to_client_messages = get_messages(message) 59 | 60 | def read_template_file(self, filename): 61 | with open(os.path.join(self.templates_dir, filename)) as fin: 62 | return self.RE_TEMPLATE_TO_FORMAT.sub(make_format, fin.read()) 63 | 64 | def create_file(self, filename, data): 65 | with open(os.path.join(self.output_directory, filename), 'w') as fout: 66 | fout.write(data) 67 | 68 | def generate_client_files(self): 69 | pass 70 | 71 | def generate_server_files(self): 72 | pass 73 | 74 | def generate_files(self): 75 | if not self.client_to_server_messages: 76 | return 77 | 78 | if not self.server_to_client_messages: 79 | return 80 | 81 | if self.client_side: 82 | self.generate_client_files() 83 | 84 | if self.server_side: 85 | self.generate_server_files() 86 | -------------------------------------------------------------------------------- /messi/py_source/__init__.py: -------------------------------------------------------------------------------- 1 | import re 2 | import os 3 | from grpc_tools import protoc 4 | from .. import generate 5 | 6 | 7 | SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) 8 | 9 | ON_MESSAGE = '''\ 10 | async def on_{message.name}(self, message): 11 | """Called when a {message.name} message is received from the server. 12 | 13 | """ 14 | ''' 15 | 16 | INIT_MESSAGE = '''\ 17 | def init_{message.name}(self): 18 | """Prepare a {message.name} message. Call `send()` to send it. 19 | 20 | """ 21 | 22 | self._output = {name}_pb2.ClientToServer() 23 | self._output.{message.name}.SetInParent() 24 | 25 | return self._output.{message.name} 26 | ''' 27 | 28 | HANDLE_MESSAGE = '''\ 29 | elif choice == '{message.name}': 30 | await self.on_{message.name}(message.{message.name})\ 31 | ''' 32 | 33 | 34 | class Generator(generate.Generator): 35 | 36 | RE_TEMPLATE_TO_FORMAT = re.compile(r'{' 37 | r'|}' 38 | r'|NAME_TITLE' 39 | r'|NAME' 40 | r'|ON_MESSAGES' 41 | r'|INIT_MESSAGES' 42 | r'|HANDLE_MESSAGES') 43 | 44 | def __init__(self, filename, side, import_paths, output_directory): 45 | super().__init__(filename, side, import_paths, output_directory) 46 | self.templates_dir = os.path.join(SCRIPT_DIR, 'templates') 47 | 48 | def generate_client(self): 49 | on_messages = [] 50 | init_messages = [] 51 | handle_messages = [] 52 | 53 | for message in self.client_to_server_messages: 54 | init_messages.append(INIT_MESSAGE.format(name=self.name, 55 | message=message)) 56 | 57 | for message in self.server_to_client_messages: 58 | on_messages.append(ON_MESSAGE.format(message=message)) 59 | handle_messages.append(HANDLE_MESSAGE.format(message=message)) 60 | 61 | name_title = self.name.title().replace('_', '') 62 | handle_messages = 8 * ' ' + '\n'.join(handle_messages)[10:] 63 | client_py = self.read_template_file('client.py') 64 | 65 | return client_py.format(name=self.name, 66 | name_title=name_title, 67 | on_messages='\n'.join(on_messages), 68 | init_messages='\n'.join(init_messages), 69 | handle_messages=handle_messages) 70 | 71 | def generate_client_files(self): 72 | self.create_file(f'{self.name}_client.py', self.generate_client()) 73 | 74 | 75 | def generate_protobuf_files(infiles, import_paths, output_directory): 76 | command = ['protoc', f'--python_out={output_directory}'] 77 | 78 | if import_paths is not None: 79 | for path in import_paths: 80 | command += ['-I', path] 81 | 82 | command += infiles 83 | 84 | result = protoc.main(command) 85 | 86 | if result != 0: 87 | raise Exception(f'protoc failed with exit code {result}.') 88 | 89 | 90 | def generate_files(infiles, side, import_paths=None, output_directory='.'): 91 | """Generate Python source code from given proto-file(s). 92 | 93 | """ 94 | 95 | generate_protobuf_files(infiles, import_paths, output_directory) 96 | 97 | for filename in infiles: 98 | generator = Generator(filename, side, import_paths, output_directory) 99 | generator.generate_files() 100 | -------------------------------------------------------------------------------- /messi/py_source/templates/client.py: -------------------------------------------------------------------------------- 1 | # This file was generated by Messi. 2 | 3 | import asyncio 4 | import logging 5 | import bitstruct 6 | 7 | import NAME_pb2 8 | 9 | 10 | LOGGER = logging.getLogger(__name__) 11 | 12 | CF_HEADER = bitstruct.compile('u8u24') 13 | 14 | 15 | class MessageType: 16 | 17 | CLIENT_TO_SERVER_USER = 1 18 | SERVER_TO_CLIENT_USER = 2 19 | PING = 3 20 | PONG = 4 21 | 22 | 23 | def parse_tcp_uri(uri): 24 | """Parse tcp://:. 25 | 26 | """ 27 | 28 | try: 29 | if uri[:6] != 'tcp://': 30 | raise ValueError 31 | 32 | address, port = uri[6:].split(':') 33 | 34 | return address, int(port) 35 | except (ValueError, TypeError): 36 | raise ValueError( 37 | f"Expected URI on the form tcp://:, but got '{uri}'.") 38 | 39 | 40 | class NAME_TITLEClient: 41 | 42 | def __init__(self, uri, keep_alive_interval=2, connect_timeout=5): 43 | self._address, self._port = parse_tcp_uri(uri) 44 | self._uri = uri 45 | self._keep_alive_interval = keep_alive_interval 46 | self._connect_timeout = connect_timeout 47 | self._reader = None 48 | self._writer = None 49 | self._task = None 50 | self._keep_alive_task = None 51 | self._pong_event = None 52 | self._output = None 53 | 54 | def start(self): 55 | """Connect to the server. `on_connected()` is called once 56 | connected. Automatic reconnect if disconnected. 57 | 58 | """ 59 | 60 | if self._task is None: 61 | self._task = asyncio.create_task(self._main()) 62 | 63 | def stop(self): 64 | """Disconnect from the server (if connected). Call `start()` to 65 | connect again. 66 | 67 | """ 68 | 69 | if self._task is not None: 70 | self._task.cancel() 71 | self._task = None 72 | 73 | def send(self): 74 | """Send prepared message to the server. 75 | 76 | """ 77 | 78 | encoded = self._output.SerializeToString() 79 | header = CF_HEADER.pack(MessageType.CLIENT_TO_SERVER_USER, len(encoded)) 80 | 81 | if self._writer is not None: 82 | self._writer.write(header + encoded) 83 | 84 | async def on_connected(self): 85 | """Called when connected to the server. 86 | 87 | """ 88 | 89 | async def on_disconnected(self): 90 | """Called when disconnected from the server. 91 | 92 | """ 93 | 94 | async def on_connect_failure(self, exception): 95 | """Called when a connection attempt to the server fails. Returns the 96 | number of seconds to wait before trying to connect again, or 97 | ``None`` never to connect again. 98 | 99 | """ 100 | 101 | if isinstance(exception, asyncio.TimeoutError): 102 | return 0 103 | else: 104 | return 1 105 | 106 | ON_MESSAGES 107 | INIT_MESSAGES 108 | async def _main(self): 109 | while True: 110 | if not await self._connect(): 111 | break 112 | 113 | await self.on_connected() 114 | self._pong_event = asyncio.Event() 115 | self._keep_alive_task = asyncio.create_task(self._keep_alive_main()) 116 | 117 | try: 118 | await self._reader_loop() 119 | except (Exception, asyncio.CancelledError) as e: 120 | LOGGER.info('Reader loop stopped by %r.', e) 121 | self._close() 122 | 123 | self._keep_alive_task.cancel() 124 | await self.on_disconnected() 125 | 126 | async def _connect(self): 127 | """Repeatedly try to connect to the server. Returns ``True`` if a 128 | connection has been established, and ``False`` otherwise. 129 | 130 | """ 131 | 132 | while True: 133 | try: 134 | self._reader, self._writer = await asyncio.wait_for( 135 | asyncio.open_connection(self._address, self._port), 136 | self._connect_timeout) 137 | 138 | return True 139 | except ConnectionRefusedError as e: 140 | LOGGER.info("Connection refused.") 141 | delay = await self.on_connect_failure(e) 142 | except asyncio.TimeoutError as e: 143 | LOGGER.info("Connect timeout.") 144 | delay = await self.on_connect_failure(e) 145 | except OSError as e: 146 | LOGGER.info("OS error: %s", e) 147 | delay = await self.on_connect_failure(e) 148 | 149 | if delay is None: 150 | return False 151 | 152 | await asyncio.sleep(delay) 153 | 154 | async def _handle_user_message(self, payload): 155 | message = NAME_pb2.ServerToClient() 156 | message.ParseFromString(payload) 157 | choice = message.WhichOneof('messages') 158 | 159 | HANDLE_MESSAGES 160 | 161 | def _handle_pong(self): 162 | self._pong_event.set() 163 | 164 | async def _reader_loop(self): 165 | while True: 166 | header = await self._reader.readexactly(4) 167 | message_type, size = CF_HEADER.unpack(header) 168 | payload = await self._reader.readexactly(size) 169 | 170 | if message_type == MessageType.SERVER_TO_CLIENT_USER: 171 | await self._handle_user_message(payload) 172 | elif message_type == MessageType.PONG: 173 | self._handle_pong() 174 | 175 | async def _keep_alive_loop(self): 176 | while True: 177 | await asyncio.sleep(self._keep_alive_interval) 178 | self._pong_event.clear() 179 | self._writer.write(CF_HEADER.pack(MessageType.PING, 0)) 180 | await asyncio.wait_for(self._pong_event.wait(), 181 | self._keep_alive_interval) 182 | 183 | async def _keep_alive_main(self): 184 | try: 185 | await self._keep_alive_loop() 186 | except (Exception, asyncio.CancelledError) as e: 187 | LOGGER.info('Keep alive task stopped by %r.', e) 188 | self._close() 189 | 190 | def _close(self): 191 | if self._writer is not None: 192 | self._writer.close() 193 | self._writer = None 194 | -------------------------------------------------------------------------------- /messi/version.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.29.0' 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pbtools 2 | nala 3 | bitstruct 4 | grpcio-tools 5 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | from setuptools import find_packages 5 | import re 6 | 7 | 8 | def find_version(): 9 | return re.search(r"^__version__ = '(.*)'$", 10 | open('messi/version.py', 'r').read(), 11 | re.MULTILINE).group(1) 12 | 13 | 14 | setup(name='messi', 15 | version=find_version(), 16 | description='Reliable message passing in distributed systems.', 17 | long_description=open('README.rst', 'r').read(), 18 | author='Erik Moqvist', 19 | author_email='erik.moqvist@gmail.com', 20 | license='MIT', 21 | classifiers=[ 22 | 'License :: OSI Approved :: MIT License', 23 | 'Programming Language :: Python :: 3', 24 | ], 25 | keywords=[], 26 | url='https://github.com/eerimoq/messi', 27 | packages=find_packages(exclude=['tests']), 28 | install_requires=[ 29 | 'pbtools', 30 | 'bitstruct', 31 | 'grpcio-tools' 32 | ], 33 | test_suite="tests", 34 | include_package_data=True, 35 | entry_points = { 36 | 'console_scripts': ['messi=messi.__init__:main'] 37 | }) 38 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eerimoq/messi/f16d2caa0b73c4c4f97f7d5998ddba103d60b9bd/tests/__init__.py -------------------------------------------------------------------------------- /tests/files/chat/async/chat_client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /* This file was generated by Messi. */ 28 | 29 | #ifndef CHAT_CLIENT_H 30 | #define CHAT_CLIENT_H 31 | 32 | #include 33 | #include "messi.h" 34 | #include "chat.h" 35 | #include "async.h" 36 | 37 | struct chat_client_t; 38 | 39 | typedef void (*chat_client_on_connected_t)(struct chat_client_t *self_p); 40 | 41 | typedef void (*chat_client_on_disconnected_t)( 42 | struct chat_client_t *self_p, 43 | enum messi_disconnect_reason_t disconnect_reason); 44 | 45 | typedef void (*chat_client_on_connect_rsp_t)( 46 | struct chat_client_t *self_p, 47 | struct chat_connect_rsp_t *message_p); 48 | 49 | typedef void (*chat_client_on_message_ind_t)( 50 | struct chat_client_t *self_p, 51 | struct chat_message_ind_t *message_p); 52 | 53 | enum chat_client_input_state_t { 54 | chat_client_input_state_header_t = 0, 55 | chat_client_input_state_payload_t 56 | }; 57 | 58 | struct chat_client_t { 59 | struct { 60 | char address[16]; 61 | int port; 62 | } server; 63 | bool is_connected; 64 | enum messi_disconnect_reason_t disconnect_reason; 65 | chat_client_on_connected_t on_connected; 66 | chat_client_on_disconnected_t on_disconnected; 67 | struct async_t *async_p; 68 | chat_client_on_connect_rsp_t on_connect_rsp; 69 | chat_client_on_message_ind_t on_message_ind; 70 | struct async_stcp_client_t stcp; 71 | struct async_timer_t keep_alive_timer; 72 | struct async_timer_t reconnect_timer; 73 | bool pong_received; 74 | struct { 75 | struct chat_server_to_client_t *message_p; 76 | struct messi_buffer_t workspace; 77 | struct { 78 | struct messi_buffer_t data; 79 | size_t size; 80 | size_t left; 81 | enum chat_client_input_state_t state; 82 | } encoded; 83 | } input; 84 | struct { 85 | struct chat_client_to_server_t *message_p; 86 | struct messi_buffer_t workspace; 87 | struct messi_buffer_t encoded; 88 | } output; 89 | }; 90 | 91 | /** 92 | * Initialize given client. 93 | */ 94 | int chat_client_init( 95 | struct chat_client_t *self_p, 96 | const char *server_uri_p, 97 | uint8_t *encoded_in_buf_p, 98 | size_t encoded_in_size, 99 | uint8_t *workspace_in_buf_p, 100 | size_t workspace_in_size, 101 | uint8_t *encoded_out_buf_p, 102 | size_t encoded_out_size, 103 | uint8_t *workspace_out_buf_p, 104 | size_t workspace_out_size, 105 | chat_client_on_connected_t on_connected, 106 | chat_client_on_disconnected_t on_disconnected, 107 | chat_client_on_connect_rsp_t on_connect_rsp, 108 | chat_client_on_message_ind_t on_message_ind, 109 | struct async_t *async_p); 110 | 111 | /** 112 | * Connect to the server. The connected callback is called once 113 | * connected. Automatic reconnect if disconnected. 114 | */ 115 | void chat_client_start(struct chat_client_t *self_p); 116 | 117 | /** 118 | * Disconnect from the server. Call start to connect again. 119 | */ 120 | void chat_client_stop(struct chat_client_t *self_p); 121 | 122 | /** 123 | * Send prepared message the server. 124 | */ 125 | void chat_client_send(struct chat_client_t *self_p); 126 | 127 | /** 128 | * Prepare a connect_req message. Call `send()` to send it. 129 | */ 130 | struct chat_connect_req_t *chat_client_init_connect_req( 131 | struct chat_client_t *self_p); 132 | 133 | /** 134 | * Prepare a message_ind message. Call `send()` to send it. 135 | */ 136 | struct chat_message_ind_t *chat_client_init_message_ind( 137 | struct chat_client_t *self_p); 138 | 139 | #endif 140 | -------------------------------------------------------------------------------- /tests/files/chat/async/chat_server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /* This file was generated by Messi. */ 28 | 29 | #ifndef CHAT_SERVER_H 30 | #define CHAT_SERVER_H 31 | 32 | #include 33 | #include "async.h" 34 | #include "messi.h" 35 | #include "chat.h" 36 | 37 | struct chat_server_t; 38 | struct chat_server_client_t; 39 | 40 | typedef void (*chat_server_on_connect_req_t)( 41 | struct chat_server_t *self_p, 42 | struct chat_server_client_t *client_p, 43 | struct chat_connect_req_t *message_p); 44 | 45 | typedef void (*chat_server_on_message_ind_t)( 46 | struct chat_server_t *self_p, 47 | struct chat_server_client_t *client_p, 48 | struct chat_message_ind_t *message_p); 49 | 50 | enum chat_server_client_input_state_t { 51 | chat_server_client_input_state_header_t = 0, 52 | chat_server_client_input_state_payload_t 53 | }; 54 | 55 | typedef void (*chat_server_on_client_connected_t)( 56 | struct chat_server_t *self_p, 57 | struct chat_server_client_t *client_p); 58 | 59 | typedef void (*chat_server_on_client_disconnected_t)( 60 | struct chat_server_t *self_p, 61 | struct chat_server_client_t *client_p); 62 | 63 | struct chat_server_t { 64 | struct { 65 | char address[16]; 66 | int port; 67 | } server; 68 | chat_server_on_client_connected_t on_client_connected; 69 | chat_server_on_client_disconnected_t on_client_disconnected; 70 | chat_server_on_connect_req_t on_connect_req; 71 | chat_server_on_message_ind_t on_message_ind; 72 | struct async_stcp_server_t stcp; 73 | struct chat_server_client_t *current_client_p; 74 | struct { 75 | struct chat_server_client_t *connected_list_p; 76 | struct chat_server_client_t *free_list_p; 77 | struct chat_server_client_t *pending_disconnect_list_p; 78 | size_t input_buffer_size; 79 | } clients; 80 | struct { 81 | struct chat_client_to_server_t *message_p; 82 | struct messi_buffer_t workspace; 83 | } input; 84 | struct { 85 | struct chat_server_to_client_t *message_p; 86 | struct messi_buffer_t workspace; 87 | struct messi_buffer_t encoded; 88 | } output; 89 | struct async_t *async_p; 90 | }; 91 | 92 | struct chat_server_client_t { 93 | struct chat_server_t *server_p; 94 | struct async_stcp_server_client_t stcp; 95 | struct async_timer_t keep_alive_timer; 96 | struct { 97 | enum chat_server_client_input_state_t state; 98 | struct messi_buffer_t data; 99 | size_t size; 100 | size_t left; 101 | } input; 102 | struct chat_server_client_t *next_p; 103 | struct chat_server_client_t *prev_p; 104 | }; 105 | 106 | /** 107 | * Initialize given server. 108 | */ 109 | int chat_server_init( 110 | struct chat_server_t *self_p, 111 | const char *server_uri_p, 112 | struct chat_server_client_t *clients_p, 113 | int clients_max, 114 | uint8_t *clients_input_bufs_p, 115 | size_t client_input_size, 116 | uint8_t *message_buf_p, 117 | size_t message_size, 118 | uint8_t *workspace_in_buf_p, 119 | size_t workspace_in_size, 120 | uint8_t *workspace_out_buf_p, 121 | size_t workspace_out_size, 122 | chat_server_on_client_connected_t on_connected, 123 | chat_server_on_client_disconnected_t on_disconnected, 124 | chat_server_on_connect_req_t on_connect_req, 125 | chat_server_on_message_ind_t on_message_ind, 126 | struct async_t *async_p); 127 | 128 | /** 129 | * Start serving clients. 130 | */ 131 | void chat_server_start(struct chat_server_t *self_p); 132 | 133 | /** 134 | * Stop serving clients. 135 | */ 136 | void chat_server_stop(struct chat_server_t *self_p); 137 | 138 | /** 139 | * Send prepared message to given client. 140 | */ 141 | void chat_server_send( 142 | struct chat_server_t *self_p, 143 | struct chat_server_client_t *client_p); 144 | 145 | /** 146 | * Send prepared message to current client. 147 | */ 148 | void chat_server_reply(struct chat_server_t *self_p); 149 | 150 | /** 151 | * Broadcast prepared message to all clients. 152 | */ 153 | void chat_server_broadcast(struct chat_server_t *self_p); 154 | 155 | /** 156 | * Disconnect given client. If given client is NULL, the currect 157 | * client is disconnected. 158 | */ 159 | void chat_server_disconnect( 160 | struct chat_server_t *self_p, 161 | struct chat_server_client_t *client_p); 162 | 163 | /** 164 | * Prepare a connect_rsp message. Call `send()`, `reply()` or `broadcast()` 165 | * to send it. 166 | */ 167 | struct chat_connect_rsp_t *chat_server_init_connect_rsp( 168 | struct chat_server_t *self_p); 169 | 170 | /** 171 | * Prepare a message_ind message. Call `send()`, `reply()` or `broadcast()` 172 | * to send it. 173 | */ 174 | struct chat_message_ind_t *chat_server_init_message_ind( 175 | struct chat_server_t *self_p); 176 | 177 | #endif 178 | -------------------------------------------------------------------------------- /tests/files/chat/chat.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package chat; 4 | 5 | message ClientToServer { 6 | oneof messages { 7 | ConnectReq connect_req = 1; 8 | MessageInd message_ind = 2; 9 | } 10 | } 11 | 12 | message ServerToClient { 13 | oneof messages { 14 | ConnectRsp connect_rsp = 1; 15 | MessageInd message_ind = 2; 16 | } 17 | } 18 | 19 | message ConnectReq { 20 | string user = 1; 21 | } 22 | 23 | message ConnectRsp { 24 | } 25 | 26 | message MessageInd { 27 | string user = 1; 28 | string text = 2; 29 | } 30 | -------------------------------------------------------------------------------- /tests/files/chat/chat_client.py: -------------------------------------------------------------------------------- 1 | # This file was generated by Messi. 2 | 3 | import asyncio 4 | import logging 5 | import bitstruct 6 | 7 | import chat_pb2 8 | 9 | 10 | LOGGER = logging.getLogger(__name__) 11 | 12 | CF_HEADER = bitstruct.compile('u8u24') 13 | 14 | 15 | class MessageType: 16 | 17 | CLIENT_TO_SERVER_USER = 1 18 | SERVER_TO_CLIENT_USER = 2 19 | PING = 3 20 | PONG = 4 21 | 22 | 23 | def parse_tcp_uri(uri): 24 | """Parse tcp://:. 25 | 26 | """ 27 | 28 | try: 29 | if uri[:6] != 'tcp://': 30 | raise ValueError 31 | 32 | address, port = uri[6:].split(':') 33 | 34 | return address, int(port) 35 | except (ValueError, TypeError): 36 | raise ValueError( 37 | f"Expected URI on the form tcp://:, but got '{uri}'.") 38 | 39 | 40 | class ChatClient: 41 | 42 | def __init__(self, uri, keep_alive_interval=2, connect_timeout=5): 43 | self._address, self._port = parse_tcp_uri(uri) 44 | self._uri = uri 45 | self._keep_alive_interval = keep_alive_interval 46 | self._connect_timeout = connect_timeout 47 | self._reader = None 48 | self._writer = None 49 | self._task = None 50 | self._keep_alive_task = None 51 | self._pong_event = None 52 | self._output = None 53 | 54 | def start(self): 55 | """Connect to the server. `on_connected()` is called once 56 | connected. Automatic reconnect if disconnected. 57 | 58 | """ 59 | 60 | if self._task is None: 61 | self._task = asyncio.create_task(self._main()) 62 | 63 | def stop(self): 64 | """Disconnect from the server (if connected). Call `start()` to 65 | connect again. 66 | 67 | """ 68 | 69 | if self._task is not None: 70 | self._task.cancel() 71 | self._task = None 72 | 73 | def send(self): 74 | """Send prepared message to the server. 75 | 76 | """ 77 | 78 | encoded = self._output.SerializeToString() 79 | header = CF_HEADER.pack(MessageType.CLIENT_TO_SERVER_USER, len(encoded)) 80 | 81 | if self._writer is not None: 82 | self._writer.write(header + encoded) 83 | 84 | async def on_connected(self): 85 | """Called when connected to the server. 86 | 87 | """ 88 | 89 | async def on_disconnected(self): 90 | """Called when disconnected from the server. 91 | 92 | """ 93 | 94 | async def on_connect_failure(self, exception): 95 | """Called when a connection attempt to the server fails. Returns the 96 | number of seconds to wait before trying to connect again, or 97 | ``None`` never to connect again. 98 | 99 | """ 100 | 101 | if isinstance(exception, asyncio.TimeoutError): 102 | return 0 103 | else: 104 | return 1 105 | 106 | async def on_connect_rsp(self, message): 107 | """Called when a connect_rsp message is received from the server. 108 | 109 | """ 110 | 111 | async def on_message_ind(self, message): 112 | """Called when a message_ind message is received from the server. 113 | 114 | """ 115 | 116 | def init_connect_req(self): 117 | """Prepare a connect_req message. Call `send()` to send it. 118 | 119 | """ 120 | 121 | self._output = chat_pb2.ClientToServer() 122 | self._output.connect_req.SetInParent() 123 | 124 | return self._output.connect_req 125 | 126 | def init_message_ind(self): 127 | """Prepare a message_ind message. Call `send()` to send it. 128 | 129 | """ 130 | 131 | self._output = chat_pb2.ClientToServer() 132 | self._output.message_ind.SetInParent() 133 | 134 | return self._output.message_ind 135 | 136 | async def _main(self): 137 | while True: 138 | if not await self._connect(): 139 | break 140 | 141 | await self.on_connected() 142 | self._pong_event = asyncio.Event() 143 | self._keep_alive_task = asyncio.create_task(self._keep_alive_main()) 144 | 145 | try: 146 | await self._reader_loop() 147 | except (Exception, asyncio.CancelledError) as e: 148 | LOGGER.info('Reader loop stopped by %r.', e) 149 | self._close() 150 | 151 | self._keep_alive_task.cancel() 152 | await self.on_disconnected() 153 | 154 | async def _connect(self): 155 | """Repeatedly try to connect to the server. Returns ``True`` if a 156 | connection has been established, and ``False`` otherwise. 157 | 158 | """ 159 | 160 | while True: 161 | try: 162 | self._reader, self._writer = await asyncio.wait_for( 163 | asyncio.open_connection(self._address, self._port), 164 | self._connect_timeout) 165 | 166 | return True 167 | except ConnectionRefusedError as e: 168 | LOGGER.info("Connection refused.") 169 | delay = await self.on_connect_failure(e) 170 | except asyncio.TimeoutError as e: 171 | LOGGER.info("Connect timeout.") 172 | delay = await self.on_connect_failure(e) 173 | except OSError as e: 174 | LOGGER.info("OS error: %s", e) 175 | delay = await self.on_connect_failure(e) 176 | 177 | if delay is None: 178 | return False 179 | 180 | await asyncio.sleep(delay) 181 | 182 | async def _handle_user_message(self, payload): 183 | message = chat_pb2.ServerToClient() 184 | message.ParseFromString(payload) 185 | choice = message.WhichOneof('messages') 186 | 187 | if choice == 'connect_rsp': 188 | await self.on_connect_rsp(message.connect_rsp) 189 | elif choice == 'message_ind': 190 | await self.on_message_ind(message.message_ind) 191 | 192 | def _handle_pong(self): 193 | self._pong_event.set() 194 | 195 | async def _reader_loop(self): 196 | while True: 197 | header = await self._reader.readexactly(4) 198 | message_type, size = CF_HEADER.unpack(header) 199 | payload = await self._reader.readexactly(size) 200 | 201 | if message_type == MessageType.SERVER_TO_CLIENT_USER: 202 | await self._handle_user_message(payload) 203 | elif message_type == MessageType.PONG: 204 | self._handle_pong() 205 | 206 | async def _keep_alive_loop(self): 207 | while True: 208 | await asyncio.sleep(self._keep_alive_interval) 209 | self._pong_event.clear() 210 | self._writer.write(CF_HEADER.pack(MessageType.PING, 0)) 211 | await asyncio.wait_for(self._pong_event.wait(), 212 | self._keep_alive_interval) 213 | 214 | async def _keep_alive_main(self): 215 | try: 216 | await self._keep_alive_loop() 217 | except (Exception, asyncio.CancelledError) as e: 218 | LOGGER.info('Keep alive task stopped by %r.', e) 219 | self._close() 220 | 221 | def _close(self): 222 | if self._writer is not None: 223 | self._writer.close() 224 | self._writer = None 225 | -------------------------------------------------------------------------------- /tests/files/chat/linux/chat_client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /* This file was generated by Messi. */ 28 | 29 | #ifndef CHAT_CLIENT_H 30 | #define CHAT_CLIENT_H 31 | 32 | #include 33 | #include "messi.h" 34 | #include "chat.h" 35 | 36 | struct chat_client_t; 37 | 38 | typedef void (*chat_client_on_connected_t)(struct chat_client_t *self_p); 39 | 40 | typedef void (*chat_client_on_disconnected_t)( 41 | struct chat_client_t *self_p, 42 | enum messi_disconnect_reason_t disconnect_reason); 43 | 44 | typedef void (*chat_client_on_connect_rsp_t)( 45 | struct chat_client_t *self_p, 46 | struct chat_connect_rsp_t *message_p); 47 | 48 | typedef void (*chat_client_on_message_ind_t)( 49 | struct chat_client_t *self_p, 50 | struct chat_message_ind_t *message_p); 51 | 52 | enum chat_client_input_state_t { 53 | chat_client_input_state_header_t = 0, 54 | chat_client_input_state_payload_t 55 | }; 56 | 57 | struct chat_client_t { 58 | struct { 59 | char address[16]; 60 | int port; 61 | } server; 62 | enum messi_disconnect_reason_t disconnect_reason; 63 | chat_client_on_connected_t on_connected; 64 | chat_client_on_disconnected_t on_disconnected; 65 | chat_client_on_connect_rsp_t on_connect_rsp; 66 | chat_client_on_message_ind_t on_message_ind; 67 | int epoll_fd; 68 | messi_epoll_ctl_t epoll_ctl; 69 | int server_fd; 70 | int keep_alive_timer_fd; 71 | int reconnect_timer_fd; 72 | bool pong_received; 73 | bool pending_disconnect; 74 | struct { 75 | struct chat_server_to_client_t *message_p; 76 | struct messi_buffer_t workspace; 77 | struct { 78 | struct messi_buffer_t data; 79 | size_t size; 80 | size_t left; 81 | enum chat_client_input_state_t state; 82 | } encoded; 83 | } input; 84 | struct { 85 | struct chat_client_to_server_t *message_p; 86 | struct messi_buffer_t workspace; 87 | struct messi_buffer_t encoded; 88 | } output; 89 | }; 90 | 91 | /** 92 | * Initialize given client. 93 | */ 94 | int chat_client_init( 95 | struct chat_client_t *self_p, 96 | const char *server_uri_p, 97 | uint8_t *encoded_in_buf_p, 98 | size_t encoded_in_size, 99 | uint8_t *workspace_in_buf_p, 100 | size_t workspace_in_size, 101 | uint8_t *encoded_out_buf_p, 102 | size_t encoded_out_size, 103 | uint8_t *workspace_out_buf_p, 104 | size_t workspace_out_size, 105 | chat_client_on_connected_t on_connected, 106 | chat_client_on_disconnected_t on_disconnected, 107 | chat_client_on_connect_rsp_t on_connect_rsp, 108 | chat_client_on_message_ind_t on_message_ind, 109 | int epoll_fd, 110 | messi_epoll_ctl_t epoll_ctl); 111 | 112 | /** 113 | * Connect to the server. The connected callback is called once 114 | * connected. Automatic reconnect if disconnected. 115 | */ 116 | void chat_client_start(struct chat_client_t *self_p); 117 | 118 | /** 119 | * Disconnect from the server. Call start to connect again. 120 | */ 121 | void chat_client_stop(struct chat_client_t *self_p); 122 | 123 | /** 124 | * Process all pending events on given file descriptor (if it belongs 125 | * to given client). 126 | */ 127 | void chat_client_process( 128 | struct chat_client_t *self_p, 129 | int fd, 130 | uint32_t events); 131 | 132 | /** 133 | * Send prepared message the server. 134 | */ 135 | void chat_client_send(struct chat_client_t *self_p); 136 | 137 | /** 138 | * Prepare a connect_req message. Call `send()` to send it. 139 | */ 140 | struct chat_connect_req_t *chat_client_init_connect_req( 141 | struct chat_client_t *self_p); 142 | 143 | /** 144 | * Prepare a message_ind message. Call `send()` to send it. 145 | */ 146 | struct chat_message_ind_t *chat_client_init_message_ind( 147 | struct chat_client_t *self_p); 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /tests/files/chat/linux/chat_server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /* This file was generated by Messi. */ 28 | 29 | #ifndef CHAT_SERVER_H 30 | #define CHAT_SERVER_H 31 | 32 | #include 33 | #include "messi.h" 34 | #include "chat.h" 35 | 36 | struct chat_server_t; 37 | struct chat_server_client_t; 38 | 39 | typedef void (*chat_server_on_connect_req_t)( 40 | struct chat_server_t *self_p, 41 | struct chat_server_client_t *client_p, 42 | struct chat_connect_req_t *message_p); 43 | 44 | typedef void (*chat_server_on_message_ind_t)( 45 | struct chat_server_t *self_p, 46 | struct chat_server_client_t *client_p, 47 | struct chat_message_ind_t *message_p); 48 | 49 | enum chat_server_client_input_state_t { 50 | chat_server_client_input_state_header_t = 0, 51 | chat_server_client_input_state_payload_t 52 | }; 53 | 54 | typedef void (*chat_server_on_client_connected_t)( 55 | struct chat_server_t *self_p, 56 | struct chat_server_client_t *client_p); 57 | 58 | typedef void (*chat_server_on_client_disconnected_t)( 59 | struct chat_server_t *self_p, 60 | struct chat_server_client_t *client_p); 61 | 62 | struct chat_server_t { 63 | struct { 64 | char address[16]; 65 | int port; 66 | } server; 67 | chat_server_on_client_connected_t on_client_connected; 68 | chat_server_on_client_disconnected_t on_client_disconnected; 69 | chat_server_on_connect_req_t on_connect_req; 70 | chat_server_on_message_ind_t on_message_ind; 71 | int epoll_fd; 72 | messi_epoll_ctl_t epoll_ctl; 73 | int listener_fd; 74 | struct chat_server_client_t *current_client_p; 75 | struct { 76 | struct chat_server_client_t *connected_list_p; 77 | struct chat_server_client_t *free_list_p; 78 | struct chat_server_client_t *pending_disconnect_list_p; 79 | size_t input_buffer_size; 80 | } clients; 81 | struct { 82 | struct chat_client_to_server_t *message_p; 83 | struct messi_buffer_t workspace; 84 | } input; 85 | struct { 86 | struct chat_server_to_client_t *message_p; 87 | struct messi_buffer_t workspace; 88 | struct messi_buffer_t encoded; 89 | } output; 90 | }; 91 | 92 | struct chat_server_client_output_item_t { 93 | size_t offset; 94 | size_t size; 95 | struct chat_server_client_output_item_t *next_p; 96 | uint8_t data[1]; 97 | }; 98 | 99 | struct chat_server_client_t { 100 | int client_fd; 101 | int keep_alive_timer_fd; 102 | struct { 103 | enum chat_server_client_input_state_t state; 104 | struct messi_buffer_t data; 105 | size_t size; 106 | size_t left; 107 | } input; 108 | struct { 109 | struct chat_server_client_output_item_t *head_p; 110 | struct chat_server_client_output_item_t *tail_p; 111 | } output; 112 | struct chat_server_client_t *next_p; 113 | struct chat_server_client_t *prev_p; 114 | }; 115 | 116 | /** 117 | * Initialize given server. 118 | */ 119 | int chat_server_init( 120 | struct chat_server_t *self_p, 121 | const char *server_uri_p, 122 | struct chat_server_client_t *clients_p, 123 | int clients_max, 124 | uint8_t *clients_input_bufs_p, 125 | size_t client_input_size, 126 | uint8_t *message_buf_p, 127 | size_t message_size, 128 | uint8_t *workspace_in_buf_p, 129 | size_t workspace_in_size, 130 | uint8_t *workspace_out_buf_p, 131 | size_t workspace_out_size, 132 | chat_server_on_client_connected_t on_client_connected, 133 | chat_server_on_client_disconnected_t on_client_disconnected, 134 | chat_server_on_connect_req_t on_connect_req, 135 | chat_server_on_message_ind_t on_message_ind, 136 | int epoll_fd, 137 | messi_epoll_ctl_t epoll_ctl); 138 | 139 | /** 140 | * Start serving clients. 141 | */ 142 | int chat_server_start(struct chat_server_t *self_p); 143 | 144 | /** 145 | * Stop serving clients. 146 | */ 147 | void chat_server_stop(struct chat_server_t *self_p); 148 | 149 | /** 150 | * Process any pending events on given file descriptor if it belongs 151 | * to given server. 152 | */ 153 | void chat_server_process(struct chat_server_t *self_p, int fd, uint32_t events); 154 | 155 | /** 156 | * Send prepared message to given client. 157 | */ 158 | void chat_server_send( 159 | struct chat_server_t *self_p, 160 | struct chat_server_client_t *client_p); 161 | 162 | /** 163 | * Send prepared message to current client. 164 | */ 165 | void chat_server_reply(struct chat_server_t *self_p); 166 | 167 | /** 168 | * Broadcast prepared message to all clients. 169 | */ 170 | void chat_server_broadcast(struct chat_server_t *self_p); 171 | 172 | /** 173 | * Disconnect given client. If given client is NULL, the currect 174 | * client is disconnected. 175 | */ 176 | void chat_server_disconnect( 177 | struct chat_server_t *self_p, 178 | struct chat_server_client_t *client_p); 179 | 180 | /** 181 | * Prepare a connect_rsp message. Call `send()`, `reply()` or `broadcast()` 182 | * to send it. 183 | */ 184 | struct chat_connect_rsp_t *chat_server_init_connect_rsp( 185 | struct chat_server_t *self_p); 186 | 187 | /** 188 | * Prepare a message_ind message. Call `send()`, `reply()` or `broadcast()` 189 | * to send it. 190 | */ 191 | struct chat_message_ind_t *chat_server_init_message_ind( 192 | struct chat_server_t *self_p); 193 | 194 | #endif 195 | -------------------------------------------------------------------------------- /tests/files/imported/imported.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "types_not_package_name.proto"; 4 | 5 | package imported; 6 | 7 | message ClientToServer { 8 | oneof messages { 9 | types.Foo foo = 1; 10 | } 11 | } 12 | 13 | message ServerToClient { 14 | oneof messages { 15 | types.Bar bar = 1; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/files/imported/linux/imported_client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /* This file was generated by Messi. */ 28 | 29 | #ifndef IMPORTED_CLIENT_H 30 | #define IMPORTED_CLIENT_H 31 | 32 | #include 33 | #include "messi.h" 34 | #include "imported.h" 35 | 36 | struct imported_client_t; 37 | 38 | typedef void (*imported_client_on_connected_t)(struct imported_client_t *self_p); 39 | 40 | typedef void (*imported_client_on_disconnected_t)( 41 | struct imported_client_t *self_p, 42 | enum messi_disconnect_reason_t disconnect_reason); 43 | 44 | typedef void (*imported_client_on_bar_t)( 45 | struct imported_client_t *self_p, 46 | struct types_bar_t *message_p); 47 | 48 | enum imported_client_input_state_t { 49 | imported_client_input_state_header_t = 0, 50 | imported_client_input_state_payload_t 51 | }; 52 | 53 | struct imported_client_t { 54 | struct { 55 | char address[16]; 56 | int port; 57 | } server; 58 | enum messi_disconnect_reason_t disconnect_reason; 59 | imported_client_on_connected_t on_connected; 60 | imported_client_on_disconnected_t on_disconnected; 61 | imported_client_on_bar_t on_bar; 62 | int epoll_fd; 63 | messi_epoll_ctl_t epoll_ctl; 64 | int server_fd; 65 | int keep_alive_timer_fd; 66 | int reconnect_timer_fd; 67 | bool pong_received; 68 | bool pending_disconnect; 69 | struct { 70 | struct imported_server_to_client_t *message_p; 71 | struct messi_buffer_t workspace; 72 | struct { 73 | struct messi_buffer_t data; 74 | size_t size; 75 | size_t left; 76 | enum imported_client_input_state_t state; 77 | } encoded; 78 | } input; 79 | struct { 80 | struct imported_client_to_server_t *message_p; 81 | struct messi_buffer_t workspace; 82 | struct messi_buffer_t encoded; 83 | } output; 84 | }; 85 | 86 | /** 87 | * Initialize given client. 88 | */ 89 | int imported_client_init( 90 | struct imported_client_t *self_p, 91 | const char *server_uri_p, 92 | uint8_t *encoded_in_buf_p, 93 | size_t encoded_in_size, 94 | uint8_t *workspace_in_buf_p, 95 | size_t workspace_in_size, 96 | uint8_t *encoded_out_buf_p, 97 | size_t encoded_out_size, 98 | uint8_t *workspace_out_buf_p, 99 | size_t workspace_out_size, 100 | imported_client_on_connected_t on_connected, 101 | imported_client_on_disconnected_t on_disconnected, 102 | imported_client_on_bar_t on_bar, 103 | int epoll_fd, 104 | messi_epoll_ctl_t epoll_ctl); 105 | 106 | /** 107 | * Connect to the server. The connected callback is called once 108 | * connected. Automatic reconnect if disconnected. 109 | */ 110 | void imported_client_start(struct imported_client_t *self_p); 111 | 112 | /** 113 | * Disconnect from the server. Call start to connect again. 114 | */ 115 | void imported_client_stop(struct imported_client_t *self_p); 116 | 117 | /** 118 | * Process all pending events on given file descriptor (if it belongs 119 | * to given client). 120 | */ 121 | void imported_client_process( 122 | struct imported_client_t *self_p, 123 | int fd, 124 | uint32_t events); 125 | 126 | /** 127 | * Send prepared message the server. 128 | */ 129 | void imported_client_send(struct imported_client_t *self_p); 130 | 131 | /** 132 | * Prepare a foo message. Call `send()` to send it. 133 | */ 134 | struct types_foo_t *imported_client_init_foo( 135 | struct imported_client_t *self_p); 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /tests/files/imported/linux/imported_server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /* This file was generated by Messi. */ 28 | 29 | #ifndef IMPORTED_SERVER_H 30 | #define IMPORTED_SERVER_H 31 | 32 | #include 33 | #include "messi.h" 34 | #include "imported.h" 35 | 36 | struct imported_server_t; 37 | struct imported_server_client_t; 38 | 39 | typedef void (*imported_server_on_foo_t)( 40 | struct imported_server_t *self_p, 41 | struct imported_server_client_t *client_p, 42 | struct types_foo_t *message_p); 43 | 44 | enum imported_server_client_input_state_t { 45 | imported_server_client_input_state_header_t = 0, 46 | imported_server_client_input_state_payload_t 47 | }; 48 | 49 | typedef void (*imported_server_on_client_connected_t)( 50 | struct imported_server_t *self_p, 51 | struct imported_server_client_t *client_p); 52 | 53 | typedef void (*imported_server_on_client_disconnected_t)( 54 | struct imported_server_t *self_p, 55 | struct imported_server_client_t *client_p); 56 | 57 | struct imported_server_t { 58 | struct { 59 | char address[16]; 60 | int port; 61 | } server; 62 | imported_server_on_client_connected_t on_client_connected; 63 | imported_server_on_client_disconnected_t on_client_disconnected; 64 | imported_server_on_foo_t on_foo; 65 | int epoll_fd; 66 | messi_epoll_ctl_t epoll_ctl; 67 | int listener_fd; 68 | struct imported_server_client_t *current_client_p; 69 | struct { 70 | struct imported_server_client_t *connected_list_p; 71 | struct imported_server_client_t *free_list_p; 72 | struct imported_server_client_t *pending_disconnect_list_p; 73 | size_t input_buffer_size; 74 | } clients; 75 | struct { 76 | struct imported_client_to_server_t *message_p; 77 | struct messi_buffer_t workspace; 78 | } input; 79 | struct { 80 | struct imported_server_to_client_t *message_p; 81 | struct messi_buffer_t workspace; 82 | struct messi_buffer_t encoded; 83 | } output; 84 | }; 85 | 86 | struct imported_server_client_output_item_t { 87 | size_t offset; 88 | size_t size; 89 | struct imported_server_client_output_item_t *next_p; 90 | uint8_t data[1]; 91 | }; 92 | 93 | struct imported_server_client_t { 94 | int client_fd; 95 | int keep_alive_timer_fd; 96 | struct { 97 | enum imported_server_client_input_state_t state; 98 | struct messi_buffer_t data; 99 | size_t size; 100 | size_t left; 101 | } input; 102 | struct { 103 | struct imported_server_client_output_item_t *head_p; 104 | struct imported_server_client_output_item_t *tail_p; 105 | } output; 106 | struct imported_server_client_t *next_p; 107 | struct imported_server_client_t *prev_p; 108 | }; 109 | 110 | /** 111 | * Initialize given server. 112 | */ 113 | int imported_server_init( 114 | struct imported_server_t *self_p, 115 | const char *server_uri_p, 116 | struct imported_server_client_t *clients_p, 117 | int clients_max, 118 | uint8_t *clients_input_bufs_p, 119 | size_t client_input_size, 120 | uint8_t *message_buf_p, 121 | size_t message_size, 122 | uint8_t *workspace_in_buf_p, 123 | size_t workspace_in_size, 124 | uint8_t *workspace_out_buf_p, 125 | size_t workspace_out_size, 126 | imported_server_on_client_connected_t on_client_connected, 127 | imported_server_on_client_disconnected_t on_client_disconnected, 128 | imported_server_on_foo_t on_foo, 129 | int epoll_fd, 130 | messi_epoll_ctl_t epoll_ctl); 131 | 132 | /** 133 | * Start serving clients. 134 | */ 135 | int imported_server_start(struct imported_server_t *self_p); 136 | 137 | /** 138 | * Stop serving clients. 139 | */ 140 | void imported_server_stop(struct imported_server_t *self_p); 141 | 142 | /** 143 | * Process any pending events on given file descriptor if it belongs 144 | * to given server. 145 | */ 146 | void imported_server_process(struct imported_server_t *self_p, int fd, uint32_t events); 147 | 148 | /** 149 | * Send prepared message to given client. 150 | */ 151 | void imported_server_send( 152 | struct imported_server_t *self_p, 153 | struct imported_server_client_t *client_p); 154 | 155 | /** 156 | * Send prepared message to current client. 157 | */ 158 | void imported_server_reply(struct imported_server_t *self_p); 159 | 160 | /** 161 | * Broadcast prepared message to all clients. 162 | */ 163 | void imported_server_broadcast(struct imported_server_t *self_p); 164 | 165 | /** 166 | * Disconnect given client. If given client is NULL, the currect 167 | * client is disconnected. 168 | */ 169 | void imported_server_disconnect( 170 | struct imported_server_t *self_p, 171 | struct imported_server_client_t *client_p); 172 | 173 | /** 174 | * Prepare a bar message. Call `send()`, `reply()` or `broadcast()` 175 | * to send it. 176 | */ 177 | struct types_bar_t *imported_server_init_bar( 178 | struct imported_server_t *self_p); 179 | 180 | #endif 181 | -------------------------------------------------------------------------------- /tests/files/imported/types_not_package_name.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package types; 4 | 5 | message Foo { 6 | } 7 | 8 | message Bar { 9 | } 10 | -------------------------------------------------------------------------------- /tests/files/my_protocol/async/my_protocol_client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /* This file was generated by Messi. */ 28 | 29 | #ifndef MY_PROTOCOL_CLIENT_H 30 | #define MY_PROTOCOL_CLIENT_H 31 | 32 | #include 33 | #include "messi.h" 34 | #include "my_protocol.h" 35 | #include "async.h" 36 | 37 | struct my_protocol_client_t; 38 | 39 | typedef void (*my_protocol_client_on_connected_t)(struct my_protocol_client_t *self_p); 40 | 41 | typedef void (*my_protocol_client_on_disconnected_t)( 42 | struct my_protocol_client_t *self_p, 43 | enum messi_disconnect_reason_t disconnect_reason); 44 | 45 | typedef void (*my_protocol_client_on_foo_rsp_t)( 46 | struct my_protocol_client_t *self_p, 47 | struct my_protocol_foo_rsp_t *message_p); 48 | 49 | typedef void (*my_protocol_client_on_fie_req_t)( 50 | struct my_protocol_client_t *self_p, 51 | struct my_protocol_fie_req_t *message_p); 52 | 53 | enum my_protocol_client_input_state_t { 54 | my_protocol_client_input_state_header_t = 0, 55 | my_protocol_client_input_state_payload_t 56 | }; 57 | 58 | struct my_protocol_client_t { 59 | struct { 60 | char address[16]; 61 | int port; 62 | } server; 63 | bool is_connected; 64 | enum messi_disconnect_reason_t disconnect_reason; 65 | my_protocol_client_on_connected_t on_connected; 66 | my_protocol_client_on_disconnected_t on_disconnected; 67 | struct async_t *async_p; 68 | my_protocol_client_on_foo_rsp_t on_foo_rsp; 69 | my_protocol_client_on_fie_req_t on_fie_req; 70 | struct async_stcp_client_t stcp; 71 | struct async_timer_t keep_alive_timer; 72 | struct async_timer_t reconnect_timer; 73 | bool pong_received; 74 | struct { 75 | struct my_protocol_server_to_client_t *message_p; 76 | struct messi_buffer_t workspace; 77 | struct { 78 | struct messi_buffer_t data; 79 | size_t size; 80 | size_t left; 81 | enum my_protocol_client_input_state_t state; 82 | } encoded; 83 | } input; 84 | struct { 85 | struct my_protocol_client_to_server_t *message_p; 86 | struct messi_buffer_t workspace; 87 | struct messi_buffer_t encoded; 88 | } output; 89 | }; 90 | 91 | /** 92 | * Initialize given client. 93 | */ 94 | int my_protocol_client_init( 95 | struct my_protocol_client_t *self_p, 96 | const char *server_uri_p, 97 | uint8_t *encoded_in_buf_p, 98 | size_t encoded_in_size, 99 | uint8_t *workspace_in_buf_p, 100 | size_t workspace_in_size, 101 | uint8_t *encoded_out_buf_p, 102 | size_t encoded_out_size, 103 | uint8_t *workspace_out_buf_p, 104 | size_t workspace_out_size, 105 | my_protocol_client_on_connected_t on_connected, 106 | my_protocol_client_on_disconnected_t on_disconnected, 107 | my_protocol_client_on_foo_rsp_t on_foo_rsp, 108 | my_protocol_client_on_fie_req_t on_fie_req, 109 | struct async_t *async_p); 110 | 111 | /** 112 | * Connect to the server. The connected callback is called once 113 | * connected. Automatic reconnect if disconnected. 114 | */ 115 | void my_protocol_client_start(struct my_protocol_client_t *self_p); 116 | 117 | /** 118 | * Disconnect from the server. Call start to connect again. 119 | */ 120 | void my_protocol_client_stop(struct my_protocol_client_t *self_p); 121 | 122 | /** 123 | * Send prepared message the server. 124 | */ 125 | void my_protocol_client_send(struct my_protocol_client_t *self_p); 126 | 127 | /** 128 | * Prepare a foo_req message. Call `send()` to send it. 129 | */ 130 | struct my_protocol_foo_req_t *my_protocol_client_init_foo_req( 131 | struct my_protocol_client_t *self_p); 132 | 133 | /** 134 | * Prepare a bar_ind message. Call `send()` to send it. 135 | */ 136 | struct my_protocol_bar_ind_t *my_protocol_client_init_bar_ind( 137 | struct my_protocol_client_t *self_p); 138 | 139 | /** 140 | * Prepare a fie_rsp message. Call `send()` to send it. 141 | */ 142 | struct my_protocol_fie_rsp_t *my_protocol_client_init_fie_rsp( 143 | struct my_protocol_client_t *self_p); 144 | 145 | #endif 146 | -------------------------------------------------------------------------------- /tests/files/my_protocol/async/my_protocol_server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /* This file was generated by Messi. */ 28 | 29 | #ifndef MY_PROTOCOL_SERVER_H 30 | #define MY_PROTOCOL_SERVER_H 31 | 32 | #include 33 | #include "async.h" 34 | #include "messi.h" 35 | #include "my_protocol.h" 36 | 37 | struct my_protocol_server_t; 38 | struct my_protocol_server_client_t; 39 | 40 | typedef void (*my_protocol_server_on_foo_req_t)( 41 | struct my_protocol_server_t *self_p, 42 | struct my_protocol_server_client_t *client_p, 43 | struct my_protocol_foo_req_t *message_p); 44 | 45 | typedef void (*my_protocol_server_on_bar_ind_t)( 46 | struct my_protocol_server_t *self_p, 47 | struct my_protocol_server_client_t *client_p, 48 | struct my_protocol_bar_ind_t *message_p); 49 | 50 | typedef void (*my_protocol_server_on_fie_rsp_t)( 51 | struct my_protocol_server_t *self_p, 52 | struct my_protocol_server_client_t *client_p, 53 | struct my_protocol_fie_rsp_t *message_p); 54 | 55 | enum my_protocol_server_client_input_state_t { 56 | my_protocol_server_client_input_state_header_t = 0, 57 | my_protocol_server_client_input_state_payload_t 58 | }; 59 | 60 | typedef void (*my_protocol_server_on_client_connected_t)( 61 | struct my_protocol_server_t *self_p, 62 | struct my_protocol_server_client_t *client_p); 63 | 64 | typedef void (*my_protocol_server_on_client_disconnected_t)( 65 | struct my_protocol_server_t *self_p, 66 | struct my_protocol_server_client_t *client_p); 67 | 68 | struct my_protocol_server_t { 69 | struct { 70 | char address[16]; 71 | int port; 72 | } server; 73 | my_protocol_server_on_client_connected_t on_client_connected; 74 | my_protocol_server_on_client_disconnected_t on_client_disconnected; 75 | my_protocol_server_on_foo_req_t on_foo_req; 76 | my_protocol_server_on_bar_ind_t on_bar_ind; 77 | my_protocol_server_on_fie_rsp_t on_fie_rsp; 78 | struct async_stcp_server_t stcp; 79 | struct my_protocol_server_client_t *current_client_p; 80 | struct { 81 | struct my_protocol_server_client_t *connected_list_p; 82 | struct my_protocol_server_client_t *free_list_p; 83 | struct my_protocol_server_client_t *pending_disconnect_list_p; 84 | size_t input_buffer_size; 85 | } clients; 86 | struct { 87 | struct my_protocol_client_to_server_t *message_p; 88 | struct messi_buffer_t workspace; 89 | } input; 90 | struct { 91 | struct my_protocol_server_to_client_t *message_p; 92 | struct messi_buffer_t workspace; 93 | struct messi_buffer_t encoded; 94 | } output; 95 | struct async_t *async_p; 96 | }; 97 | 98 | struct my_protocol_server_client_t { 99 | struct my_protocol_server_t *server_p; 100 | struct async_stcp_server_client_t stcp; 101 | struct async_timer_t keep_alive_timer; 102 | struct { 103 | enum my_protocol_server_client_input_state_t state; 104 | struct messi_buffer_t data; 105 | size_t size; 106 | size_t left; 107 | } input; 108 | struct my_protocol_server_client_t *next_p; 109 | struct my_protocol_server_client_t *prev_p; 110 | }; 111 | 112 | /** 113 | * Initialize given server. 114 | */ 115 | int my_protocol_server_init( 116 | struct my_protocol_server_t *self_p, 117 | const char *server_uri_p, 118 | struct my_protocol_server_client_t *clients_p, 119 | int clients_max, 120 | uint8_t *clients_input_bufs_p, 121 | size_t client_input_size, 122 | uint8_t *message_buf_p, 123 | size_t message_size, 124 | uint8_t *workspace_in_buf_p, 125 | size_t workspace_in_size, 126 | uint8_t *workspace_out_buf_p, 127 | size_t workspace_out_size, 128 | my_protocol_server_on_client_connected_t on_connected, 129 | my_protocol_server_on_client_disconnected_t on_disconnected, 130 | my_protocol_server_on_foo_req_t on_foo_req, 131 | my_protocol_server_on_bar_ind_t on_bar_ind, 132 | my_protocol_server_on_fie_rsp_t on_fie_rsp, 133 | struct async_t *async_p); 134 | 135 | /** 136 | * Start serving clients. 137 | */ 138 | void my_protocol_server_start(struct my_protocol_server_t *self_p); 139 | 140 | /** 141 | * Stop serving clients. 142 | */ 143 | void my_protocol_server_stop(struct my_protocol_server_t *self_p); 144 | 145 | /** 146 | * Send prepared message to given client. 147 | */ 148 | void my_protocol_server_send( 149 | struct my_protocol_server_t *self_p, 150 | struct my_protocol_server_client_t *client_p); 151 | 152 | /** 153 | * Send prepared message to current client. 154 | */ 155 | void my_protocol_server_reply(struct my_protocol_server_t *self_p); 156 | 157 | /** 158 | * Broadcast prepared message to all clients. 159 | */ 160 | void my_protocol_server_broadcast(struct my_protocol_server_t *self_p); 161 | 162 | /** 163 | * Disconnect given client. If given client is NULL, the currect 164 | * client is disconnected. 165 | */ 166 | void my_protocol_server_disconnect( 167 | struct my_protocol_server_t *self_p, 168 | struct my_protocol_server_client_t *client_p); 169 | 170 | /** 171 | * Prepare a foo_rsp message. Call `send()`, `reply()` or `broadcast()` 172 | * to send it. 173 | */ 174 | struct my_protocol_foo_rsp_t *my_protocol_server_init_foo_rsp( 175 | struct my_protocol_server_t *self_p); 176 | 177 | /** 178 | * Prepare a fie_req message. Call `send()`, `reply()` or `broadcast()` 179 | * to send it. 180 | */ 181 | struct my_protocol_fie_req_t *my_protocol_server_init_fie_req( 182 | struct my_protocol_server_t *self_p); 183 | 184 | #endif 185 | -------------------------------------------------------------------------------- /tests/files/my_protocol/linux/my_protocol_client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /* This file was generated by Messi. */ 28 | 29 | #ifndef MY_PROTOCOL_CLIENT_H 30 | #define MY_PROTOCOL_CLIENT_H 31 | 32 | #include 33 | #include "messi.h" 34 | #include "my_protocol.h" 35 | 36 | struct my_protocol_client_t; 37 | 38 | typedef void (*my_protocol_client_on_connected_t)(struct my_protocol_client_t *self_p); 39 | 40 | typedef void (*my_protocol_client_on_disconnected_t)( 41 | struct my_protocol_client_t *self_p, 42 | enum messi_disconnect_reason_t disconnect_reason); 43 | 44 | typedef void (*my_protocol_client_on_foo_rsp_t)( 45 | struct my_protocol_client_t *self_p, 46 | struct my_protocol_foo_rsp_t *message_p); 47 | 48 | typedef void (*my_protocol_client_on_fie_req_t)( 49 | struct my_protocol_client_t *self_p, 50 | struct my_protocol_fie_req_t *message_p); 51 | 52 | enum my_protocol_client_input_state_t { 53 | my_protocol_client_input_state_header_t = 0, 54 | my_protocol_client_input_state_payload_t 55 | }; 56 | 57 | struct my_protocol_client_t { 58 | struct { 59 | char address[16]; 60 | int port; 61 | } server; 62 | enum messi_disconnect_reason_t disconnect_reason; 63 | my_protocol_client_on_connected_t on_connected; 64 | my_protocol_client_on_disconnected_t on_disconnected; 65 | my_protocol_client_on_foo_rsp_t on_foo_rsp; 66 | my_protocol_client_on_fie_req_t on_fie_req; 67 | int epoll_fd; 68 | messi_epoll_ctl_t epoll_ctl; 69 | int server_fd; 70 | int keep_alive_timer_fd; 71 | int reconnect_timer_fd; 72 | bool pong_received; 73 | bool pending_disconnect; 74 | struct { 75 | struct my_protocol_server_to_client_t *message_p; 76 | struct messi_buffer_t workspace; 77 | struct { 78 | struct messi_buffer_t data; 79 | size_t size; 80 | size_t left; 81 | enum my_protocol_client_input_state_t state; 82 | } encoded; 83 | } input; 84 | struct { 85 | struct my_protocol_client_to_server_t *message_p; 86 | struct messi_buffer_t workspace; 87 | struct messi_buffer_t encoded; 88 | } output; 89 | }; 90 | 91 | /** 92 | * Initialize given client. 93 | */ 94 | int my_protocol_client_init( 95 | struct my_protocol_client_t *self_p, 96 | const char *server_uri_p, 97 | uint8_t *encoded_in_buf_p, 98 | size_t encoded_in_size, 99 | uint8_t *workspace_in_buf_p, 100 | size_t workspace_in_size, 101 | uint8_t *encoded_out_buf_p, 102 | size_t encoded_out_size, 103 | uint8_t *workspace_out_buf_p, 104 | size_t workspace_out_size, 105 | my_protocol_client_on_connected_t on_connected, 106 | my_protocol_client_on_disconnected_t on_disconnected, 107 | my_protocol_client_on_foo_rsp_t on_foo_rsp, 108 | my_protocol_client_on_fie_req_t on_fie_req, 109 | int epoll_fd, 110 | messi_epoll_ctl_t epoll_ctl); 111 | 112 | /** 113 | * Connect to the server. The connected callback is called once 114 | * connected. Automatic reconnect if disconnected. 115 | */ 116 | void my_protocol_client_start(struct my_protocol_client_t *self_p); 117 | 118 | /** 119 | * Disconnect from the server. Call start to connect again. 120 | */ 121 | void my_protocol_client_stop(struct my_protocol_client_t *self_p); 122 | 123 | /** 124 | * Process all pending events on given file descriptor (if it belongs 125 | * to given client). 126 | */ 127 | void my_protocol_client_process( 128 | struct my_protocol_client_t *self_p, 129 | int fd, 130 | uint32_t events); 131 | 132 | /** 133 | * Send prepared message the server. 134 | */ 135 | void my_protocol_client_send(struct my_protocol_client_t *self_p); 136 | 137 | /** 138 | * Prepare a foo_req message. Call `send()` to send it. 139 | */ 140 | struct my_protocol_foo_req_t *my_protocol_client_init_foo_req( 141 | struct my_protocol_client_t *self_p); 142 | 143 | /** 144 | * Prepare a bar_ind message. Call `send()` to send it. 145 | */ 146 | struct my_protocol_bar_ind_t *my_protocol_client_init_bar_ind( 147 | struct my_protocol_client_t *self_p); 148 | 149 | /** 150 | * Prepare a fie_rsp message. Call `send()` to send it. 151 | */ 152 | struct my_protocol_fie_rsp_t *my_protocol_client_init_fie_rsp( 153 | struct my_protocol_client_t *self_p); 154 | 155 | #endif 156 | -------------------------------------------------------------------------------- /tests/files/my_protocol/linux/my_protocol_server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020, Erik Moqvist 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /* This file was generated by Messi. */ 28 | 29 | #ifndef MY_PROTOCOL_SERVER_H 30 | #define MY_PROTOCOL_SERVER_H 31 | 32 | #include 33 | #include "messi.h" 34 | #include "my_protocol.h" 35 | 36 | struct my_protocol_server_t; 37 | struct my_protocol_server_client_t; 38 | 39 | typedef void (*my_protocol_server_on_foo_req_t)( 40 | struct my_protocol_server_t *self_p, 41 | struct my_protocol_server_client_t *client_p, 42 | struct my_protocol_foo_req_t *message_p); 43 | 44 | typedef void (*my_protocol_server_on_bar_ind_t)( 45 | struct my_protocol_server_t *self_p, 46 | struct my_protocol_server_client_t *client_p, 47 | struct my_protocol_bar_ind_t *message_p); 48 | 49 | typedef void (*my_protocol_server_on_fie_rsp_t)( 50 | struct my_protocol_server_t *self_p, 51 | struct my_protocol_server_client_t *client_p, 52 | struct my_protocol_fie_rsp_t *message_p); 53 | 54 | enum my_protocol_server_client_input_state_t { 55 | my_protocol_server_client_input_state_header_t = 0, 56 | my_protocol_server_client_input_state_payload_t 57 | }; 58 | 59 | typedef void (*my_protocol_server_on_client_connected_t)( 60 | struct my_protocol_server_t *self_p, 61 | struct my_protocol_server_client_t *client_p); 62 | 63 | typedef void (*my_protocol_server_on_client_disconnected_t)( 64 | struct my_protocol_server_t *self_p, 65 | struct my_protocol_server_client_t *client_p); 66 | 67 | struct my_protocol_server_t { 68 | struct { 69 | char address[16]; 70 | int port; 71 | } server; 72 | my_protocol_server_on_client_connected_t on_client_connected; 73 | my_protocol_server_on_client_disconnected_t on_client_disconnected; 74 | my_protocol_server_on_foo_req_t on_foo_req; 75 | my_protocol_server_on_bar_ind_t on_bar_ind; 76 | my_protocol_server_on_fie_rsp_t on_fie_rsp; 77 | int epoll_fd; 78 | messi_epoll_ctl_t epoll_ctl; 79 | int listener_fd; 80 | struct my_protocol_server_client_t *current_client_p; 81 | struct { 82 | struct my_protocol_server_client_t *connected_list_p; 83 | struct my_protocol_server_client_t *free_list_p; 84 | struct my_protocol_server_client_t *pending_disconnect_list_p; 85 | size_t input_buffer_size; 86 | } clients; 87 | struct { 88 | struct my_protocol_client_to_server_t *message_p; 89 | struct messi_buffer_t workspace; 90 | } input; 91 | struct { 92 | struct my_protocol_server_to_client_t *message_p; 93 | struct messi_buffer_t workspace; 94 | struct messi_buffer_t encoded; 95 | } output; 96 | }; 97 | 98 | struct my_protocol_server_client_output_item_t { 99 | size_t offset; 100 | size_t size; 101 | struct my_protocol_server_client_output_item_t *next_p; 102 | uint8_t data[1]; 103 | }; 104 | 105 | struct my_protocol_server_client_t { 106 | int client_fd; 107 | int keep_alive_timer_fd; 108 | struct { 109 | enum my_protocol_server_client_input_state_t state; 110 | struct messi_buffer_t data; 111 | size_t size; 112 | size_t left; 113 | } input; 114 | struct { 115 | struct my_protocol_server_client_output_item_t *head_p; 116 | struct my_protocol_server_client_output_item_t *tail_p; 117 | } output; 118 | struct my_protocol_server_client_t *next_p; 119 | struct my_protocol_server_client_t *prev_p; 120 | }; 121 | 122 | /** 123 | * Initialize given server. 124 | */ 125 | int my_protocol_server_init( 126 | struct my_protocol_server_t *self_p, 127 | const char *server_uri_p, 128 | struct my_protocol_server_client_t *clients_p, 129 | int clients_max, 130 | uint8_t *clients_input_bufs_p, 131 | size_t client_input_size, 132 | uint8_t *message_buf_p, 133 | size_t message_size, 134 | uint8_t *workspace_in_buf_p, 135 | size_t workspace_in_size, 136 | uint8_t *workspace_out_buf_p, 137 | size_t workspace_out_size, 138 | my_protocol_server_on_client_connected_t on_client_connected, 139 | my_protocol_server_on_client_disconnected_t on_client_disconnected, 140 | my_protocol_server_on_foo_req_t on_foo_req, 141 | my_protocol_server_on_bar_ind_t on_bar_ind, 142 | my_protocol_server_on_fie_rsp_t on_fie_rsp, 143 | int epoll_fd, 144 | messi_epoll_ctl_t epoll_ctl); 145 | 146 | /** 147 | * Start serving clients. 148 | */ 149 | int my_protocol_server_start(struct my_protocol_server_t *self_p); 150 | 151 | /** 152 | * Stop serving clients. 153 | */ 154 | void my_protocol_server_stop(struct my_protocol_server_t *self_p); 155 | 156 | /** 157 | * Process any pending events on given file descriptor if it belongs 158 | * to given server. 159 | */ 160 | void my_protocol_server_process(struct my_protocol_server_t *self_p, int fd, uint32_t events); 161 | 162 | /** 163 | * Send prepared message to given client. 164 | */ 165 | void my_protocol_server_send( 166 | struct my_protocol_server_t *self_p, 167 | struct my_protocol_server_client_t *client_p); 168 | 169 | /** 170 | * Send prepared message to current client. 171 | */ 172 | void my_protocol_server_reply(struct my_protocol_server_t *self_p); 173 | 174 | /** 175 | * Broadcast prepared message to all clients. 176 | */ 177 | void my_protocol_server_broadcast(struct my_protocol_server_t *self_p); 178 | 179 | /** 180 | * Disconnect given client. If given client is NULL, the currect 181 | * client is disconnected. 182 | */ 183 | void my_protocol_server_disconnect( 184 | struct my_protocol_server_t *self_p, 185 | struct my_protocol_server_client_t *client_p); 186 | 187 | /** 188 | * Prepare a foo_rsp message. Call `send()`, `reply()` or `broadcast()` 189 | * to send it. 190 | */ 191 | struct my_protocol_foo_rsp_t *my_protocol_server_init_foo_rsp( 192 | struct my_protocol_server_t *self_p); 193 | 194 | /** 195 | * Prepare a fie_req message. Call `send()`, `reply()` or `broadcast()` 196 | * to send it. 197 | */ 198 | struct my_protocol_fie_req_t *my_protocol_server_init_fie_req( 199 | struct my_protocol_server_t *self_p); 200 | 201 | #endif 202 | -------------------------------------------------------------------------------- /tests/files/my_protocol/my_protocol.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | // The protocol name. 4 | package my_protocol; 5 | 6 | // Messages sent from client to server. 7 | message ClientToServer { 8 | oneof messages { 9 | FooReq foo_req = 1; 10 | BarInd bar_ind = 2; 11 | FieRsp fie_rsp = 3; 12 | } 13 | } 14 | 15 | // Messages sent from server to client. 16 | message ServerToClient { 17 | oneof messages { 18 | FooRsp foo_rsp = 1; 19 | FieReq fie_req = 2; 20 | } 21 | } 22 | 23 | // Message definitions. 24 | message FooReq { 25 | } 26 | 27 | message FooRsp { 28 | } 29 | 30 | message BarInd { 31 | } 32 | 33 | message FieReq { 34 | } 35 | 36 | message FieRsp { 37 | } 38 | -------------------------------------------------------------------------------- /tests/files/my_protocol/my_protocol_client.py: -------------------------------------------------------------------------------- 1 | # This file was generated by Messi. 2 | 3 | import asyncio 4 | import logging 5 | import bitstruct 6 | 7 | import my_protocol_pb2 8 | 9 | 10 | LOGGER = logging.getLogger(__name__) 11 | 12 | CF_HEADER = bitstruct.compile('u8u24') 13 | 14 | 15 | class MessageType: 16 | 17 | CLIENT_TO_SERVER_USER = 1 18 | SERVER_TO_CLIENT_USER = 2 19 | PING = 3 20 | PONG = 4 21 | 22 | 23 | def parse_tcp_uri(uri): 24 | """Parse tcp://:. 25 | 26 | """ 27 | 28 | try: 29 | if uri[:6] != 'tcp://': 30 | raise ValueError 31 | 32 | address, port = uri[6:].split(':') 33 | 34 | return address, int(port) 35 | except (ValueError, TypeError): 36 | raise ValueError( 37 | f"Expected URI on the form tcp://:, but got '{uri}'.") 38 | 39 | 40 | class MyProtocolClient: 41 | 42 | def __init__(self, uri, keep_alive_interval=2, connect_timeout=5): 43 | self._address, self._port = parse_tcp_uri(uri) 44 | self._uri = uri 45 | self._keep_alive_interval = keep_alive_interval 46 | self._connect_timeout = connect_timeout 47 | self._reader = None 48 | self._writer = None 49 | self._task = None 50 | self._keep_alive_task = None 51 | self._pong_event = None 52 | self._output = None 53 | 54 | def start(self): 55 | """Connect to the server. `on_connected()` is called once 56 | connected. Automatic reconnect if disconnected. 57 | 58 | """ 59 | 60 | if self._task is None: 61 | self._task = asyncio.create_task(self._main()) 62 | 63 | def stop(self): 64 | """Disconnect from the server (if connected). Call `start()` to 65 | connect again. 66 | 67 | """ 68 | 69 | if self._task is not None: 70 | self._task.cancel() 71 | self._task = None 72 | 73 | def send(self): 74 | """Send prepared message to the server. 75 | 76 | """ 77 | 78 | encoded = self._output.SerializeToString() 79 | header = CF_HEADER.pack(MessageType.CLIENT_TO_SERVER_USER, len(encoded)) 80 | 81 | if self._writer is not None: 82 | self._writer.write(header + encoded) 83 | 84 | async def on_connected(self): 85 | """Called when connected to the server. 86 | 87 | """ 88 | 89 | async def on_disconnected(self): 90 | """Called when disconnected from the server. 91 | 92 | """ 93 | 94 | async def on_connect_failure(self, exception): 95 | """Called when a connection attempt to the server fails. Returns the 96 | number of seconds to wait before trying to connect again, or 97 | ``None`` never to connect again. 98 | 99 | """ 100 | 101 | if isinstance(exception, asyncio.TimeoutError): 102 | return 0 103 | else: 104 | return 1 105 | 106 | async def on_foo_rsp(self, message): 107 | """Called when a foo_rsp message is received from the server. 108 | 109 | """ 110 | 111 | async def on_fie_req(self, message): 112 | """Called when a fie_req message is received from the server. 113 | 114 | """ 115 | 116 | def init_foo_req(self): 117 | """Prepare a foo_req message. Call `send()` to send it. 118 | 119 | """ 120 | 121 | self._output = my_protocol_pb2.ClientToServer() 122 | self._output.foo_req.SetInParent() 123 | 124 | return self._output.foo_req 125 | 126 | def init_bar_ind(self): 127 | """Prepare a bar_ind message. Call `send()` to send it. 128 | 129 | """ 130 | 131 | self._output = my_protocol_pb2.ClientToServer() 132 | self._output.bar_ind.SetInParent() 133 | 134 | return self._output.bar_ind 135 | 136 | def init_fie_rsp(self): 137 | """Prepare a fie_rsp message. Call `send()` to send it. 138 | 139 | """ 140 | 141 | self._output = my_protocol_pb2.ClientToServer() 142 | self._output.fie_rsp.SetInParent() 143 | 144 | return self._output.fie_rsp 145 | 146 | async def _main(self): 147 | while True: 148 | if not await self._connect(): 149 | break 150 | 151 | await self.on_connected() 152 | self._pong_event = asyncio.Event() 153 | self._keep_alive_task = asyncio.create_task(self._keep_alive_main()) 154 | 155 | try: 156 | await self._reader_loop() 157 | except (Exception, asyncio.CancelledError) as e: 158 | LOGGER.info('Reader loop stopped by %r.', e) 159 | self._close() 160 | 161 | self._keep_alive_task.cancel() 162 | await self.on_disconnected() 163 | 164 | async def _connect(self): 165 | """Repeatedly try to connect to the server. Returns ``True`` if a 166 | connection has been established, and ``False`` otherwise. 167 | 168 | """ 169 | 170 | while True: 171 | try: 172 | self._reader, self._writer = await asyncio.wait_for( 173 | asyncio.open_connection(self._address, self._port), 174 | self._connect_timeout) 175 | 176 | return True 177 | except ConnectionRefusedError as e: 178 | LOGGER.info("Connection refused.") 179 | delay = await self.on_connect_failure(e) 180 | except asyncio.TimeoutError as e: 181 | LOGGER.info("Connect timeout.") 182 | delay = await self.on_connect_failure(e) 183 | except OSError as e: 184 | LOGGER.info("OS error: %s", e) 185 | delay = await self.on_connect_failure(e) 186 | 187 | if delay is None: 188 | return False 189 | 190 | await asyncio.sleep(delay) 191 | 192 | async def _handle_user_message(self, payload): 193 | message = my_protocol_pb2.ServerToClient() 194 | message.ParseFromString(payload) 195 | choice = message.WhichOneof('messages') 196 | 197 | if choice == 'foo_rsp': 198 | await self.on_foo_rsp(message.foo_rsp) 199 | elif choice == 'fie_req': 200 | await self.on_fie_req(message.fie_req) 201 | 202 | def _handle_pong(self): 203 | self._pong_event.set() 204 | 205 | async def _reader_loop(self): 206 | while True: 207 | header = await self._reader.readexactly(4) 208 | message_type, size = CF_HEADER.unpack(header) 209 | payload = await self._reader.readexactly(size) 210 | 211 | if message_type == MessageType.SERVER_TO_CLIENT_USER: 212 | await self._handle_user_message(payload) 213 | elif message_type == MessageType.PONG: 214 | self._handle_pong() 215 | 216 | async def _keep_alive_loop(self): 217 | while True: 218 | await asyncio.sleep(self._keep_alive_interval) 219 | self._pong_event.clear() 220 | self._writer.write(CF_HEADER.pack(MessageType.PING, 0)) 221 | await asyncio.wait_for(self._pong_event.wait(), 222 | self._keep_alive_interval) 223 | 224 | async def _keep_alive_main(self): 225 | try: 226 | await self._keep_alive_loop() 227 | except (Exception, asyncio.CancelledError) as e: 228 | LOGGER.info('Keep alive task stopped by %r.', e) 229 | self._close() 230 | 231 | def _close(self): 232 | if self._writer is not None: 233 | self._writer.close() 234 | self._writer = None 235 | -------------------------------------------------------------------------------- /tests/test_client.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import asyncio 3 | import unittest 4 | from unittest.mock import patch 5 | import shutil 6 | 7 | import messi 8 | messi.py_source.generate_files(['tests/files/chat/chat.proto'], 'both') 9 | shutil.copy('tests/files/chat/chat_pb2.py', '.') 10 | import chat_client 11 | 12 | 13 | def create_tcp_uri(listener): 14 | address, port = listener.sockets[0].getsockname() 15 | 16 | return f'tcp://{address}:{port}' 17 | 18 | 19 | class ChatClient(chat_client.ChatClient): 20 | 21 | def __init__(self, 22 | uri, 23 | keep_alive_interval=2, 24 | connection_refused_delay=0, 25 | connect_timeout_delay=0): 26 | super().__init__(uri, keep_alive_interval) 27 | self._connection_refused_delay = connection_refused_delay 28 | self._connect_timeout_delay = connect_timeout_delay 29 | self.connected_queue = asyncio.Queue() 30 | self.disconnected_queue = asyncio.Queue() 31 | self.connection_refused_queue = asyncio.Queue() 32 | self.connect_timeout_queue = asyncio.Queue() 33 | 34 | async def on_connected(self): 35 | message = self.init_connect_req() 36 | message.user = 'Erik' 37 | self.send() 38 | 39 | async def on_disconnected(self): 40 | print("Disconnected from the server.") 41 | await self.disconnected_queue.put(None) 42 | 43 | async def on_connect_failure(self, exception): 44 | if isinstance(exception, ConnectionRefusedError): 45 | print("Connection refused.") 46 | await self.connection_refused_queue.put(None) 47 | 48 | return self._connection_refused_delay 49 | elif isinstance(exception, asyncio.TimeoutError): 50 | print("Connect timeout.") 51 | await self.connect_timeout_queue.put(None) 52 | 53 | return self._connect_timeout_delay 54 | else: 55 | raise exception 56 | 57 | async def on_connect_rsp(self, message): 58 | await self.connected_queue.put(None) 59 | 60 | async def on_message_ind(self, message): 61 | print(f"<{message.user}> {message.text}"); 62 | 63 | 64 | async def server_main(listener): 65 | async with listener: 66 | try: 67 | await listener.serve_forever() 68 | except asyncio.CancelledError: 69 | pass 70 | 71 | 72 | CONNECT_REQ = b'\x01\x00\x00\x08\n\x06\n\x04Erik' 73 | CONNECT_RSP = b'\x02\x00\x00\x02\x0a\x00' 74 | PING = b'\x03\x00\x00\x00' 75 | PONG = b'\x04\x00\x00\x00' 76 | 77 | 78 | class ClientTest(unittest.TestCase): 79 | 80 | def setUp(self): 81 | self.connect_count = 0 82 | 83 | async def read_connect_req(self, reader): 84 | connect_req = await reader.readexactly(12) 85 | self.assertEqual(connect_req, CONNECT_REQ) 86 | 87 | async def read_ping(self, reader): 88 | ping = await reader.readexactly(4) 89 | self.assertEqual(ping, PING) 90 | 91 | def test_connect_disconnect(self): 92 | asyncio.run(self.connect_disconnect()) 93 | 94 | async def connect_disconnect(self): 95 | async def on_client_connected(reader, writer): 96 | await self.read_connect_req(reader) 97 | writer.write(CONNECT_RSP) 98 | writer.close() 99 | 100 | listener = await asyncio.start_server(on_client_connected, 'localhost', 0) 101 | 102 | async def client_main(): 103 | client = ChatClient(create_tcp_uri(listener)) 104 | client.start() 105 | await asyncio.wait_for(client.connected_queue.get(), 2) 106 | client.stop() 107 | listener.close() 108 | 109 | await asyncio.wait_for( 110 | asyncio.gather(server_main(listener), client_main()), 2) 111 | 112 | def test_reconnect_on_missing_pong(self): 113 | asyncio.run(self.reconnect_on_missing_pong()) 114 | 115 | async def reconnect_on_missing_pong(self): 116 | 117 | async def on_client_connected(reader, writer): 118 | if self.connect_count == 0: 119 | self.connect_count += 1 120 | await self.read_connect_req(reader) 121 | writer.write(CONNECT_RSP) 122 | await self.read_ping(reader) 123 | # Do not send pong. Wait for the client to close the 124 | # socket. 125 | self.assertEqual(await reader.read(1), b'') 126 | elif self.connect_count == 1: 127 | await self.read_connect_req(reader) 128 | writer.write(CONNECT_RSP) 129 | 130 | writer.close() 131 | 132 | listener = await asyncio.start_server(on_client_connected, 'localhost', 0) 133 | 134 | async def client_main(): 135 | client = ChatClient(create_tcp_uri(listener)) 136 | client.start() 137 | await client.connected_queue.get() 138 | await client.disconnected_queue.get() 139 | await client.connected_queue.get() 140 | client.stop() 141 | listener.close() 142 | 143 | await asyncio.wait_for( 144 | asyncio.gather(server_main(listener), client_main()), 10) 145 | 146 | def test_keep_alive(self): 147 | asyncio.run(self.keep_alive()) 148 | 149 | async def keep_alive(self): 150 | 151 | async def on_client_connected(reader, writer): 152 | await self.read_connect_req(reader) 153 | await self.read_ping(reader) 154 | writer.write(PONG) 155 | await self.read_ping(reader) 156 | writer.write(CONNECT_RSP) 157 | writer.close() 158 | 159 | listener = await asyncio.start_server(on_client_connected, 'localhost', 0) 160 | 161 | async def client_main(): 162 | client = ChatClient(create_tcp_uri(listener), keep_alive_interval=1) 163 | client.start() 164 | await asyncio.wait_for(client.connected_queue.get(), 10) 165 | client.stop() 166 | listener.close() 167 | 168 | await asyncio.wait_for( 169 | asyncio.gather(server_main(listener), client_main()), 10) 170 | 171 | def test_connection_refused(self): 172 | asyncio.run(self.connection_refused()) 173 | 174 | async def connection_refused(self): 175 | async def open_connection(host, port): 176 | raise ConnectionRefusedError() 177 | 178 | with patch('asyncio.open_connection', open_connection): 179 | client = ChatClient('tcp://1.2.3.4:5000') 180 | client.start() 181 | await client.connection_refused_queue.get() 182 | await client.connection_refused_queue.get() 183 | client.stop() 184 | 185 | def test_connect_timeout(self): 186 | asyncio.run(self.connect_timeout()) 187 | 188 | async def connect_timeout(self): 189 | async def open_connection(host, port): 190 | raise asyncio.TimeoutError() 191 | 192 | with patch('asyncio.open_connection', open_connection): 193 | client = ChatClient('tcp://1.2.3.4:5000') 194 | client.start() 195 | await client.connect_timeout_queue.get() 196 | await client.connect_timeout_queue.get() 197 | client.stop() 198 | 199 | def test_connection_refused_no_reconnect(self): 200 | asyncio.run(self.connection_refused_no_reconnect()) 201 | 202 | async def connection_refused_no_reconnect(self): 203 | async def open_connection(host, port): 204 | raise ConnectionRefusedError() 205 | 206 | with patch('asyncio.open_connection', open_connection): 207 | client = ChatClient('tcp://1.2.3.4:5000', connection_refused_delay=None) 208 | client.start() 209 | await client.connection_refused_queue.get() 210 | 211 | with self.assertRaises(asyncio.TimeoutError): 212 | await asyncio.wait_for(client.connection_refused_queue.get(), 1) 213 | 214 | client.stop() 215 | 216 | def test_connect_timeout_no_reconnect(self): 217 | asyncio.run(self.connect_timeout_no_reconnect()) 218 | 219 | async def connect_timeout_no_reconnect(self): 220 | async def open_connection(host, port): 221 | raise asyncio.TimeoutError() 222 | 223 | with patch('asyncio.open_connection', open_connection): 224 | client = ChatClient('tcp://1.2.3.4:5000', connect_timeout_delay=None) 225 | client.start() 226 | await client.connect_timeout_queue.get() 227 | 228 | with self.assertRaises(asyncio.TimeoutError): 229 | await asyncio.wait_for(client.connect_timeout_queue.get(), 1) 230 | 231 | client.stop() 232 | 233 | def test_parse_tcp_uri(self): 234 | self.assertEqual(chat_client.parse_tcp_uri('tcp://127.0.0.1:6000'), 235 | ('127.0.0.1', 6000)); 236 | 237 | def test_parse_uri_tcp_any_address(self): 238 | self.assertEqual(chat_client.parse_tcp_uri('tcp://:6000'), 239 | ('', 6000)) 240 | 241 | def test_parse_uri_tcp_bad_scheme(self): 242 | with self.assertRaises(ValueError) as cm: 243 | chat_client.parse_tcp_uri('foo://127.0.0.1:6000') 244 | 245 | self.assertEqual( 246 | str(cm.exception), 247 | "Expected URI on the form tcp://:, but got " 248 | "'foo://127.0.0.1:6000'.") 249 | 250 | def test_parse_uri_tcp_no_colon_before_port(self): 251 | with self.assertRaises(ValueError) as cm: 252 | chat_client.parse_tcp_uri('tcp://127.0.0.1 6000') 253 | 254 | self.assertEqual( 255 | str(cm.exception), 256 | "Expected URI on the form tcp://:, but got " 257 | "'tcp://127.0.0.1 6000'.") 258 | 259 | 260 | logging.basicConfig(level=logging.DEBUG) 261 | -------------------------------------------------------------------------------- /tests/test_command_line.py: -------------------------------------------------------------------------------- 1 | import os 2 | import unittest 3 | from unittest.mock import patch 4 | import shutil 5 | 6 | import messi 7 | 8 | 9 | def read_file(filename): 10 | with open(filename, 'r') as fin: 11 | return fin.read() 12 | 13 | 14 | def remove_directory(name): 15 | if os.path.exists(name): 16 | shutil.rmtree(name) 17 | 18 | 19 | class CommandLineTest(unittest.TestCase): 20 | 21 | maxDiff = None 22 | 23 | def assert_files_equal(self, actual, expected): 24 | open(expected, 'w').write(open(actual, 'r').read()) 25 | self.assertEqual(read_file(actual), read_file(expected)) 26 | 27 | def assert_file_exists(self, path): 28 | self.assertTrue(os.path.exists(path), path) 29 | 30 | def assert_file_missing(self, path): 31 | self.assertFalse(os.path.exists(path), path) 32 | 33 | def assert_generated_c_files(self, 34 | protocol, 35 | platform, 36 | client_side=True, 37 | server_side=True): 38 | if client_side: 39 | self.assert_files_equal( 40 | f'generated/{protocol}_client.h', 41 | f'tests/files/{protocol}/{platform}/{protocol}_client.h') 42 | self.assert_files_equal( 43 | f'generated/{protocol}_client.c', 44 | f'tests/files/{protocol}/{platform}/{protocol}_client.c') 45 | else: 46 | self.assert_file_missing(f'generated/{protocol}_client.h') 47 | self.assert_file_missing(f'generated/{protocol}_client.c') 48 | 49 | if server_side: 50 | self.assert_files_equal( 51 | f'generated/{protocol}_server.h', 52 | f'tests/files/{protocol}/{platform}/{protocol}_server.h') 53 | self.assert_files_equal( 54 | f'generated/{protocol}_server.c', 55 | f'tests/files/{protocol}/{platform}/{protocol}_server.c') 56 | else: 57 | self.assert_file_missing(f'generated/{protocol}_server.h') 58 | self.assert_file_missing(f'generated/{protocol}_server.c') 59 | 60 | self.assert_file_exists(f'generated/{protocol}.h') 61 | self.assert_file_exists(f'generated/{protocol}.c') 62 | 63 | def assert_generated_py_files(self, protocol, client_side=True): 64 | if client_side: 65 | self.assert_files_equal( 66 | f'generated/{protocol}_client.py', 67 | f'tests/files/{protocol}/{protocol}_client.py') 68 | else: 69 | self.assert_file_missing(f'generated/{protocol}_client.py') 70 | 71 | self.assert_file_exists( 72 | f'generated/tests/files/{protocol}/{protocol}_pb2.py') 73 | 74 | def test_generate_c_source_linux(self): 75 | protocols = [ 76 | 'chat', 77 | 'my_protocol' 78 | ] 79 | 80 | for protocol in protocols: 81 | argv = [ 82 | 'messi', 83 | 'generate_c_source', 84 | '-o', 'generated', 85 | '-p', 'linux', 86 | f'tests/files/{protocol}/{protocol}.proto' 87 | ] 88 | 89 | remove_directory('generated') 90 | os.mkdir('generated') 91 | 92 | with patch('sys.argv', argv): 93 | messi.main() 94 | 95 | self.assert_generated_c_files(protocol, 'linux') 96 | 97 | def test_generate_c_source_async(self): 98 | protocols = [ 99 | 'chat', 100 | 'my_protocol' 101 | ] 102 | 103 | for protocol in protocols: 104 | argv = [ 105 | 'messi', 106 | 'generate_c_source', 107 | '-o', 'generated', 108 | '-p', 'async', 109 | f'tests/files/{protocol}/{protocol}.proto' 110 | ] 111 | 112 | remove_directory('generated') 113 | os.mkdir('generated') 114 | 115 | with patch('sys.argv', argv): 116 | messi.main() 117 | 118 | self.assert_generated_c_files(protocol, 'async') 119 | 120 | def test_generate_c_source_side(self): 121 | datas = [ 122 | ('both', True, True), 123 | ('client', True, False), 124 | ('server', False, True) 125 | ] 126 | 127 | for side, client_side, server_side in datas: 128 | argv = [ 129 | 'messi', 130 | 'generate_c_source', 131 | '-o', 'generated', 132 | '-s', side, 133 | f'tests/files/chat/chat.proto' 134 | ] 135 | 136 | remove_directory('generated') 137 | os.mkdir('generated') 138 | 139 | with patch('sys.argv', argv): 140 | messi.main() 141 | 142 | self.assert_generated_c_files('chat', 'linux', client_side, server_side) 143 | 144 | def test_generate_c_source_linux_imported(self): 145 | argv = [ 146 | 'messi', 147 | 'generate_c_source', 148 | '-o', 'generated', 149 | '-p', 'linux', 150 | '-I', 'tests/files/imported', 151 | 'tests/files/imported/types_not_package_name.proto', 152 | 'tests/files/imported/imported.proto' 153 | ] 154 | 155 | remove_directory('generated') 156 | os.mkdir('generated') 157 | 158 | with patch('sys.argv', argv): 159 | messi.main() 160 | 161 | self.assert_generated_c_files('imported', 'linux') 162 | self.assert_file_missing('generated/types_not_package_name_server.h') 163 | self.assert_file_missing('generated/types_not_package_name_server.c') 164 | self.assert_file_missing('generated/types_not_package_name_client.h') 165 | self.assert_file_missing('generated/types_not_package_name_client.c') 166 | self.assert_file_exists('generated/types_not_package_name.h') 167 | self.assert_file_exists('generated/types_not_package_name.c') 168 | 169 | def test_generate_py_source(self): 170 | protocols = [ 171 | 'chat', 172 | 'my_protocol' 173 | ] 174 | 175 | for protocol in protocols: 176 | argv = [ 177 | 'messi', 178 | 'generate_py_source', 179 | '-o', 'generated', 180 | f'tests/files/{protocol}/{protocol}.proto' 181 | ] 182 | 183 | remove_directory('generated') 184 | os.mkdir('generated') 185 | 186 | with patch('sys.argv', argv): 187 | messi.main() 188 | 189 | self.assert_generated_py_files(protocol) 190 | 191 | def test_generate_py_source_side(self): 192 | datas = [ 193 | ('both', True), 194 | ('client', True), 195 | ('server', False) 196 | ] 197 | 198 | for side, client_side in datas: 199 | argv = [ 200 | 'messi', 201 | 'generate_py_source', 202 | '-o', 'generated', 203 | '-s', side, 204 | f'tests/files/chat/chat.proto' 205 | ] 206 | 207 | remove_directory('generated') 208 | os.mkdir('generated') 209 | 210 | with patch('sys.argv', argv): 211 | messi.main() 212 | 213 | self.assert_generated_py_files('chat', client_side) 214 | -------------------------------------------------------------------------------- /tst/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(MAKE) -C linux 3 | $(MAKE) -C async 4 | $(MAKE) -C messi 5 | -------------------------------------------------------------------------------- /tst/async/Makefile: -------------------------------------------------------------------------------- 1 | TESTS += test_chat_client.c 2 | TESTS += test_chat_server.c 3 | SRC += chat/chat.c 4 | SRC += chat/chat_server.c 5 | SRC += chat/chat_client.c 6 | SRC += my_protocol/my_protocol.c 7 | SRC += my_protocol/my_protocol_server.c 8 | SRC += my_protocol/my_protocol_client.c 9 | SRC += ../../3pp/pbtools/lib/src/pbtools.c 10 | SRC += ../../lib/src/messi.c 11 | INC += chat 12 | INC += imported 13 | INC += ../../lib/include 14 | INC += ../../3pp/pbtools/lib/include 15 | INC += ../../3pp/async/include 16 | INC += ../../3pp/async/3pp/mbedtls/include 17 | NO_IMPLEMENTATION += async_* 18 | 19 | default: messi-generate 20 | $(MAKE) all 21 | 22 | messi-generate: 23 | @echo "Generating Messi files." 24 | for protocol in "chat" "my_protocol" ; do \ 25 | mkdir -p $$protocol && \ 26 | PYTHONPATH=../.. \ 27 | python3 -m messi -d generate_c_source \ 28 | -o $$protocol -p async \ 29 | ../../tests/files/$$protocol/$$protocol.proto ; \ 30 | done 31 | 32 | include ../test.mk 33 | -------------------------------------------------------------------------------- /tst/async/test_chat_server.c: -------------------------------------------------------------------------------- 1 | #include "nala.h" 2 | #include "chat_server.h" 3 | 4 | TEST(connect_and_disconnect_clients) 5 | { 6 | async_stcp_server_init_mock_none(); 7 | async_stcp_server_add_client_mock_none(); 8 | async_stcp_server_start_mock_none(); 9 | async_stcp_server_stop_mock_none(); 10 | async_stcp_server_client_write_mock_none(); 11 | async_stcp_server_client_read_mock_none(); 12 | } 13 | -------------------------------------------------------------------------------- /tst/linux/Makefile: -------------------------------------------------------------------------------- 1 | TESTS += test_chat_server.c 2 | TESTS += test_chat_client.c 3 | SRC += chat/chat.c 4 | SRC += chat/chat_server.c 5 | SRC += chat/chat_client.c 6 | SRC += imported/imported.c 7 | SRC += imported/imported_server.c 8 | SRC += imported/imported_client.c 9 | SRC += imported/types_not_package_name.c 10 | SRC += my_protocol/my_protocol.c 11 | SRC += my_protocol/my_protocol_server.c 12 | SRC += my_protocol/my_protocol_client.c 13 | SRC += ../../3pp/pbtools/lib/src/pbtools.c 14 | SRC += ../../lib/src/messi.c 15 | INC += chat 16 | INC += imported 17 | INC += ../../lib/include 18 | INC += ../../3pp/pbtools/lib/include 19 | 20 | default: messi-generate 21 | $(MAKE) all 22 | 23 | messi-generate: 24 | @echo "Generating Messi files." 25 | for protocol in "chat" "my_protocol" ; do \ 26 | mkdir -p $$protocol && \ 27 | PYTHONPATH=../.. \ 28 | python3 -m messi -d generate_c_source \ 29 | -o $$protocol -p linux \ 30 | ../../tests/files/$$protocol/$$protocol.proto ; \ 31 | done 32 | mkdir -p imported 33 | PYTHONPATH=../.. \ 34 | python3 -m messi -d generate_c_source \ 35 | -o imported -p linux -I ../../tests/files/imported \ 36 | ../../tests/files/imported/types_not_package_name.proto \ 37 | ../../tests/files/imported/imported.proto 38 | 39 | include ../test.mk 40 | -------------------------------------------------------------------------------- /tst/messi/Makefile: -------------------------------------------------------------------------------- 1 | TESTS += test_messi.c 2 | SRC += ../../lib/src/messi.c 3 | INC += ../../lib/include 4 | 5 | include ../test.mk 6 | -------------------------------------------------------------------------------- /tst/messi/test_messi.c: -------------------------------------------------------------------------------- 1 | #include "nala.h" 2 | #include "messi.h" 3 | 4 | TEST(parse_uri_tcp) 5 | { 6 | const char uri[] = "tcp://127.0.0.1:6000"; 7 | char host[16]; 8 | int port; 9 | 10 | ASSERT_EQ(messi_parse_tcp_uri(&uri[0], &host[0], sizeof(host), &port), 0); 11 | ASSERT_EQ(&host[0], "127.0.0.1"); 12 | ASSERT_EQ(port, 6000); 13 | } 14 | 15 | TEST(parse_uri_tcp_any_address) 16 | { 17 | const char uri[] = "tcp://:6000"; 18 | char host[16]; 19 | int port; 20 | 21 | ASSERT_EQ(messi_parse_tcp_uri(&uri[0], &host[0], sizeof(host), &port), 0); 22 | ASSERT_EQ(&host[0], ""); 23 | ASSERT_EQ(port, 6000); 24 | } 25 | 26 | TEST(parse_uri_tcp_long_address) 27 | { 28 | const char uri[] = "tcp://123.456.789.876:6001"; 29 | char host[16]; 30 | int port; 31 | 32 | ASSERT_EQ(messi_parse_tcp_uri(&uri[0], &host[0], sizeof(host), &port), 0); 33 | ASSERT_EQ(&host[0], "123.456.789.876"); 34 | ASSERT_EQ(port, 6001); 35 | } 36 | 37 | TEST(parse_uri_tcp_small_host_buffer) 38 | { 39 | const char uri[] = "tcp://127.0.0.1:6000"; 40 | char host[9]; 41 | int port; 42 | 43 | ASSERT_EQ(messi_parse_tcp_uri(&uri[0], &host[0], sizeof(host), &port), -1); 44 | } 45 | 46 | TEST(parse_uri_tcp_bad_scheme) 47 | { 48 | const char uri[] = "foo://127.0.0.1:6000"; 49 | char host[16]; 50 | int port; 51 | 52 | ASSERT_EQ(messi_parse_tcp_uri(&uri[0], &host[0], sizeof(host), &port), -1); 53 | } 54 | 55 | TEST(parse_uri_tcp_no_colon_before_port) 56 | { 57 | const char uri[] = "tcp://127.0.0.1 6000"; 58 | char host[16]; 59 | int port; 60 | 61 | ASSERT_EQ(messi_parse_tcp_uri(&uri[0], &host[0], sizeof(host), &port), -1); 62 | } 63 | 64 | TEST(parse_uri_tcp_only_scheme) 65 | { 66 | const char uri[] = "tcp://"; 67 | char host[16]; 68 | int port; 69 | 70 | ASSERT_EQ(messi_parse_tcp_uri(&uri[0], &host[0], sizeof(host), &port), -1); 71 | } 72 | 73 | TEST(disconnect_reason_string) 74 | { 75 | ASSERT_EQ(messi_disconnect_reason_string( 76 | messi_disconnect_reason_message_encode_error_t), 77 | "Message encode error."); 78 | ASSERT_EQ(messi_disconnect_reason_string( 79 | messi_disconnect_reason_message_decode_error_t), 80 | "Message decode error."); 81 | ASSERT_EQ(messi_disconnect_reason_string( 82 | messi_disconnect_reason_connection_closed_t), 83 | "Connection closed."); 84 | ASSERT_EQ(messi_disconnect_reason_string( 85 | messi_disconnect_reason_keep_alive_timeout_t), 86 | "Keep alive timeout."); 87 | ASSERT_EQ(messi_disconnect_reason_string( 88 | messi_disconnect_reason_general_error_t), 89 | "General error."); 90 | ASSERT_EQ(messi_disconnect_reason_string( 91 | messi_disconnect_reason_message_too_big_t), 92 | "Message too big."); 93 | } 94 | -------------------------------------------------------------------------------- /tst/test.mk: -------------------------------------------------------------------------------- 1 | NALA ?= nala 2 | BUILD = build 3 | EXE = $(BUILD)/app 4 | INC += $(BUILD) 5 | INC += $(CURDIR) 6 | INC += $(shell $(NALA) include_dir) 7 | SRC += $(BUILD)/nala_mocks.c 8 | SRC += $(shell $(NALA) c_sources) 9 | SRC += $(TESTS) 10 | # To evaluate once for fewer nala include_dir/c_sources calls. 11 | INC := $(INC) 12 | SRC := $(SRC) 13 | OBJ = $(patsubst %,$(BUILD)%,$(abspath $(SRC:%.c=%.o))) 14 | OBJDEPS = $(OBJ:%.o=%.d) 15 | MOCKGENDEPS = $(BUILD)/nala_mocks.ldflags.d 16 | DEPS = $(OBJDEPS) $(MOCKGENDEPS) 17 | CFLAGS += $(INC:%=-I%) 18 | CFLAGS += -g 19 | CFLAGS += -O0 20 | CFLAGS += -no-pie 21 | CFLAGS += -coverage 22 | CFLAGS += -Wall 23 | CFLAGS += -Wextra 24 | CFLAGS += -Wpedantic 25 | CFLAGS += -Werror 26 | CFLAGS += -Wno-unused-command-line-argument 27 | ifeq ($(SANITIZE), yes) 28 | CFLAGS += -fsanitize=address 29 | CFLAGS += -fsanitize=undefined 30 | endif 31 | CFLAGS += -DNALA_INCLUDE_NALA_MOCKS_H 32 | MOCKGENFLAGS += $(IMPLEMENTATION:%=-i %) 33 | MOCKGENFLAGS += $(NO_IMPLEMENTATION:%=-n %) 34 | REPORT_JSON = $(BUILD)/report.json 35 | EXEARGS += $(ARGS) 36 | EXEARGS += $(JOBS:%=-j %) 37 | EXEARGS += $(REPORT_JSON:%=-r %) 38 | LIBS ?= 39 | 40 | .PHONY: all build generate clean coverage gdb gdb-run auto auto-run help 41 | 42 | all: build 43 | $(EXE) $(EXEARGS) 44 | 45 | auto: 46 | $(MAKE) || true 47 | while true ; do \ 48 | $(MAKE) auto-run ; \ 49 | done 50 | 51 | auto-run: 52 | for f in $(OBJDEPS) ; do \ 53 | ls -1 $$(cat $$f | sed s/\\\\//g | sed s/.*://g) ; \ 54 | done | sort | uniq | grep -v $(BUILD) | entr -d -p $(MAKE) 55 | 56 | build: generate 57 | $(MAKE) $(EXE) 58 | 59 | generate: $(BUILD)/nala_mocks.ldflags 60 | 61 | clean: 62 | rm -rf $(BUILD) 63 | 64 | coverage: 65 | gcovr $(GCOVR_ARGS) --html-details --output index.html $(BUILD) 66 | mkdir -p $(BUILD)/coverage 67 | mv index.* $(BUILD)/coverage 68 | @echo "Code coverage report: $$(readlink -f $(BUILD)/coverage/index.html)" 69 | 70 | # Recursive make for helpful output. 71 | gdb: 72 | test_file_func=$$($(EXE) --print-test-file-func $(TEST)) && \ 73 | $(MAKE) gdb-run TEST_FILE_FUNC=$$test_file_func 74 | 75 | gdb-run: 76 | gdb \ 77 | -ex "b $(TEST_FILE_FUNC)_before_fork" \ 78 | -ex "r $(TEST)" \ 79 | -ex "set follow-fork-mode child" \ 80 | -ex c \ 81 | $(EXE) 82 | 83 | help: 84 | @echo "TARGET DESCRIPTION" 85 | @echo "---------------------------------------------------------" 86 | @echo "all Build and run with given ARGS." 87 | @echo "auto Build and run with given ARGS on source change." 88 | @echo "clean Remove build output." 89 | @echo "coverage Create the code coverage report." 90 | @echo "gdb Debug given test TEST with gdb." 91 | 92 | $(EXE): $(OBJ) 93 | echo "LD $@" 94 | $(CC) $(CFLAGS) @$(BUILD)/nala_mocks.ldflags $^ $(LIBS=%=-l%) -o $@ 95 | 96 | define COMPILE_template 97 | $(patsubst %.c,$(BUILD)%.o,$(abspath $1)): $1 98 | @echo "CC $1" 99 | mkdir -p $$(@D) 100 | $$(CC) -MMD $$(CFLAGS) -c -o $$@ $$< 101 | $(NALA) wrap_internal_symbols $(BUILD)/nala_mocks.ldflags $$@ 102 | endef 103 | $(foreach file,$(SRC),$(eval $(call COMPILE_template,$(file)))) 104 | 105 | $(BUILD)/nala_mocks.ldflags: $(TESTS) 106 | echo "MOCKGEN $(TESTS)" 107 | mkdir -p $(@D) 108 | [ -f $(BUILD)/nala_mocks.h ] || touch $(BUILD)/nala_mocks.h 109 | $(NALA) cat $(TESTS) \ 110 | | $(CC) $(CFLAGS) -DNALA_GENERATE_MOCKS -x c -E - \ 111 | | $(NALA) generate_mocks $(MOCKGENFLAGS) -o $(BUILD) 112 | cat $(TESTS) \ 113 | | $(CC) -MM -MT $@ $(CFLAGS) -x c -o $@.d - 114 | touch $@ 115 | 116 | -include $(DEPS) 117 | --------------------------------------------------------------------------------