[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset      = utf-8\nindent_style = tab\nindent_size  = 4\nend_of_line  = lf\nmax_line_length = 100\ninsert_final_newline     = true\ntrim_trailing_whitespace = true\n\n[*.md]\ntrim_trailing_whitespace = false\nmax_line_length = 80\n"
  },
  {
    "path": ".gitattributes",
    "content": "*.c   eol=lf\n*.cpp eol=lf\n*.h   eol=lf\n*.sc  eol=lf\n*.sh  eol=lf\n*.m   eol=lf\n*.mm  eol=lf\n*.md  eol=lf\n*.lua eol=lf\n*.mk  eol=lf\nmakefile eol=lf\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: [bkaradzic]\n"
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "name: CI\n\nconcurrency:\n  group: ${{ github.ref }}\n  cancel-in-progress: true\n\non:\n  push:\n  pull_request:\n\njobs:\n  msvc:\n    strategy:\n      fail-fast: true\n      matrix:\n        include: [\n          { config: Debug,   platform: x64, bindir: 'win64_vs2022', genie-action: 'vs2022', solution-ext: 'sln' },\n          { config: Release, platform: x64, bindir: 'win64_vs2022', genie-action: 'vs2022', solution-ext: 'sln' },\n        ]\n    name: msvc-${{ matrix.config }}-${{ matrix.platform }}\n    runs-on: windows-latest\n    steps:\n      - name: Checkout bnet\n        uses: actions/checkout@v6\n        with:\n          path: bnet\n      - name: Checkout bx\n        uses: actions/checkout@v6\n        with:\n          repository: bkaradzic/bx\n          path: bx\n      - name: Prepare\n        uses: microsoft/setup-msbuild@v2\n      - name: Build\n        shell: cmd\n        run: |\n          cd bnet\n          ..\\bx\\tools\\bin\\windows\\genie.exe ${{ matrix.genie-action }}\n          msbuild \".build/projects/${{ matrix.genie-action }}/bnet.${{ matrix.solution-ext }}\" /m /v:minimal /p:Configuration=${{ matrix.config }} /p:Platform=${{ matrix.platform }}\n  mingw:\n    strategy:\n      fail-fast: true\n      matrix:\n        include: [\n          { msystem: MINGW64, project: 'mingw-gcc',   bindir: 'win64_mingw-gcc'   },\n          { msystem: CLANG64, project: 'mingw-clang', bindir: 'win64_mingw-clang' },\n        ]\n    name: mingw-${{ matrix.msystem }}\n    runs-on: windows-2022\n    steps:\n      - name: Checkout bnet\n        uses: actions/checkout@v6\n        with:\n          path: bnet\n      - name: Checkout bx\n        uses: actions/checkout@v6\n        with:\n          repository: bkaradzic/bx\n          path: bx\n      - name: Prepare\n        uses: msys2/setup-msys2@v2\n        with:\n          msystem: ${{ matrix.msystem }}\n          update: true\n          install: make\n          pacboy: cc:p\n      - name: Build\n        shell: msys2 {0}\n        run: |\n          cd bnet\n          make ${{ matrix.project }}-release64 -j$(nproc) AR=ar CC=cc CXX=c++ MINGW=$MINGW_PREFIX\n  linux:\n    strategy:\n      fail-fast: true\n      matrix:\n        include: [\n          { compiler: gcc,   config: debug,   binsuffix: Debug,   bindir: linux64_gcc   },\n          { compiler: gcc,   config: release, binsuffix: Release, bindir: linux64_gcc   },\n          { compiler: clang, config: debug,   binsuffix: Debug,   bindir: linux64_clang },\n          { compiler: clang, config: release, binsuffix: Release, bindir: linux64_clang },\n        ]\n    name: linux-${{ matrix.compiler }}-${{ matrix.config }}64\n    runs-on: ubuntu-24.04\n    steps:\n      - name: Checkout bnet\n        uses: actions/checkout@v6\n        with:\n          path: bnet\n      - name: Checkout bx\n        uses: actions/checkout@v6\n        with:\n          repository: bkaradzic/bx\n          path: bx\n      - name: Build\n        run: |\n          cd bnet\n          make -j$(nproc) linux-${{ matrix.compiler }}-${{ matrix.config }}64\n  osx:\n    strategy:\n      fail-fast: true\n      matrix:\n        include: [\n          { runs-on: macos-14, arch: osx-arm64, bindir: osx-arm64, config: debug,   binsuffix: Debug   },\n          { runs-on: macos-14, arch: osx-arm64, bindir: osx-arm64, config: release, binsuffix: Release },\n        ]\n    name: osx-${{ matrix.arch }}-${{ matrix.config }}\n    runs-on: ${{ matrix.runs-on }}\n    steps:\n      - name: Checkout bnet\n        uses: actions/checkout@v6\n        with:\n          path: bnet\n      - name: Checkout bx\n        uses: actions/checkout@v6\n        with:\n          repository: bkaradzic/bx\n          path: bx\n      - name: Build\n        run: |\n          cd bnet\n          make -j$(sysctl -n hw.physicalcpu) ${{ matrix.arch }}-${{ matrix.config }}\n  android:\n    strategy:\n      fail-fast: true\n      matrix:\n        include: [\n          { project: android-arm,   bindir: android-arm   },\n          { project: android-arm64, bindir: android-arm64 },\n        ]\n    name: ${{ matrix.project }}\n    runs-on: ubuntu-24.04\n    steps:\n      - name: Checkout bnet\n        uses: actions/checkout@v6\n        with:\n          path: bnet\n      - name: Checkout bx\n        uses: actions/checkout@v6\n        with:\n          repository: bkaradzic/bx\n          path: bx\n      - uses: nttld/setup-ndk@v1\n        id: setup-ndk\n        with:\n          ndk-version: r27c\n      - name: Build\n        env:\n          ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}\n          ANDROID_NDK_ARM: ${{ steps.setup-ndk.outputs.ndk-path }}\n          ANDROID_NDK_ARM64: ${{ steps.setup-ndk.outputs.ndk-path }}\n        run: |\n          cd bnet\n          make -j$(nproc) ${{ matrix.project }}-release\n  emscripten:\n    strategy:\n      fail-fast: true\n      matrix:\n        include: [\n          { config: debug,   binsuffix: Debug   },\n          { config: release, binsuffix: Release },\n        ]\n    name: wasm-${{ matrix.config }}\n    runs-on: ubuntu-24.04\n    steps:\n      - name: Checkout bnet\n        uses: actions/checkout@v6\n        with:\n          path: bnet\n      - name: Checkout bx\n        uses: actions/checkout@v6\n        with:\n          repository: bkaradzic/bx\n          path: bx\n      - uses: mymindstorm/setup-emsdk@v14\n        with:\n          version: 5.0.2\n      - name: Build\n        run: |\n          cd bnet\n          make -j$(nproc) wasm-${{ matrix.config }} EMSCRIPTEN=$EMSDK/upstream/emscripten\n"
  },
  {
    "path": ".gitignore",
    "content": ".build/\n.debug/\n.svn/\ntags\n.DS_Store\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2010-2026 Branimir Karadzic\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n   1. Redistributions of source code must retain the above copyright notice, this\n      list of conditions and the following disclaimer.\n\n   2. Redistributions in binary form must reproduce the above copyright notice,\n      this list of conditions and the following disclaimer in the documentation\n      and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\nIN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\nINDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\nBUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\nOF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\nOR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\nOF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "[bnet](https://github.com/bkaradzic/bnet) - Message oriented networking library\n===============================================================================\n\nMessage oriented networking library using TCP transport.\n\n[![GitHub Actions](https://github.com/bkaradzic/bnet/actions/workflows/main.yml/badge.svg)](https://github.com/bkaradzic/bnet/actions)\n[![License](https://img.shields.io/badge/license-BSD--2%20clause-blue.svg)](https://bkaradzic.github.io/bgfx/license.html)\n[![Join the chat at https://discord.gg/9eMbv7J](https://img.shields.io/discord/712512073522872352?color=%237289DA&label=bnet&logo=discord&logoColor=white)](https://discord.gg/9eMbv7J)\n\nContact\n-------\n\n[@bkaradzic](https://twitter.com/bkaradzic)  \n\nProject page  \nhttps://github.com/bkaradzic/bnet\n\n[License (BSD 2-clause)](https://github.com/bkaradzic/bnet/blob/master/LICENSE)\n-------------------------------------------------------------------------------\n\n<a href=\"http://opensource.org/licenses/BSD-2-Clause\" target=\"_blank\">\n<img align=\"right\" src=\"https://opensource.org/wp-content/uploads/2022/10/osi-badge-dark.svg\" width=\"100\" height=\"137\">\n</a>\n\n\tCopyright 2010-2026 Branimir Karadzic\n\t\n\tRedistribution and use in source and binary forms, with or without modification,\n\tare permitted provided that the following conditions are met:\n\t\n\t   1. Redistributions of source code must retain the above copyright notice, this\n\t      list of conditions and the following disclaimer.\n\t\n\t   2. Redistributions in binary form must reproduce the above copyright notice,\n\t      this list of conditions and the following disclaimer in the documentation\n\t      and/or other materials provided with the distribution.\n\t\n\tTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n\tANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n\tWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n\tIN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\n\tINDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n\tBUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n\tDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n\tOF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\n\tOR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\n\tOF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "examples/00-chat/chat.cpp",
    "content": "/*\n * Copyright 2010-2026 Branimir Karadzic. All rights reserved.\n * License: https://github.com/bkaradzic/bnet/blob/master/LICENSE\n */\n\n#include <bnet/bnet.h>\n\n#include <stdio.h>\n#include <string.h>\n#include <set>\n#include <malloc.h>\n\n#include <bx/string.h>\n#include <bx/commandline.h>\n\nstatic const char* s_certs[] = {\n\t\"-----BEGIN CERTIFICATE-----\\n\"\n\t\"MIICDTCCAXYCCQDQyM1G6kagwzANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJV\\n\"\n\t\"UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHU2VhdHRsZTEVMBMGA1UE\\n\"\n\t\"ChMMQ2FyYm9uIEdhbWVzMB4XDTExMDgxMzIxMDY1N1oXDTExMDkxMjIxMDY1N1ow\\n\"\n\t\"SzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Nl\\n\"\n\t\"YXR0bGUxFTATBgNVBAoTDENhcmJvbiBHYW1lczCBnzANBgkqhkiG9w0BAQEFAAOB\\n\"\n\t\"jQAwgYkCgYEArVlCwFQMdhbitUzhuXb8r5NNjr3mgJAfXqpgG19r8IQDI/ueD4DY\\n\"\n\t\"ueLa+34VVWwaP27D3XIrAi7+WmGzUGf47b138F98E49HAYyoHnr/ww1mCGU4Jf8t\\n\"\n\t\"6JDREeyjqaFKhU1+zNjsyonooL1tPpw7u5fwjdrc6PX0qCKHHdLU+Q0CAwEAATAN\\n\"\n\t\"BgkqhkiG9w0BAQUFAAOBgQBm+bpTDyJttfpMeKkfmtZH828fX7qzdoj9Qs+G6Dyc\\n\"\n\t\"NqROo3yQiZfT8rhvyU9MdEhQcE53Eh+RA4uqAz3vkQH39mRcyrcyO3ktvCUC+0YY\\n\"\n\t\"WT7lu6St2t+/RgXY6ghaYCb8ko31HIJYcINZUXSBtpDYeZtRCS/nUb6LelMaOXDE\\n\"\n\t\"1A==\\n\"\n\t\"-----END CERTIFICATE-----\\n\"\n\t,\n\tNULL\n};\n\nstatic const char* s_key =\n\t\"-----BEGIN RSA PRIVATE KEY-----\\n\"\n\t\"MIICXAIBAAKBgQCtWULAVAx2FuK1TOG5dvyvk02OveaAkB9eqmAbX2vwhAMj+54P\\n\"\n\t\"gNi54tr7fhVVbBo/bsPdcisCLv5aYbNQZ/jtvXfwX3wTj0cBjKgeev/DDWYIZTgl\\n\"\n\t\"/y3okNER7KOpoUqFTX7M2OzKieigvW0+nDu7l/CN2tzo9fSoIocd0tT5DQIDAQAB\\n\"\n\t\"AoGBAImw1Oyf1iYWl40avFDsyllLz9cJ0AVedQxkmGIlsT8iHLyAKFR4K627G+WX\\n\"\n\t\"iKqJa2/nM3y6Kp9ZZH+2CxBbBcXBib9thsv3uZ0GPVU03RVZoOc+oia1BefhQ8qg\\n\"\n\t\"3nZd8yWe99a+VJnDrPA+QJ/qrL7KrstqKcYkOB35Yt+iDCUBAkEA44/sAt77sIlJ\\n\"\n\t\"1YB6RAL1G0Ocq3X9MQXGXFd6atRP+Lld1gfgvT1H01y6ERXCD4g6+dc+ICcIeuCi\\n\"\n\t\"9nJULJ9VGQJBAMMC9jO+65P2n10CmnyIiYk3nzoWAsPxxyG64XymmG1PZU1hffV3\\n\"\n\t\"wx1uztDs/v4F9OBxF+Xh+mC6Ovwofj7MrhUCQHGNvOjF2nSCXYyjet97VlIPkBtj\\n\"\n\t\"Wj/fMNedc2HhpjJoVXHbJoNoE/JdwB+MavUTNtK7XK3wrGOcutUdwfEuZOkCQFLA\\n\"\n\t\"VP1MTOcyxhlP24Jw5fwGUFjzsiS32kpj5P9iKlhoUpJthme9dFxvAvABQYtFt83t\\n\"\n\t\"77grFnYpUJJkFH5NmKkCQBk80OK/HBQNlmDkcdFpSDgAH3qHxcljL4XOopDNz5fe\\n\"\n\t\"cW6N7/0QrO2GhTp7JNXIdYx35iTHTY6poO0uNAwdrgE=\\n\"\n\t\"-----END RSA PRIVATE KEY-----\\n\"\n\t;\n\nvoid printMsg(const bnet::Message* _msg)\n{\n\tuint16_t len = _msg->size;\n\tchar* temp = (char*)BX_STACK_ALLOC(len);\n\tbx::memCopy(temp, &_msg->data[1], len-1);\n\ttemp[len-1] = '\\0';\n\tprintf(\"UserMessage %d: %s\\n\", _msg->data[0], temp);\n}\n\nint main(int _argc, const char* _argv[])\n{\n\tbx::CommandLine cmdLine(_argc, _argv);\n\n\tuint16_t port = 1337;\n\tconst char* portOpt = cmdLine.findOption('p');\n\tif (NULL != portOpt)\n\t{\n\t\tint32_t result;\n\t\tif (bx::fromString(&result, portOpt)\n\t\t&&  result < UINT16_MAX)\n\t\t{\n\t\t\tport = uint16_t(result);\n\t\t}\n\t}\n\n\tbool server = cmdLine.hasArg('s', \"server\");\n\tif (server)\n\t{\n\t\tbnet::init(10, 1, s_certs);\n\t\tuint32_t ip = bnet::toIpv4(\"localhost\");\n\t\tbnet::listen(ip, port, false, s_certs[0], s_key);\n\t}\n\telse\n\t{\n\t\tbnet::init(1, 0, s_certs);\n\n\t\tconst char* host = cmdLine.findOption('h', \"host\");\n\t\tuint32_t ip = bnet::toIpv4(NULL == host ? \"localhost\" : host);\n\t\tbnet::Handle handle = bnet::connect(ip, port, false, true);\n\n\t\tconst char* hello = \"hello there!\";\n\t\tuint16_t len = (uint16_t)strlen(hello);\n\t\tbnet::Message* msg = bnet::alloc(handle, len+1);\n\t\tmsg->data[0] = bnet::MessageId::UserDefined;\n\t\tbx::memCopy(&msg->data[1], hello, len);\n\t\tbnet::send(msg);\n\t}\n\n\tbool cont = true;\n\twhile (server || cont)\n\t{\n\t\tbnet::Message* msg = bnet::recv();\n\t\tif (NULL != msg)\n\t\t{\n\t\t\tif (bnet::MessageId::UserDefined > msg->data[0])\n\t\t\t{\n\t\t\t\tswitch (msg->data[0])\n\t\t\t\t{\n\t\t\t\tcase bnet::MessageId::ListenFailed:\n\t\t\t\t\tprintf(\"Listen failed port is already in use?\\n\");\n\t\t\t\t\tcont = server = false;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase bnet::MessageId::IncomingConnection:\n\t\t\t\t\t{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbnet::Handle listen = { *( (uint16_t*)&msg->data[1]) };\n\t\t\t\t\t\t\tuint32_t rip = *( (uint32_t*)&msg->data[3]);\n\t\t\t\t\t\t\tuint16_t rport = *( (uint16_t*)&msg->data[7]);\n\n\t\t\t\t\t\t\tprintf(\"%d.%d.%d.%d:%d connected\\n\"\n\t\t\t\t\t\t\t\t, rip>>24\n\t\t\t\t\t\t\t\t, (rip>>16)&0xff\n\t\t\t\t\t\t\t\t, (rip>>8)&0xff\n\t\t\t\t\t\t\t\t, rip&0xff\n\t\t\t\t\t\t\t\t, rport\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tbnet::stop(listen);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tuint32_t ip = bnet::toIpv4(\"localhost\");\n\t\t\t\t\t\tbnet::listen(ip, port, false, s_certs[0], s_key);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase bnet::MessageId::LostConnection:\n\t\t\t\t\tprintf(\"disconnected\\n\");\n\t\t\t\t\tcont = false;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase bnet::MessageId::ConnectFailed:\n\t\t\t\t\tprintf(\"%d\\n\", msg->data[0]);\n\t\t\t\t\tcont = false;\n\t\t\t\t\tbnet::disconnect(msg->handle);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase bnet::MessageId::RawData:\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\t// fail...\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tprintMsg(msg);\n\n\t\t\t\tbnet::Handle handle = msg->handle;\n\n\t\t\t\t{\n\t\t\t\t\tconst char* ping = \"ping!\";\n\t\t\t\t\tconst char* pong = \"pong!\";\n\t\t\t\t\tconst char* hello = server ? ping : pong;\n\t\t\t\t\tuint16_t len = (uint16_t)strlen(hello);\n\t\t\t\t\tbnet::Message* omsg = bnet::alloc(handle, len+1);\n\t\t\t\t\tomsg->data[0] = bnet::MessageId::UserDefined+1;\n\t\t\t\t\tbx::memCopy(&omsg->data[1], hello, len);\n\t\t\t\t\tbnet::send(omsg);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tbnet::release(msg);\n\t\t}\n\t}\n\n\tbnet::shutdown();\n\treturn bx::kExitSuccess;\n}\n"
  },
  {
    "path": "examples/01-http/http.cpp",
    "content": "/*\n * Copyright 2010-2026 Branimir Karadzic. All rights reserved.\n * License: https://github.com/bkaradzic/bnet/blob/master/LICENSE\n */\n\n#include <bnet/bnet.h>\n\n#include <malloc.h>\n\n#include <bx/string.h>\n#include <bx/url.h>\n#include <bx/file.h>\n\nbnet::Handle httpSendRequest(uint32_t _ip, uint16_t _port, const char* _request, bool secure)\n{\n\tbnet::Handle handle = bnet::connect(_ip, _port, true, secure);\n\n\tbnet::Message* out = bnet::alloc(handle, (uint16_t)bx::strLen(_request) );\n\tbx::memCopy(out->data, _request, out->size);\n\tbnet::send(out);\n\tbnet::notify(handle, UINT64_C(0x123456789ABCDEF) );\n\n\treturn handle;\n}\n\nstatic const char* s_cert[] = {\n\t// Equifax Secure CA\n\t\"-----BEGIN CERTIFICATE-----\\n\"\n\t\"MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV\\n\"\n\t\"UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy\\n\"\n\t\"dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1\\n\"\n\t\"MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx\\n\"\n\t\"dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B\\n\"\n\t\"AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f\\n\"\n\t\"BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A\\n\"\n\t\"cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC\\n\"\n\t\"AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ\\n\"\n\t\"MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm\\n\"\n\t\"aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw\\n\"\n\t\"ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj\\n\"\n\t\"IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF\\n\"\n\t\"MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA\\n\"\n\t\"A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y\\n\"\n\t\"7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh\\n\"\n\t\"1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4\\n\"\n\t\"-----END CERTIFICATE-----\\n\"\n\t,\n\t\"-----BEGIN CERTIFICATE-----\\n\"\n\t\"MIICDTCCAXYCCQDQyM1G6kagwzANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJV\\n\"\n\t\"UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHU2VhdHRsZTEVMBMGA1UE\\n\"\n\t\"ChMMQ2FyYm9uIEdhbWVzMB4XDTExMDgxMzIxMDY1N1oXDTExMDkxMjIxMDY1N1ow\\n\"\n\t\"SzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Nl\\n\"\n\t\"YXR0bGUxFTATBgNVBAoTDENhcmJvbiBHYW1lczCBnzANBgkqhkiG9w0BAQEFAAOB\\n\"\n\t\"jQAwgYkCgYEArVlCwFQMdhbitUzhuXb8r5NNjr3mgJAfXqpgG19r8IQDI/ueD4DY\\n\"\n\t\"ueLa+34VVWwaP27D3XIrAi7+WmGzUGf47b138F98E49HAYyoHnr/ww1mCGU4Jf8t\\n\"\n\t\"6JDREeyjqaFKhU1+zNjsyonooL1tPpw7u5fwjdrc6PX0qCKHHdLU+Q0CAwEAATAN\\n\"\n\t\"BgkqhkiG9w0BAQUFAAOBgQBm+bpTDyJttfpMeKkfmtZH828fX7qzdoj9Qs+G6Dyc\\n\"\n\t\"NqROo3yQiZfT8rhvyU9MdEhQcE53Eh+RA4uqAz3vkQH39mRcyrcyO3ktvCUC+0YY\\n\"\n\t\"WT7lu6St2t+/RgXY6ghaYCb8ko31HIJYcINZUXSBtpDYeZtRCS/nUb6LelMaOXDE\\n\"\n\t\"1A==\\n\"\n\t\"-----END CERTIFICATE-----\\n\"\n\t,\n\tNULL\n\t};\n\nint main(int /*_argc*/, const char* /*_argv*/[])\n{\n\tbnet::init(1, 0, s_cert);\n\n\tconst char* url = \"http://gravatar.com/avatar/cc47d6856403a62afc5c74d269b7e610.png\";\n//\tconst char* url = \"https://encrypted.google.com/\";\n\n\tbx::UrlView urlView;\n\turlView.parse(url);\n\n\tbool secure = false;\n\tuint32_t port = 0;\n\tif (0 == bx::strCmpI(\"http\", urlView.get(bx::UrlView::Scheme) ) )\n\t{\n\t\tport = 80;\n\t}\n\telse if (0 == bx::strCmpI(\"https\", urlView.get(bx::UrlView::Scheme) ) )\n\t{\n\t\tport = 443;\n\t\tsecure = true;\n\t}\n\n\tif (0 != port)\n\t{\n\t\tchar host[1024];\n\t\tstrCopy(host, BX_COUNTOF(host), urlView.get(bx::UrlView::Host) );\n\n\t\tuint32_t ip = bnet::toIpv4(host);\n\t\tif (!urlView.get(bx::UrlView::Port).isEmpty() )\n\t\t{\n\t\t\tbx::fromString(&port, urlView.get(bx::UrlView::Port) );\n\t\t}\n\n\t\tchar path[1024];\n\t\tstrCopy(path, BX_COUNTOF(path), urlView.get(bx::UrlView::Path) );\n\n\t\tchar header[1024];\n\t\tbx::snprintf(header\n\t\t\t\t, sizeof(header)\n\t\t\t\t, \"GET %s HTTP/1.0\\r\\nHost: %s\\r\\n\\r\\n\"\n\t\t\t\t, path\n\t\t\t\t, host\n\t\t\t\t);\n\n\t\tbnet::Handle handle = httpSendRequest(ip, uint16_t(port), header, secure);\n\n\t\tuint32_t size = 0;\n\t\tuint8_t* data = NULL;\n\n\t\tbool cont = bnet::isValid(handle);\n\t\tif (cont)\n\t\t{\n\t\t\tbx::printf(\"Connecting to %s (%d.%d.%d.%d:%d)\\n\"\n\t\t\t\t, url\n\t\t\t\t, ip>>24\n\t\t\t\t, (ip>>16)&0xff\n\t\t\t\t, (ip>>8)&0xff\n\t\t\t\t, ip&0xff\n\t\t\t\t, port\n\t\t\t\t);\n\t\t}\n\n\t\twhile (cont)\n\t\t{\n\t\t\tbnet::Message* msg = bnet::recv();\n\t\t\tif (NULL != msg)\n\t\t\t{\n\t\t\t\tif (bnet::MessageId::UserDefined > msg->data[0])\n\t\t\t\t{\n\t\t\t\t\tswitch (msg->data[0])\n\t\t\t\t\t{\n\t\t\t\t\tcase bnet::MessageId::Notify:\n\t\t\t\t\t\tbx::printf(\"notify!\\n\");\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase bnet::MessageId::LostConnection:\n\t\t\t\t\tcase bnet::MessageId::ConnectFailed:\n\t\t\t\t\t\tcont = false;\n\t\t\t\t\t\tif (NULL != data)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbx::FileWriter writer;\n\t\t\t\t\t\t\tbx::Error err;\n\t\t\t\t\t\t\tif (bx::open(&writer, \"http.txt\", false, &err) )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tbx::write(&writer, data, size, &err);\n\t\t\t\t\t\t\t\tbx::close(&writer);\n\n\t\t\t\t\t\t\t\tbx::printf(\"Received total %d. Data saved into http.txt.\\n\", size);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tbx::printf(\"Failed to open http.txt!\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase bnet::MessageId::RawData:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbx::printf(\"# raw %d bytes.\\n\", msg->size);\n\t\t\t\t\t\t\tuint32_t pos = size;\n\t\t\t\t\t\t\tsize += msg->size-1;\n\t\t\t\t\t\t\tdata = (uint8_t*)realloc(data, size+1);\n\t\t\t\t\t\t\tbx::memCopy(&data[pos], &msg->data[1], msg->size-1);\n\t\t\t\t\t\t\tdata[size-1] = '\\0';\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tbnet::release(msg);\n\t\t\t}\n\t\t}\n\t}\n\n\tbnet::shutdown();\n\treturn 0;\n}\n\n"
  },
  {
    "path": "examples/common/common.h",
    "content": "/*\n * Copyright 2011-2026 Branimir Karadzic. All rights reserved.\n * License: https://github.com/bkaradzic/bnet/blob/master/LICENSE\n */\n\n#ifndef __COMMON_H__\n#define __COMMON_H__\n\n#include \"dbg.h\"\n\n#if 0\n#\tdefine BX_TRACE(_format, ...) \\\n\t\tdo { \\\n\t\t\tDBG(BX_FILE_LINE_LITERAL \"BGFX \" _format \"\\n\", ##__VA_ARGS__); \\\n\t\t} while(0)\n\n#\tdefine BX_WARN(_condition, _format, ...) \\\n\t\tdo { \\\n\t\t\tif (!(_condition) ) \\\n\t\t\t{ \\\n\t\t\t\tDBG(\"WARN \" _format, ##__VA_ARGS__); \\\n\t\t\t} \\\n\t\t} while(0)\n\n#\tdefine BX_CHECK(_condition, _format, ...) \\\n\t\tdo { \\\n\t\t\tif (!(_condition) ) \\\n\t\t\t{ \\\n\t\t\t\tDBG(\"CHECK \" _format, ##__VA_ARGS__); \\\n\t\t\t\tbx::debugBreak(); \\\n\t\t\t} \\\n\t\t} while(0)\n#endif // 0\n\n#include <bx/bx.h>\n#include <bx/debug.h>\n\n#endif // __COMMON_H__\n"
  },
  {
    "path": "examples/common/dbg.h",
    "content": "/*\n * Copyright 2011-2026 Branimir Karadzic. All rights reserved.\n * License: https://github.com/bkaradzic/bnet/blob/master/LICENSE\n */\n\n##ifndef DBG_H_HEADER_GUARD\n#define DBG_H_HEADER_GUARD\n\n#include <bx/debug.h>\n\n#define DBG_STRINGIZE(_x) DBG_STRINGIZE_(_x)\n#define DBG_STRINGIZE_(_x) #_x\n#define DBG_FILE_LINE_LITERAL \"\" __FILE__ \"(\" DBG_STRINGIZE(__LINE__) \"): \"\n#define DBG(_format, ...) bx::debugPrintf(DBG_FILE_LINE_LITERAL \"\" _format \"\\n\", ##__VA_ARGS__)\n\n#endif // DBG_H_HEADER_GUARD\n"
  },
  {
    "path": "examples/runtime/.gitignore",
    "content": "http.txt\n"
  },
  {
    "path": "include/bnet/bnet.h",
    "content": "/*\n * Copyright 2010-2026 Branimir Karadzic. All rights reserved.\n * License: https://github.com/bkaradzic/bnet/blob/master/LICENSE\n */\n\n#ifndef BNET_H_HEADER_GUARD\n#define BNET_H_HEADER_GUARD\n\n#include <stdint.h> // uint32_t\n#include <stdlib.h> // NULL\n\n#define BNET_HANDLE(_name) struct _name { uint16_t idx; }\n\nnamespace bx { struct AllocatorI; }\n\nnamespace bnet\n{\n\tBNET_HANDLE(Handle);\n\n\tstatic const Handle invalidHandle = { UINT16_MAX };\n\tstatic const uint16_t maxMessageSize = UINT16_MAX;\n\n\tstruct MessageId\n\t{\n\t\tenum Enum\n\t\t{\n\t\t\tNotify = 1,\n\t\t\tIncomingConnection,\n\t\t\tLostConnection,\n\t\t\tListenFailed,\n\t\t\tConnectFailed,\n\t\t\tRawData,\n\n\t\t\tUserDefined = 8\n\t\t};\n\t};\n\n\tstruct DisconnectReason\n\t{\n\t\tenum Enum\n\t\t{\n\t\t\tNone,\n\t\t\tHostClosed,\n\t\t\tRecvFailed,\n\t\t\tSendFailed,\n\t\t\tInvalidMessageId,\n\t\t};\n\t};\n\n\t/// Returned by `bnet::alloc` or `bnet::recv` call.\n\tstruct Message\n\t{\n\t\tuint8_t* data; //< Message data.\n\t\tuint16_t size; //< Message size.\n\t\tHandle handle; //< Connection handle.\n\t};\n\n\ttypedef Message IncomingMessage;\n\ttypedef Message OutgoingMessage;\n\n\t/// Returns is handle is valid.\n\tinline bool isValid(Handle _handle) { return invalidHandle.idx != _handle.idx; }\n\n\t/// Initialize networking.\n\t///\n\t/// @param _maxConnections Maximum concurrent outgoing connections.\n\t/// @param _maxListenSockets Maximum number of listen ports.\n\t/// @param _certs SSL certificates.\n\t/// @param _allocator Custom allocator.\n\t///\n\tvoid init(uint16_t _maxConnections, uint16_t _maxListenSockets = 0, const char* _certs[] = NULL, bx::AllocatorI* _allocator = NULL);\n\n\t/// Shutdown networking.\n\tvoid shutdown();\n\n\t/// Start listen for incoming connections.\n\t///\n\t/// @returns Handle to connection object.\n\t///\n\tHandle listen(uint32_t _ip, uint16_t _port, bool _raw = false, const char* _cert = NULL, const char* _key = NULL);\n\n\t/// Stop listening for incoming connections.\n\t///\n\t/// @param _handle Handle to connection object.\n\t///\n\tvoid stop(Handle _handle);\n\n\t/// Connect to remote host.\n\t///\n\t/// @param _ip IPv4 address.\n\t/// @param _port Port.\n\t/// @param _raw Non-structured messages. When this is `false` bnet\n\t///   frames messages.\n\t/// @param _secure Create TLS/SSL connection.\n\t///\n\t/// @returns Handle to connection object.\n\t///\n\tHandle connect(uint32_t _ip, uint16_t _port, bool _raw = false, bool _secure = false);\n\n\t/// Disconnect from remote host.\n\t///\n\t/// @param _handle Handle to connection object.\n\t/// @param _finish Send all pending messages before closing\n\t///   connection.\n\t///\n\tvoid disconnect(Handle _handle, bool _finish = false);\n\n\t/// Notify sender when all prior messages are sent.\n\tvoid notify(Handle _handle, uint64_t _userData = 0);\n\n\t/// Allocate outgoing message.\n\t///\n\t/// @param _handle Handle to connection object.\n\t/// @param _size Message size.\n\t///\n\t/// @returns Outgoing message object.\n\t///\n\tOutgoingMessage* alloc(Handle _handle, uint16_t _size);\n\n\t/// Send message.\n\t///\n\t/// @param Message object allocated with `bnet::alloc` call.\n\t///\n\tvoid send(OutgoingMessage* _msg);\n\n\t/// Process receive.\n\t///\n\t/// @returns Incoming message object. Must be released by calling `bnet::release`.\n\t///\n\tIncomingMessage* recv();\n\n\t/// Release incoming message.\n\t///\n\t/// @param Message returned by `bnet::recv` call.\n\t///\n\tvoid release(IncomingMessage* _msg);\n\n\t/// Convert name to IP address.\n\t///\n\t/// @param _addr Name or IPv4 string.\n\t///\n\t/// @returns IPv4 address.\n\t///\n\tuint32_t toIpv4(const char* _addr = \"\");\n\n} // namespace bnet\n\n#endif // BNET_H_HEADER_GUARD\n"
  },
  {
    "path": "makefile",
    "content": "#\n# Copyright 2011-2026 Branimir Karadzic. All rights reserved.\n# License: https://github.com/bkaradzic/bnet/blob/master/LICENSE\n#\n\nUNAME := $(shell uname)\nifeq ($(UNAME),$(filter $(UNAME),Linux Darwin))\nifeq ($(UNAME),$(filter $(UNAME),Darwin))\nOS=darwin\nelse\nOS=linux\nendif\n\nhelp:\n\t@echo Available targets:\n\t@grep -E \"^[a-zA-Z0-9_-]+:.*?## .*$$\" $(MAKEFILE_LIST) | awk 'BEGIN {FS = \":.*?## \"}; {printf \"\\033[36m%-30s\\033[0m %s\\n\", $$1, $$2}'\n\nelse\nOS=windows\n\nhelp: projgen\n\nendif\n\nBX_DIR?=../bx\nGENIE?=$(BX_DIR)/tools/bin/$(OS)/genie $(EXTRA_GENIE_ARGS)\n\n.PHONY: help\n\nclean: ## Clean all intermediate files.\n\t@echo Cleaning...\n\t-@rm -rf .build\n\t@mkdir .build\n\nprojgen: ## Generate project files for all configurations.\n\t$(GENIE)                       vs2022\n\t$(GENIE) --gcc=mingw-gcc       gmake\n\t$(GENIE) --gcc=mingw-clang     gmake\n\t$(GENIE) --gcc=linux-gcc       gmake\n\t$(GENIE) --gcc=linux-clang     gmake\n\t$(GENIE) --gcc=osx-arm64       gmake\n\t$(GENIE) --gcc=osx-x64         gmake\n\t$(GENIE) --xcode=osx           xcode9\n\t$(GENIE) --xcode=ios           xcode9\n\t$(GENIE) --gcc=android-arm     gmake\n\t$(GENIE) --gcc=android-arm64   gmake\n\t$(GENIE) --gcc=ios-arm64       gmake\n\t$(GENIE) --gcc=rpi             gmake\n\n.build/projects/gmake-android-arm:\n\t$(GENIE) --gcc=android-arm gmake\nandroid-arm-debug: .build/projects/gmake-android-arm ## Build - Android ARM Debug\n\t$(MAKE) -R -C .build/projects/gmake-android-arm config=debug\nandroid-arm-release: .build/projects/gmake-android-arm ## Build - Android ARM Release\n\t$(MAKE) -R -C .build/projects/gmake-android-arm config=release\nandroid-arm: android-arm-debug android-arm-release ## Build - Android ARM Debug and Release\n\n.build/projects/gmake-android-arm64:\n\t$(GENIE) --gcc=android-arm64 gmake\nandroid-arm64-debug: .build/projects/gmake-android-arm64 ## Build - Android ARM64 Debug\n\t$(MAKE) -R -C .build/projects/gmake-android-arm64 config=debug\nandroid-arm64-release: .build/projects/gmake-android-arm64 ## Build - Android ARM64 Release\n\t$(MAKE) -R -C .build/projects/gmake-android-arm64 config=release\nandroid-arm64: android-arm64-debug android-arm64-release ## Build - Android ARM64 Debug and Release\n\n.build/projects/gmake-wasm:\n\t$(GENIE) --gcc=wasm gmake\nwasm-debug: .build/projects/gmake-wasm ## Build - Emscripten Debug\n\t$(MAKE) -R -C .build/projects/gmake-wasm config=debug\nwasm-release: .build/projects/gmake-wasm ## Build - Emscripten Release\n\t$(MAKE) -R -C .build/projects/gmake-wasm config=release\nwasm: wasm-debug wasm-release ## Build - Emscripten Debug and Release\n\n.build/projects/gmake-linux-gcc:\n\t$(GENIE) --gcc=linux-gcc gmake\nlinux-gcc-debug64: .build/projects/gmake-linux-gcc ## Build - Linux GCC x64 Debug\n\t$(MAKE) -R -C .build/projects/gmake-linux-gcc config=debug64\nlinux-gcc-release64: .build/projects/gmake-linux-gcc ## Build - Linux GCC x64 Release\n\t$(MAKE) -R -C .build/projects/gmake-linux-gcc config=release64\nlinux-gcc: linux-gcc-debug64 linux-gcc-release64 ## Build - Linux GCC x86/x64 Debug and Release\n\n.build/projects/gmake-linux-clang:\n\t$(GENIE) --gcc=linux-clang gmake\nlinux-clang-debug64: .build/projects/gmake-linux-clang ## Build - Linux Clang x64 Debug\n\t$(MAKE) -R -C .build/projects/gmake-linux-clang config=debug64\nlinux-clang-release64: .build/projects/gmake-linux-clang ## Build - Linux Clang x64 Release\n\t$(MAKE) -R -C .build/projects/gmake-linux-clang config=release64\nlinux-clang: linux-clang-debug64 linux-clang-release64 ## Build - Linux Clang x86/x64 Debug and Release\n\n.build/projects/gmake-mingw-gcc:\n\t$(GENIE) --gcc=mingw-gcc gmake\nmingw-gcc-debug32: .build/projects/gmake-mingw-gcc ## Build - MinGW GCC x86 Debug\n\t$(MAKE) -R -C .build/projects/gmake-mingw-gcc config=debug32\nmingw-gcc-release32: .build/projects/gmake-mingw-gcc ## Build - MinGW GCC x86 Release\n\t$(MAKE) -R -C .build/projects/gmake-mingw-gcc config=release32\nmingw-gcc-debug64: .build/projects/gmake-mingw-gcc ## Build - MinGW GCC x64 Debug\n\t$(MAKE) -R -C .build/projects/gmake-mingw-gcc config=debug64\nmingw-gcc-release64: .build/projects/gmake-mingw-gcc ## Build - MinGW GCC x64 Release\n\t$(MAKE) -R -C .build/projects/gmake-mingw-gcc config=release64\nmingw-gcc: mingw-gcc-debug32 mingw-gcc-release32 mingw-gcc-debug64 mingw-gcc-release64 ## Build - MinGW GCC x86/x64 Debug and Release\n\n.build/projects/gmake-mingw-clang:\n\t$(GENIE) --gcc=mingw-clang gmake\nmingw-clang-debug32: .build/projects/gmake-mingw-clang ## Build - MinGW Clang x86 Debug\n\t$(MAKE) -R -C .build/projects/gmake-mingw-clang config=debug32\nmingw-clang-release32: .build/projects/gmake-mingw-clang ## Build - MinGW Clang x86 Release\n\t$(MAKE) -R -C .build/projects/gmake-mingw-clang config=release32\nmingw-clang-debug64: .build/projects/gmake-mingw-clang ## Build - MinGW Clang x64 Debug\n\t$(MAKE) -R -C .build/projects/gmake-mingw-clang config=debug64\nmingw-clang-release64: .build/projects/gmake-mingw-clang ## Build - MinGW Clang x64 Release\n\t$(MAKE) -R -C .build/projects/gmake-mingw-clang config=release64\nmingw-clang: mingw-clang-debug32 mingw-clang-release32 mingw-clang-debug64 mingw-clang-release64 ## Build - MinGW Clang x86/x64 Debug and Release\n\n.build/projects/vs2022:\n\t$(GENIE) vs2022\nvs2022-debug32: .build/projects/vs2022 ## Build - vs2022 x86 Debug\n\tdevenv .build/projects/vs2022/bnet.sln /Build \"Debug|Win32\"\nvs2022-release32: .build/projects/vs2022 ## Build - vs2022 x86 Release\n\tdevenv .build/projects/vs2022/bnet.sln /Build \"Release|Win32\"\nvs2022-debug64: .build/projects/vs2022 ## Build - vs2022 x64 Debug\n\tdevenv .build/projects/vs2022/bnet.sln /Build \"Debug|x64\"\nvs2022-release64: .build/projects/vs2022 ## Build - vs2022 x64 Release\n\tdevenv .build/projects/vs2022/bnet.sln /Build \"Release|x64\"\nvs2022: vs2022-debug32 vs2022-release32 vs2022-debug64 vs2022-release64 ## Build - vs2022 x86/x64 Debug and Release\n\n.build/projects/gmake-osx-arm64:\n\t$(GENIE) --gcc=osx-arm64 gmake\nosx-arm64-debug: .build/projects/gmake-osx-arm64 ## Build - macOS ARM Debug\n\t$(MAKE) -C .build/projects/gmake-osx-arm64 config=debug\nosx-arm64-release: .build/projects/gmake-osx-arm64 ## Build - macOS ARM Release\n\t$(MAKE) -C .build/projects/gmake-osx-arm64 config=release\nosx-arm64: osx-arm64-debug osx-arm64-release ## Build - macOS ARM Debug and Release\n\n.build/projects/gmake-osx-x64:\n\t$(GENIE) --gcc=osx-x64 gmake\nosx-x64-debug: .build/projects/gmake-osx-x64 ## Build - macOS x64 Debug\n\t$(MAKE) -C .build/projects/gmake-osx-x64 config=debug\nosx-x64-release: .build/projects/gmake-osx-x64 ## Build - macOS x64 Release\n\t$(MAKE) -C .build/projects/gmake-osx-x64 config=release\nosx-x64: osx-x64-debug osx-x64-release ## Build - macOS x64 Debug and Release\n\n.build/projects/gmake-ios-arm64:\n\t$(GENIE) --gcc=ios-arm64 gmake\nios-arm64-debug: .build/projects/gmake-ios-arm64 ## Build - iOS ARM64 Debug\n\t$(MAKE) -R -C .build/projects/gmake-ios-arm64 config=debug\nios-arm64-release: .build/projects/gmake-ios-arm64 ## Build - iOS ARM64 Release\n\t$(MAKE) -R -C .build/projects/gmake-ios-arm64 config=release\nios-arm64: ios-arm64-debug ios-arm64-release ## Build - iOS ARM64 Debug and Release\n\n.build/projects/gmake-rpi:\n\t$(GENIE) --gcc=rpi gmake\nrpi-debug: .build/projects/gmake-rpi ## Build - RasberryPi Debug\n\t$(MAKE) -R -C .build/projects/gmake-rpi config=debug\nrpi-release: .build/projects/gmake-rpi ## Build - RasberryPi Release\n\t$(MAKE) -R -C .build/projects/gmake-rpi config=release\nrpi: rpi-debug rpi-release ## Build - RasberryPi Debug and Release\n"
  },
  {
    "path": "scripts/bnet.lua",
    "content": "project \"bnet\"\n\tuuid \"e72d44a0-ab28-11e0-9f1c-0800200c9a66\"\n\tkind \"StaticLib\"\n\n\tincludedirs {\n\t\tpath.join(BNET_DIR, \"include\"),\n\t}\n\n\tfiles {\n\t\tpath.join(BNET_DIR, \"include/**.h\"),\n\t\tpath.join(BNET_DIR, \"src/**.cpp\"),\n\t\tpath.join(BNET_DIR, \"src/**.h\"),\n\t}\n\n\tusing_bx()\n\n\tconfiguration { \"x32\", \"vs*\" }\n\t\tincludedirs { path.join(BNET_DIR, \"3rdparty/openssl/lib/win32_\", _ACTION, \"include\") }\n\n\tconfiguration { \"x64\", \"vs*\" }\n\t\tincludedirs { path.join(BNET_DIR, \"3rdparty/openssl/lib/win64_\", _ACTION, \"include\") }\n\n\tconfiguration { \"android-arm7\" }\n\t\tincludedirs { path.join(BNET_DIR, \"3rdparty/openssl/lib/android_arm7/include\") }\n\n\tconfiguration { \"default-linux\", \"x32\" }\n\t\tincludedirs { path.join(BNET_DIR, \"3rdparty/openssl/lib/linux-generic32/include\") }\n\n\tconfiguration { \"default-linux\", \"x64\" }\n\t\tincludedirs { path.join(BNET_DIR, \"3rdparty/openssl/lib/linux-generic64/include\") }\n\n\tconfiguration {}\n\n\tcopyLib()\n"
  },
  {
    "path": "scripts/genie.lua",
    "content": "--\n-- Copyright 2010-2026 Branimir Karadzic. All rights reserved.\n-- License: https://github.com/bkaradzic/bnet/blob/master/LICENSE\n--\n\nnewoption {\n\ttrigger = \"with-openssl\",\n\tdescription = \"Enable OpenSSL integration.\",\n}\n\nsolution \"bnet\"\n\tconfigurations {\n\t\t\"Debug\",\n\t\t\"Release\",\n\t}\n\n\tplatforms {\n\t\t\"x32\",\n\t\t\"x64\",\n\t\t\"Xbox360\",\n\t\t\"Native\", -- for targets where bitness is not specified\n\t}\n\n\tlanguage \"C++\"\n\nBNET_DIR = (path.getabsolute(\"..\") .. \"/\")\nBX_DIR   = os.getenv(\"BX_DIR\")\n\nlocal BNET_BUILD_DIR = path.join(BNET_DIR, \".build\")\nlocal BNET_THIRD_PARTY_DIR = path.join(BNET_DIR, \"3rdparty\")\nif not BX_DIR then\n\tBX_DIR = path.getabsolute(path.join(BNET_DIR, \"../bx\") )\nend\n\ndefines {\n\t\"BX_CONFIG_ENABLE_MSVC_LEVEL4_WARNINGS=1\"\n}\n\ndofile (path.join(BX_DIR, \"scripts/toolchain.lua\") )\ntoolchain(BNET_BUILD_DIR, BNET_THIRD_PARTY_DIR)\n\nfunction copyLib()\nend\n\ngroup \"libs\"\ndofile(path.join(BX_DIR, \"scripts/bx.lua\"))\ndofile \"bnet.lua\"\n\nfunction exampleProject(_name)\n\n\tproject (\"example-\" .. _name)\n\t\tuuid (os.uuid(\"example-\" .. _name))\n\t\tkind \"ConsoleApp\"\n\n\tconfiguration {}\n\n\tdebugdir (path.join(BNET_DIR, \"examples/runtime\") )\n\n\tincludedirs {\n\t\tpath.join(BNET_DIR, \"include\"),\n\t}\n\n\tfiles {\n\t\tpath.join(BNET_DIR, \"examples\", _name, \"**.cpp\"),\n\t\tpath.join(BNET_DIR, \"examples\", _name, \"**.h\"),\n\t}\n\n\tlinks {\n\t\t\"bnet\",\n\t}\n\n\tusing_bx()\n\n\tconfiguration { \"vs* or mingw*\" }\n\t\tlinks {\n\t\t\t\"psapi\",\n\t\t\t\"ws2_32\",\n\t\t}\n\n\tif _OPTIONS[\"with-openssl\"] then\n\t\tconfiguration { \"x32\", \"vs*\" }\n\t\t\tlibdirs { path.join(BNET_DIR, \"3rdparty/openssl/lib/win32_\", _ACTION, \"lib\") }\n\n\t\tconfiguration { \"x64\", \"vs*\" }\n\t\t\tlibdirs { path.join(BNET_DIR, \"3rdparty/openssl/lib/win64_\", _ACTION, \"lib\") }\n\n\t\tconfiguration { \"vs* or mingw*\" }\n\t\t\tlinks {\n\t\t\t\t\"libeay32\",\n\t\t\t\t\"ssleay32\",\n\t\t\t}\n\tend\n\n\tconfiguration { \"android*\" }\n\t\tkind \"ConsoleApp\"\n\t\ttargetextension \".so\"\n\t\tlinkoptions {\n\t\t\t\"-shared\",\n\t\t}\n\n\tconfiguration { \"osx*\" }\n\t\tlinkoptions {\n\t\t\t\"-framework Cocoa\",\n\t\t}\n\n\tconfiguration { \"ios*\" }\n\t\tkind \"ConsoleApp\"\n\t\tlinkoptions {\n\t\t\t\"-framework CoreFoundation\",\n\t\t\t\"-framework Foundation\",\n\t\t\t\"-framework UIKit\",\n\t\t\t\"-framework QuartzCore\",\n\t\t}\n\n\tconfiguration {}\n\n\tstrip()\nend\n\ngroup \"examples\"\nexampleProject(\"00-chat\")\nexampleProject(\"01-http\")\n"
  },
  {
    "path": "src/bnet.cpp",
    "content": "/*\n * Copyright 2010-2026 Branimir Karadzic. All rights reserved.\n * License: https://github.com/bkaradzic/bnet/blob/master/LICENSE\n */\n\n#include \"bnet_p.h\"\n\n#include <bx/endian.h>\n#include <bx/allocator.h>\n\nnamespace bnet\n{\n\tstatic bx::DefaultAllocator s_allocatorStub;\n\tbx::AllocatorI* g_allocator = &s_allocatorStub;\n\n#if BNET_CONFIG_OPENSSL && BX_CONFIG_DEBUG\n\n\tstatic void getSslErrorInfo()\n\t{\n\t\tBIO* bio = BIO_new(BIO_s_mem());\n\t\tERR_print_errors(bio);\n\t\tBUF_MEM *bptr;\n\t\tBIO_get_mem_ptr(bio, &bptr);\n\t\tBX_TRACE(\"OpenSSL Error: %.*s\", bptr->length, bptr->data);\n\t\tBIO_free(bio);\n\t}\n\n#\tdefine TRACE_SSL_ERROR() getSslErrorInfo()\n#else\n#\tdefine TRACE_SSL_ERROR()\n#endif // BNET_CONFIG_OPENSSL && BX_CONFIG_DEBUG\n\n\tint getLastError()\n\t{\n#if BX_PLATFORM_WINDOWS\n\t\treturn WSAGetLastError();\n#elif  BX_PLATFORM_LINUX \\\n\t|| BX_PLATFORM_ANDROID \\\n\t|| BX_PLATFORM_EMSCRIPTEN \\\n\t|| BX_PLATFORM_OSX \\\n\t|| BX_PLATFORM_IOS\n\t\treturn errno;\n#else\n\t\treturn 0;\n#endif // BX_PLATFORM_\n\t}\n\n#if BNET_CONFIG_OPENSSL\n#else\n\tstatic int sslDummyContext;\n#endif\n\n\tbool isInProgress()\n\t{\n#if BX_PLATFORM_WINDOWS\n\t\treturn WSAEINPROGRESS == getLastError();\n#else\n\t\treturn EINPROGRESS == getLastError();\n#endif // BX_PLATFORM_WINDOWS\n\t}\n\n\tbool isWouldBlock()\n\t{\n#if BX_PLATFORM_WINDOWS\n\t\treturn WSAEWOULDBLOCK == getLastError();\n#else\n\t\treturn EWOULDBLOCK == getLastError();\n#endif // BX_PLATFORM_WINDOWS\n\t}\n\n\tvoid setNonBlock(SOCKET _socket)\n\t{\n#if BX_PLATFORM_WINDOWS\n\t\tunsigned long opt = 1 ;\n\t\t::ioctlsocket(_socket, FIONBIO, &opt);\n#elif  BX_PLATFORM_LINUX \\\n\t|| BX_PLATFORM_ANDROID \\\n\t|| BX_PLATFORM_EMSCRIPTEN \\\n\t|| BX_PLATFORM_OSX \\\n\t|| BX_PLATFORM_IOS\n\t\t::fcntl(_socket, F_SETFL, O_NONBLOCK);\n#else\n\t\tBX_UNUSED(_socket);\n#endif // BX_PLATFORM_\n\t}\n\n\tstatic void setSockOpts(SOCKET _socket)\n\t{\n\t\tint result;\n\n\t\tint win = 256<<10;\n\t\tresult = ::setsockopt(_socket, SOL_SOCKET, SO_RCVBUF, (char*)&win, sizeof(win) );\n\t\tresult = ::setsockopt(_socket, SOL_SOCKET, SO_SNDBUF, (char*)&win, sizeof(win) );\n\n\t\tint noDelay = 1;\n\t\tresult = ::setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, (char*)&noDelay, sizeof(noDelay) );\n\t\tBX_UNUSED(result);\n\t}\n\n\tclass Connection\n\t{\n\tpublic:\n\t\tConnection()\n\t\t\t: m_socket(INVALID_SOCKET)\n\t\t\t, m_handle(invalidHandle)\n\t\t\t, m_incomingBuffer( (uint8_t*)bx::alloc(g_allocator, BNET_CONFIG_MAX_INCOMING_BUFFER_SIZE) )\n\t\t\t, m_incoming(BNET_CONFIG_MAX_INCOMING_BUFFER_SIZE)\n\t\t\t, m_recv(m_incoming, (char*)m_incomingBuffer)\n#if BNET_CONFIG_OPENSSL\n\t\t\t, m_ssl(NULL)\n#endif // BNET_CONFIG_OPENSSL\n\t\t\t, m_len(-1)\n\t\t\t, m_raw(false)\n\t\t\t, m_tcpHandshake(true)\n\t\t\t, m_sslHandshake(false)\n\t\t{\n\t\t}\n\n\t\t~Connection()\n\t\t{\n\t\t\tbx::free(g_allocator, m_incomingBuffer);\n\t\t}\n\n\t\tvoid connect(Handle _handle, uint32_t _ip, uint16_t _port, bool _raw, SSL_CTX* _sslCtx)\n\t\t{\n\t\t\tinit(_handle, _raw);\n\n\t\t\tm_socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\n\t\t\tif (INVALID_SOCKET == m_socket)\n\t\t\t{\n\t\t\t\tctxPush(m_handle, MessageId::ConnectFailed);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsetSockOpts(m_socket);\n\n\t\t\tconst bool ssl = _sslCtx != NULL;\n\t\t\tint err = connectsocket(m_socket, _ip, _port, ssl);\n\n\t\t\tif (0 != err\n\t\t\t&&  !(isInProgress() || isWouldBlock() ) )\n\t\t\t{\n\t\t\t\tBX_TRACE(\"Connect %d - Connect failed. %d\", m_handle, getLastError() );\n\n\t\t\t\t::closesocket(m_socket);\n\t\t\t\tm_socket = INVALID_SOCKET;\n\n\t\t\t\tctxPush(m_handle, MessageId::ConnectFailed);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsetNonBlock(m_socket);\n\n#if BNET_CONFIG_OPENSSL\n\t\t\tif (ssl)\n\t\t\t{\n\t\t\t\tm_sslHandshake = true;\n\t\t\t\tm_ssl = SSL_new(_sslCtx);\n\t\t\t\tSSL_set_fd(m_ssl, (int)m_socket);\n\t\t\t\tSSL_set_connect_state(m_ssl);\n\t\t\t\tSSL_write(m_ssl, NULL, 0);\n\t\t\t}\n#else\n\t\t\tBX_UNUSED(_sslCtx);\n#endif // BNET_CONFIG_OPENSSL\n\t\t}\n\n\t\tvoid accept(Handle _handle, Handle _listenHandle, SOCKET _socket, uint32_t _ip, uint16_t _port, bool _raw, SSL_CTX* _sslCtx, X509* _cert, EVP_PKEY* _key)\n\t\t{\n\t\t\tinit(_handle, _raw);\n\n\t\t\tm_socket = _socket;\n\t\t\tMessage* msg = msgAlloc(m_handle, 9, true);\n\t\t\tmsg->data[0] = MessageId::IncomingConnection;\n\t\t\t*( (uint16_t*)&msg->data[1]) = _listenHandle.idx;\n\t\t\t*( (uint32_t*)&msg->data[3]) = _ip;\n\t\t\t*( (uint16_t*)&msg->data[7]) = _port;\n\t\t\tctxPush(msg);\n\n#if BNET_CONFIG_OPENSSL\n\t\t\tif (NULL != _sslCtx)\n\t\t\t{\n\t\t\t\tm_sslHandshake = true;\n\t\t\t\tm_ssl = SSL_new(_sslCtx);\n\t\t\t\tint result;\n\t\t\t\tresult = SSL_use_certificate(m_ssl, _cert);\n\t\t\t\tresult = SSL_use_PrivateKey(m_ssl, _key);\n\t\t\t\tresult = SSL_set_fd(m_ssl, (int)m_socket);\n\t\t\t\tBX_UNUSED(result);\n\t\t\t\tSSL_set_accept_state(m_ssl);\n\t\t\t\tSSL_read(m_ssl, NULL, 0);\n\t\t\t}\n#else\n\t\t\tBX_UNUSED(_sslCtx);\n\t\t\tBX_UNUSED(_cert);\n\t\t\tBX_UNUSED(_key);\n#endif // BNET_CONFIG_OPENSSL\n\t\t}\n\n\t\tvoid disconnect(DisconnectReason::Enum _reason = DisconnectReason::None)\n\t\t{\n#if BNET_CONFIG_OPENSSL\n\t\t\tif (m_ssl)\n\t\t\t{\n\t\t\t\tSSL_shutdown(m_ssl);\n\t\t\t\tSSL_free(m_ssl);\n\t\t\t\tm_ssl = NULL;\n\t\t\t}\n#endif // BNET_CONFIG_OPENSSL\n\n\t\t\tif (INVALID_SOCKET != m_socket)\n\t\t\t{\n\t\t\t\t::closesocket(m_socket);\n\t\t\t\tm_socket = INVALID_SOCKET;\n\t\t\t}\n\n\t\t\tfor (Message* msg = m_outgoing.pop(); NULL != msg; msg = m_outgoing.pop() )\n\t\t\t{\n\t\t\t\trelease(msg);\n\t\t\t}\n\n\t\t\tif (_reason != DisconnectReason::None)\n\t\t\t{\n\t\t\t\tMessage* msg = msgAlloc(m_handle, 2, true);\n\t\t\t\tmsg->data[0] = MessageId::LostConnection;\n\t\t\t\tmsg->data[1] = uint8_t(_reason);\n\t\t\t\tctxPush(msg);\n\t\t\t}\n\t\t}\n\n\t\tvoid send(Message* _msg)\n\t\t{\n\t\t\tBX_ASSERT(m_raw || _msg->data[0] >= MessageId::UserDefined, \"Sending message with MessageId below UserDefined is not allowed!\");\n\t\t\tif (INVALID_SOCKET != m_socket)\n\t\t\t{\n\t\t\t\tm_outgoing.push(_msg);\n\t\t\t\tupdate();\n\t\t\t}\n\t\t}\n\n\t\tvoid update()\n\t\t{\n\t\t\tif (INVALID_SOCKET != m_socket)\n\t\t\t{\n\t\t\t\tupdateSocket();\n\n\t\t\t\tif (!m_tcpHandshake\n\t\t\t\t&&  !m_sslHandshake)\n\t\t\t\t{\n\t\t\t\t\tupdateIncomingMessages();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tbool hasSocket() const\n\t\t{\n\t\t\treturn INVALID_SOCKET != m_socket;\n\t\t}\n\n\tprivate:\n\t\tvoid init(Handle _handle, bool _raw)\n\t\t{\n\t\t\tm_handle = _handle;\n\t\t\tm_tcpHandshake = true;\n\t\t\tm_sslHandshake = false;\n\t\t\tm_tcpHandshakeTimeout = bx::getHPCounter() + bx::getHPFrequency()*BNET_CONFIG_CONNECT_TIMEOUT_SECONDS;\n\t\t\tm_len = -1;\n\t\t\tm_raw = _raw;\n\t\t}\n\n\t\tvoid read(bx::WriteRingBuffer& _out, uint32_t _len)\n\t\t{\n\t\t\tbx::ReadRingBuffer incoming(m_incoming, (char*)m_incomingBuffer, _len);\n\t\t\t_out.write(incoming, _len);\n\t\t\tincoming.end();\n\t\t}\n\n\t\tvoid read(uint32_t _len)\n\t\t{\n\t\t\tm_incoming.consume(_len);\n\t\t}\n\n\t\tvoid read(char* _data, uint32_t _len)\n\t\t{\n\t\t\tbx::ReadRingBuffer incoming(m_incoming, (char*)m_incomingBuffer, _len);\n\t\t\tincoming.read(_data, _len);\n\t\t\tincoming.end();\n\t\t}\n\n\t\tvoid peek(char* _data, uint32_t _len)\n\t\t{\n\t\t\tbx::ReadRingBuffer incoming(m_incoming, (char*)m_incomingBuffer, _len);\n\t\t\tincoming.read(_data, _len);\n\t\t}\n\n\t\tvoid updateIncomingMessages()\n\t\t{\n\t\t\tif (m_raw)\n\t\t\t{\n\t\t\t\tuint16_t available = uint16_t(bx::min<uint32_t>(m_incoming.getNumUsed(), maxMessageSize-1) );\n\n\t\t\t\tif (0 < available)\n\t\t\t\t{\n\t\t\t\t\tMessage* msg = msgAlloc(m_handle, available+1, true);\n\t\t\t\t\tmsg->data[0] = MessageId::RawData;\n\t\t\t\t\tread( (char*)&msg->data[1], available);\n\t\t\t\t\tctxPush(msg);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tuint32_t available = bx::min<uint32_t>(m_incoming.getNumUsed(), maxMessageSize);\n\n\t\t\t\twhile (0 < available)\n\t\t\t\t{\n\t\t\t\t\tif (-1 == m_len)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (2 > available)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tuint16_t len;\n\t\t\t\t\t\t\tread((char*)&len, 2);\n\t\t\t\t\t\t\tm_len = bx::toHostEndian(len, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (m_len > int(available) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tMessage* msg = msgAlloc(m_handle, uint16_t(m_len), true);\n\t\t\t\t\t\t\tread( (char*)msg->data, m_len);\n\t\t\t\t\t\t\tuint8_t id = msg->data[0];\n\n\t\t\t\t\t\t\tif (id < MessageId::UserDefined)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmsgRelease(msg);\n\n\t\t\t\t\t\t\t\tBX_TRACE(\"Disconnect %d - Invalid message id.\", m_handle);\n\t\t\t\t\t\t\t\tdisconnect(DisconnectReason::InvalidMessageId);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tctxPush(msg);\n\n\t\t\t\t\t\t\tm_len = -1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tavailable = bx::min<uint32_t>(m_incoming.getNumUsed(), maxMessageSize);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvoid updateSocket()\n\t\t{\n\t\t\tif (updateTcpHandshake()\n\t\t\t&&  updateSslHandshake() )\n\t\t\t{\n\t\t\t\tint bytes;\n\n#if BNET_CONFIG_OPENSSL\n\t\t\t\tif (NULL != m_ssl)\n\t\t\t\t{\n\t\t\t\t\tbytes = m_recv.recv(m_ssl);\n\t\t\t\t}\n\t\t\t\telse\n#endif // BNET_CONFIG_OPENSSL\n\t\t\t\t{\n\t\t\t\t\tbytes = m_recv.recv(m_socket);\n\t\t\t\t}\n\n\t\t\t\tif (1 > bytes)\n\t\t\t\t{\n\t\t\t\t\tif (0 == bytes)\n\t\t\t\t\t{\n\t\t\t\t\t\tBX_TRACE(\"Disconnect %d - Host closed connection.\", m_handle);\n\t\t\t\t\t\tdisconnect(DisconnectReason::HostClosed);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\telse if (!isWouldBlock() )\n\t\t\t\t\t{\n\t\t\t\t\t\tTRACE_SSL_ERROR();\n\t\t\t\t\t\tBX_TRACE(\"Disconnect %d - Receive failed. %d\", m_handle, getLastError() );\n\t\t\t\t\t\tdisconnect(DisconnectReason::RecvFailed);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!m_sslHandshake)\n\t\t\t\t{\n\t\t\t\t\tif (m_raw)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (Message* msg = m_outgoing.peek(); NULL != msg; msg = m_outgoing.peek() )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tInternal::Enum id = Internal::Enum(*(msg->data - 2) );\n\t\t\t\t\t\t\tif (Internal::None != id)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (!processInternal(id, msg) )\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (!send( (char*)msg->data, msg->size) )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\trelease(m_outgoing.pop() );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (Message* msg = m_outgoing.peek(); NULL != msg; msg = m_outgoing.peek() )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tInternal::Enum id = Internal::Enum(*(msg->data - 2) );\n\t\t\t\t\t\t\tif (Internal::None != id)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t*( (uint16_t*)msg->data - 1) = msg->size;\n\t\t\t\t\t\t\t\tif (!processInternal(id, msg) )\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t*( (uint16_t*)msg->data - 1) = bx::toLittleEndian(msg->size);\n\t\t\t\t\t\t\t\tif (!send( (char*)msg->data - 2, msg->size+2) )\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\trelease(m_outgoing.pop() );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tbool processInternal(Internal::Enum _id, Message* _msg)\n\t\t{\n\t\t\tswitch (_id)\n\t\t\t{\n\t\t\tcase Internal::Disconnect:\n\t\t\t\t{\n\t\t\t\t\tMessage* msg = msgAlloc(_msg->handle, 2, true);\n\t\t\t\t\tmsg->data[0] = 0;\n\t\t\t\t\tmsg->data[1] = Internal::Disconnect;\n\t\t\t\t\tctxPush(msg);\n\n\t\t\t\t\tBX_TRACE(\"Disconnect %d - Client closed connection (finish).\", m_handle);\n\t\t\t\t\tdisconnect();\n\t\t\t\t}\n\t\t\t\treturn false;\n\n\t\t\tcase Internal::Notify:\n\t\t\t\t{\n\t\t\t\t\tMessage* msg = msgAlloc(_msg->handle, _msg->size+1, true);\n\t\t\t\t\tmsg->data[0] = MessageId::Notify;\n\t\t\t\t\tbx::memCopy(&msg->data[1], _msg->data, _msg->size);\n\t\t\t\t\tctxPush(msg);\n\t\t\t\t}\n\t\t\t\treturn true;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tBX_ASSERT(false, \"You should not be here!\");\n\t\t\treturn true;\n\t\t}\n\n\t\tbool updateTcpHandshake()\n\t\t{\n\t\t\tif (!m_tcpHandshake)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tuint64_t now = bx::getHPCounter();\n\t\t\tif (now > m_tcpHandshakeTimeout)\n\t\t\t{\n\t\t\t\tBX_TRACE(\"Disconnect %d - Connect timeout.\", m_handle);\n\t\t\t\tctxPush(m_handle, MessageId::ConnectFailed);\n\t\t\t\tdisconnect();\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tm_tcpHandshake = !issocketready(m_socket);\n\t\t\treturn !m_tcpHandshake;\n\t\t}\n\n\t\tbool updateSslHandshake()\n\t\t{\n#if BNET_CONFIG_OPENSSL\n\t\t\tif (NULL != m_ssl\n\t\t\t&&  m_sslHandshake)\n\t\t\t{\n\t\t\t\tint err = SSL_do_handshake(m_ssl);\n\n\t\t\t\tif (1 == err)\n\t\t\t\t{\n\t\t\t\t\tm_sslHandshake = false;\n#\tif BX_CONFIG_DEBUG\n\t\t\t\t\tX509* cert = SSL_get_peer_certificate(m_ssl);\n\t\t\t\t\tBX_TRACE(\"Server certificate:\");\n\n\t\t\t\t\tchar* temp;\n\t\t\t\t\ttemp = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);\n\t\t\t\t\tBX_TRACE(\"\\t subject: %s\", temp);\n\t\t\t\t\tOPENSSL_free(temp);\n\n\t\t\t\t\ttemp = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);\n\t\t\t\t\tBX_TRACE(\"\\t issuer: %s\", temp);\n\t\t\t\t\tOPENSSL_free(temp);\n\n\t\t\t\t\tX509_free(cert);\n#\tendif // BX_CONFIG_DEBUG\n\n\t\t\t\t\tlong result = SSL_get_verify_result(m_ssl);\n\t\t\t\t\tif (X509_V_OK != result)\n\t\t\t\t\t{\n\t\t\t\t\t\tBX_TRACE(\"Disconnect %d - SSL verify failed %d.\", m_handle, result);\n\t\t\t\t\t\tctxPush(m_handle, MessageId::ConnectFailed);\n\t\t\t\t\t\tdisconnect();\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\tBX_TRACE(\"SSL connection using %s\", SSL_get_cipher(m_ssl) );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tint sslError = SSL_get_error(m_ssl, err);\n\t\t\t\t\tswitch (sslError)\n\t\t\t\t\t{\n\t\t\t\t\tcase SSL_ERROR_WANT_READ:\n\t\t\t\t\t\tSSL_read(m_ssl, NULL, 0);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase SSL_ERROR_WANT_WRITE:\n\t\t\t\t\t\tSSL_write(m_ssl, NULL, 0);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tTRACE_SSL_ERROR();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n#endif // BNET_CONFIG_OPENSSL\n\n\t\t\treturn true;\n\t\t}\n\n\t\tbool send(const char* _data, uint32_t _len)\n\t\t{\n\t\t\tint bytes;\n\t\t\tuint32_t offset = 0;\n\t\t\tdo\n\t\t\t{\n#if BNET_CONFIG_OPENSSL\n\t\t\t\tif (NULL != m_ssl)\n\t\t\t\t{\n\t\t\t\t\tbytes = SSL_write(m_ssl\n\t\t\t\t\t\t, &_data[offset]\n\t\t\t\t\t\t, _len\n\t\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\telse\n#endif // BNET_CONFIG_OPENSSL\n\t\t\t\t{\n\t\t\t\t\tbytes = ::send(m_socket\n\t\t\t\t\t\t, &_data[offset]\n\t\t\t\t\t\t, _len\n\t\t\t\t\t\t, 0\n\t\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tif (0 > bytes)\n\t\t\t\t{\n\t\t\t\t\tif (-1 == bytes\n\t\t\t\t\t&&  !isWouldBlock() )\n\t\t\t\t\t{\n\t\t\t\t\t\tBX_TRACE(\"Disconnect %d - Send failed.\", m_handle);\n\t\t\t\t\t\tdisconnect(DisconnectReason::SendFailed);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t_len -= bytes;\n\t\t\t\t\toffset += bytes;\n\t\t\t\t}\n\n\t\t\t} while (0 < _len);\n\n\t\t\treturn true;\n\t\t}\n\n\t\tuint64_t m_tcpHandshakeTimeout;\n\t\tSOCKET m_socket;\n\t\tHandle m_handle;\n\t\tuint8_t* m_incomingBuffer;\n\t\tbx::RingBufferControl m_incoming;\n\t\tRecvRingBuffer m_recv;\n\t\tMessageQueue m_outgoing;\n#if BNET_CONFIG_OPENSSL\n\t\tSSL* m_ssl;\n#endif // BNET_CONFIG_OPENSSL\n\n\t\tint m_len;\n\t\tbool m_raw;\n\t\tbool m_tcpHandshake;\n\t\tbool m_sslHandshake;\n\t};\n\n\ttypedef FreeList<Connection> Connections;\n\n\tclass ListenSocket\n\t{\n\tpublic:\n\t\tListenSocket()\n\t\t\t: m_socket(INVALID_SOCKET)\n\t\t\t, m_handle(invalidHandle)\n\t\t\t, m_raw(false)\n\t\t\t, m_secure(false)\n\t\t\t, m_cert(NULL)\n\t\t\t, m_key(NULL)\n\t\t{\n\t\t}\n\n\t\t~ListenSocket()\n\t\t{\n\t\t\tclose();\n\t\t}\n\n\t\tvoid close()\n\t\t{\n\t\t\tif (INVALID_SOCKET != m_socket)\n\t\t\t{\n\t\t\t\t::closesocket(m_socket);\n\t\t\t\tm_socket = INVALID_SOCKET;\n\t\t\t}\n\n#if BNET_CONFIG_OPENSSL\n\t\t\tif (NULL != m_cert)\n\t\t\t{\n\t\t\t\tX509_free(m_cert);\n\t\t\t\tm_cert = NULL;\n\t\t\t}\n\n\t\t\tif (NULL != m_key)\n\t\t\t{\n\t\t\t\tEVP_PKEY_free(m_key);\n\t\t\t\tm_key = NULL;\n\t\t\t}\n#endif // BNET_CONFIG_OPENSSL\n\t\t}\n\n\t\tvoid listen(Handle _handle, uint32_t _ip, uint16_t _port, bool _raw, const char* _cert, const char* _key)\n\t\t{\n\t\t\tm_handle = _handle;\n\t\t\tm_raw = _raw;\n\n#if BNET_CONFIG_OPENSSL\n\t\t\tif (NULL != _cert)\n\t\t\t{\n\t\t\t\tBIO* mem = BIO_new_mem_buf(const_cast<char*>(_cert), -1);\n\t\t\t\tm_cert = PEM_read_bio_X509(mem, NULL, NULL, NULL);\n\t\t\t\tBIO_free(mem);\n\t\t\t}\n\n\t\t\tif (NULL != _key)\n\t\t\t{\n\t\t\t\tBIO* mem = BIO_new_mem_buf(const_cast<char*>(_key), -1);\n\t\t\t\tm_key = PEM_read_bio_PrivateKey(mem, NULL, NULL, NULL);\n\t\t\t\tBIO_free(mem);\n\t\t\t}\n\n\t\t\tm_secure = NULL != m_key && NULL != m_cert;\n#endif // BNET_CONFIG_OPENSSL\n\n\t\t\tif (!m_secure\n\t\t\t&&  (NULL != _cert || NULL != _key) )\n\t\t\t{\n#if BNET_CONFIG_OPENSSL\n\t\t\t\tBX_TRACE(\"Certificate of key is not set correctly.\");\n#else\n\t\t\t\tBX_TRACE(\"BNET_CONFIG_OPENSSL is not enabled.\");\n#endif // BNET_CONFIG_OPENSSL\n\t\t\t\tctxPush(m_handle, MessageId::ListenFailed);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tm_socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\n\t\t\tif (INVALID_SOCKET == m_socket)\n\t\t\t{\n\t\t\t\tBX_TRACE(\"Create socket failed.\");\n\t\t\t\tctxPush(m_handle, MessageId::ListenFailed);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsetSockOpts(m_socket);\n\n\t\t\tm_addr.sin_family = AF_INET;\n\t\t\tm_addr.sin_addr.s_addr = htonl(_ip);\n\t\t\tm_addr.sin_port = htons(_port);\n\n\t\t\tif (SOCKET_ERROR == ::bind(m_socket, (sockaddr*)&m_addr, sizeof(m_addr) )\n\t\t\t||  SOCKET_ERROR == ::listen(m_socket, SOMAXCONN) )\n\t\t\t{\n\t\t\t\t::closesocket(m_socket);\n\t\t\t\tm_socket = INVALID_SOCKET;\n\n\t\t\t\tBX_TRACE(\"Bind or listen socket failed.\");\n\t\t\t\tctxPush(m_handle, MessageId::ListenFailed);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsetNonBlock(m_socket);\n\t\t}\n\n\t\tvoid update()\n\t\t{\n\t\t\tsockaddr_in addr;\n\t\t\tsocklen_t len = sizeof(addr);\n\t\t\tSOCKET socket = ::accept(m_socket, (sockaddr*)&addr, &len);\n\t\t\tif (INVALID_SOCKET != socket)\n\t\t\t{\n\t\t\t\tuint32_t ip = ntohl(addr.sin_addr.s_addr);\n\t\t\t\tuint16_t port = ntohs(addr.sin_port);\n\t\t\t\tctxAccept(m_handle, socket, ip, port, m_raw, m_cert, m_key);\n\t\t\t}\n\t\t}\n\n\tprivate:\n\t\tsockaddr_in m_addr;\n\t\tSOCKET m_socket;\n\t\tHandle m_handle;\n\t\tbool m_raw;\n\t\tbool m_secure;\n\t\tX509* m_cert;\n\t\tEVP_PKEY* m_key;\n\t};\n\n\ttypedef FreeList<ListenSocket> ListenSockets;\n\n\tclass Context\n\t{\n\tpublic:\n\t\tContext()\n\t\t\t: m_connections(NULL)\n\t\t\t, m_listenSockets(NULL)\n\t\t\t, m_sslCtx(NULL)\n\t\t\t, m_sslCtxServer(NULL)\n\t\t{\n\t\t}\n\n\t\t~Context()\n\t\t{\n\t\t}\n\n\t\tvoid init(uint16_t _maxConnections, uint16_t _maxListenSockets, const char* _certs[])\n\t\t{\n#if BNET_CONFIG_OPENSSL\n\t\t\tCRYPTO_get_mem_functions(&m_sslMalloc, &m_sslRealloc, &m_sslFree);\n\t\t\tCRYPTO_set_mem_functions(sslMalloc, sslRealloc, sslFree);\n\t\t\tSSL_library_init();\n#\tif BX_CONFIG_DEBUG\n\t\t\tSSL_load_error_strings();\n#\tendif // BX_CONFIG_DEBUG\n\t\t\tm_sslCtx = SSL_CTX_new(SSLv23_client_method() );\n\t\t\tSSL_CTX_set_verify(m_sslCtx, SSL_VERIFY_NONE, NULL);\n\t\t\tif (NULL != _certs)\n\t\t\t{\n\t\t\t\tX509_STORE* store = SSL_CTX_get_cert_store(m_sslCtx);\n\t\t\t\tfor (const char** cert = _certs; NULL != *cert; ++cert )\n\t\t\t\t{\n\t\t\t\t\tBIO* mem = BIO_new_mem_buf(const_cast<char*>(*cert), -1);\n\t\t\t\t\tX509* x509 = PEM_read_bio_X509(mem, NULL, NULL, NULL);\n\t\t\t\t\tX509_STORE_add_cert(store, x509);\n\t\t\t\t\tX509_free(x509);\n\t\t\t\t\tBIO_free(mem);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (_maxListenSockets)\n\t\t\t{\n\t\t\t\tm_sslCtxServer = SSL_CTX_new(SSLv23_server_method());\n\t\t\t}\n#else\n\t\t\tm_sslCtx = &sslDummyContext;\n\t\t\tm_sslCtxServer = &sslDummyContext;\n\t\t\tBX_UNUSED(_certs);\n#endif // BNET_CONFIG_OPENSSL\n\n\t\t\t_maxConnections = _maxConnections == 0 ? 1 : _maxConnections;\n\n\t\t\tm_connections = BX_NEW(g_allocator, Connections)(_maxConnections);\n\n\t\t\tif (0 != _maxListenSockets)\n\t\t\t{\n\t\t\t\tm_listenSockets = BX_NEW(g_allocator, ListenSockets)(_maxListenSockets);\n\t\t\t}\n\t\t}\n\n\t\tvoid shutdown()\n\t\t{\n\t\t\tfor (Message* msg = m_incoming.pop(); NULL != msg; msg = m_incoming.pop() )\n\t\t\t{\n\t\t\t\trelease(msg);\n\t\t\t}\n\n\t\t\tbx::deleteObject(g_allocator, m_connections);\n\n\t\t\tif (NULL != m_listenSockets)\n\t\t\t{\n\t\t\t\tbx::deleteObject(g_allocator, m_listenSockets);\n\t\t\t}\n\n#if BNET_CONFIG_OPENSSL\n\t\t\tif (NULL != m_sslCtx)\n\t\t\t{\n\t\t\t\tSSL_CTX_free(m_sslCtx);\n\t\t\t}\n\t\t\tm_sslCtx = NULL;\n\n\t\t\tif (NULL != m_sslCtxServer)\n\t\t\t{\n\t\t\t\tSSL_CTX_free(m_sslCtxServer);\n\t\t\t}\n\t\t\tm_sslCtxServer = NULL;\n\t\t\tCRYPTO_set_mem_functions(m_sslMalloc, m_sslRealloc, m_sslFree);\n#endif // BNET_CONFIG_OPENSSL\n\t\t}\n\n\t\tHandle listen(uint32_t _ip, uint16_t _port, bool _raw, const char* _cert, const char* _key)\n\t\t{\n\t\t\tListenSocket* listenSocket = m_listenSockets->create();\n\t\t\tif (NULL != listenSocket)\n\t\t\t{\n\t\t\t\tHandle handle = { m_listenSockets->getHandle(listenSocket) };\n\t\t\t\tlistenSocket->listen(handle, _ip, _port, _raw, _cert, _key);\n\t\t\t\treturn handle;\n\t\t\t}\n\n\t\t\treturn invalidHandle;\n\t\t}\n\n\t\tvoid stop(Handle _handle)\n\t\t{\n\t\t\tListenSocket* listenSocket = { m_listenSockets->getFromHandle(_handle.idx) };\n\t\t\tlistenSocket->close();\n\t\t\tm_listenSockets->destroy(listenSocket);\n\t\t}\n\n\t\tHandle accept(Handle _listenHandle, SOCKET _socket, uint32_t _ip, uint16_t _port, bool _raw, X509* _cert, EVP_PKEY* _key)\n\t\t{\n\t\t\tConnection* connection = m_connections->create();\n\t\t\tif (NULL != connection)\n\t\t\t{\n\t\t\t\tHandle handle = { m_connections->getHandle(connection) };\n\t\t\t\tbool secure = NULL != _cert && NULL != _key;\n\t\t\t\tconnection->accept(handle, _listenHandle, _socket, _ip, _port, _raw, secure?m_sslCtxServer:NULL, _cert, _key);\n\t\t\t\treturn handle;\n\t\t\t}\n\n\t\t\treturn invalidHandle;\n\t\t}\n\n\t\tHandle connect(uint32_t _ip, uint16_t _port, bool _raw, bool _secure)\n\t\t{\n\t\t\tConnection* connection = m_connections->create();\n\t\t\tif (NULL != connection)\n\t\t\t{\n\t\t\t\tHandle handle = { m_connections->getHandle(connection) };\n\t\t\t\tconnection->connect(handle, _ip, _port, _raw, _secure?m_sslCtx:NULL);\n\t\t\t\treturn handle;\n\t\t\t}\n\n\t\t\treturn invalidHandle;\n\t\t}\n\n\t\tvoid disconnect(Handle _handle, bool _finish)\n\t\t{\n\t\t\tBX_ASSERT(_handle.idx < m_connections->getMaxHandles(), \"Invalid handle %d!\", _handle.idx);\n\n\t\t\tConnection* connection = { m_connections->getFromHandle(_handle.idx) };\n\t\t\tif (_finish\n\t\t\t&&  connection->hasSocket() )\n\t\t\t{\n\t\t\t\tMessage* msg = msgAlloc(_handle, 0, false, Internal::Disconnect);\n\t\t\t\tconnection->send(msg);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tBX_TRACE(\"Disconnect %d - Client closed connection.\", _handle);\n\t\t\t\tconnection->disconnect();\n\n\t\t\t\tMessage* msg = msgAlloc(_handle, 2, true);\n\t\t\t\tmsg->data[0] = 0;\n\t\t\t\tmsg->data[1] = Internal::Disconnect;\n\t\t\t\tctxPush(msg);\n\t\t\t}\n\t\t}\n\n\t\tvoid notify(Handle _handle, uint64_t _userData)\n\t\t{\n\t\t\tBX_ASSERT(_handle.idx == invalidHandle.idx // loopback\n\t\t\t      || _handle.idx < m_connections->getMaxHandles(), \"Invalid handle %d!\", _handle.idx);\n\n\t\t\tif (invalidHandle.idx != _handle.idx)\n\t\t\t{\n\t\t\t\tMessage* msg = msgAlloc(_handle, sizeof(_userData), false, Internal::Notify);\n\t\t\t\tbx::memCopy(msg->data, &_userData, sizeof(_userData) );\n\t\t\t\tConnection* connection = m_connections->getFromHandle(_handle.idx);\n\t\t\t\tconnection->send(msg);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// loopback\n\t\t\t\tMessage* msg = msgAlloc(_handle, sizeof(_userData)+1, true);\n\t\t\t\tmsg->data[0] = MessageId::Notify;\n\t\t\t\tbx::memCopy(&msg->data[1], &_userData, sizeof(_userData) );\n\t\t\t\tctxPush(msg);\n\t\t\t}\n\t\t}\n\n\t\tvoid send(Message* _msg)\n\t\t{\n\t\t\tBX_ASSERT(_msg->handle.idx == invalidHandle.idx // loopback\n\t\t\t      || _msg->handle.idx < m_connections->getMaxHandles(), \"Invalid handle %d!\", _msg->handle.idx);\n\n\t\t\tif (invalidHandle.idx != _msg->handle.idx)\n\t\t\t{\n\t\t\t\tConnection* connection = m_connections->getFromHandle(_msg->handle.idx);\n\t\t\t\tconnection->send(_msg);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// loopback\n\t\t\t\tpush(_msg);\n\t\t\t}\n\t\t}\n\n\t\tMessage* recv()\n\t\t{\n\t\t\tif (NULL != m_listenSockets)\n\t\t\t{\n\t\t\t\tfor (uint16_t ii = 0, num = m_listenSockets->getNumHandles(); ii < num; ++ii)\n\t\t\t\t{\n\t\t\t\t\tListenSocket* listenSocket = m_listenSockets->getFromHandleAt(ii);\n\t\t\t\t\tlistenSocket->update();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (uint16_t ii = 0, num = m_connections->getNumHandles(); ii < num; ++ii)\n\t\t\t{\n\t\t\t\tConnection* connection = m_connections->getFromHandleAt(ii);\n\t\t\t\tconnection->update();\n\t\t\t}\n\n\t\t\tMessage* msg = m_incoming.pop();\n\n\t\t\twhile (NULL != msg)\n\t\t\t{\n\t\t\t\tif (invalidHandle.idx == msg->handle.idx) // loopback\n\t\t\t\t{\n\t\t\t\t\treturn msg;\n\t\t\t\t}\n\n\t\t\t\tConnection* connection = m_connections->getFromHandle(msg->handle.idx);\n\n\t\t\t\tuint8_t id = msg->data[0];\n\t\t\t\tif (0 == id\n\t\t\t\t&&  Internal::Disconnect == msg->data[1])\n\t\t\t\t{\n\t\t\t\t\tm_connections->destroy(connection);\n\t\t\t\t}\n\t\t\t\telse if (connection->hasSocket() || MessageId::UserDefined > id)\n\t\t\t\t{\n\t\t\t\t\treturn msg;\n\t\t\t\t}\n\n\t\t\t\trelease(msg);\n\t\t\t\tmsg = m_incoming.pop();\n\t\t\t}\n\n\t\t\treturn msg;\n\t\t}\n\n\t\tvoid push(Message* _msg)\n\t\t{\n\t\t\tm_incoming.push(_msg);\n\t\t}\n\n\tprivate:\n\t\tConnections* m_connections;\n\t\tListenSockets* m_listenSockets;\n\n\t\tMessageQueue m_incoming;\n\n#if BNET_CONFIG_OPENSSL\n\t\tstatic void* sslMalloc(size_t _size)\n\t\t{\n\t\t\treturn BX_ALLOC(g_allocator, _size);\n\t\t}\n\n\t\tstatic void* sslRealloc(void* _ptr, size_t _size)\n\t\t{\n\t\t\treturn BX_REALLOC(g_allocator, _ptr, _size);\n\t\t}\n\n\t\tstatic void sslFree(void* _ptr)\n\t\t{\n\t\t\treturn BX_FREE(g_allocator, _ptr);\n\t\t}\n\n\t\ttypedef void* (*MallocFn)(size_t _size);\n\t\tMallocFn m_sslMalloc;\n\n\t\ttypedef void* (*ReallocFn)(void* _ptr, size_t _size);\n\t\tReallocFn m_sslRealloc;\n\n\t\ttypedef void (*FreeFn)(void* _ptr);\n\t\tFreeFn m_sslFree;\n#endif // BNET_CONFIG_OPENSSL\n\n\t\tSSL_CTX* m_sslCtx;\n\t\tSSL_CTX* m_sslCtxServer;\n\t};\n\n\tstatic Context s_ctx;\n\n\tHandle ctxAccept(Handle _listenHandle, SOCKET _socket, uint32_t _ip, uint16_t _port, bool _raw, X509* _cert, EVP_PKEY* _key)\n\t{\n\t\treturn s_ctx.accept(_listenHandle, _socket, _ip, _port, _raw, _cert, _key);\n\t}\n\n\tvoid ctxPush(Handle _handle, MessageId::Enum _id)\n\t{\n\t\tMessage* msg = msgAlloc(_handle, 1, true);\n\t\tmsg->data[0] = uint8_t(_id);\n\t\ts_ctx.push(msg);\n\t}\n\n\tvoid ctxPush(Message* _msg)\n\t{\n\t\ts_ctx.push(_msg);\n\t}\n\n\tMessage* msgAlloc(Handle _handle, uint16_t _size, bool _incoming, Internal::Enum _type)\n\t{\n\t\tuint16_t offset = _incoming ? 0 : 2;\n\t\tMessage* msg = (Message*)bx::alloc(g_allocator, sizeof(Message) + offset + _size);\n\t\tmsg->size = _size;\n\t\tmsg->handle = _handle;\n\t\tuint8_t* data = (uint8_t*)msg + sizeof(Message);\n\t\tdata[0] = uint8_t(_type);\n\t\tmsg->data = data + offset;\n\t\treturn msg;\n\t}\n\n\tvoid msgRelease(Message* _msg)\n\t{\n\t\tbx::free(g_allocator, _msg);\n\t}\n\n\tvoid init(uint16_t _maxConnections, uint16_t _maxListenSockets, const char* _certs[], bx::AllocatorI* _allocator)\n\t{\n\t\tif (NULL != _allocator)\n\t\t{\n\t\t\tg_allocator = _allocator;\n\t\t}\n\n#if BX_PLATFORM_WINDOWS\n\t\tWSADATA wsaData;\n\t\tWSAStartup(MAKEWORD(2, 2), &wsaData);\n#endif // BX_PLATFORM_WINDOWS\n\n\t\ts_ctx.init(_maxConnections, _maxListenSockets, _certs);\n\t}\n\n\tvoid shutdown()\n\t{\n\t\ts_ctx.shutdown();\n\n#if BX_PLATFORM_WINDOWS\n\t\tWSACleanup();\n#endif // BX_PLATFORM_WINDOWS\n\t}\n\n\tHandle listen(uint32_t _ip, uint16_t _port, bool _raw, const char* _cert, const char* _key)\n\t{\n\t\treturn s_ctx.listen(_ip, _port, _raw, _cert, _key);\n\t}\n\n\tvoid stop(Handle _handle)\n\t{\n\t\treturn s_ctx.stop(_handle);\n\t}\n\n\tHandle connect(uint32_t _ip, uint16_t _port, bool _raw, bool _secure)\n\t{\n\t\treturn s_ctx.connect(_ip, _port, _raw, _secure);\n\t}\n\n\tvoid disconnect(Handle _handle, bool _finish)\n\t{\n\t\ts_ctx.disconnect(_handle, _finish);\n\t}\n\n\tvoid notify(Handle _handle, uint64_t _userData)\n\t{\n\t\ts_ctx.notify(_handle, _userData);\n\t}\n\n\tOutgoingMessage* alloc(Handle _handle, uint16_t _size)\n\t{\n\t\treturn msgAlloc(_handle, _size);\n\t}\n\n\tvoid release(IncomingMessage* _msg)\n\t{\n\t\tmsgRelease(_msg);\n\t}\n\n\tvoid send(OutgoingMessage* _msg)\n\t{\n\t\ts_ctx.send(_msg);\n\t}\n\n\tIncomingMessage* recv()\n\t{\n\t\treturn s_ctx.recv();\n\t}\n\n\tuint32_t toIpv4(const char* _addr)\n\t{\n\t\tuint32_t a0, a1, a2, a3;\n\t\tchar dummy;\n\t\tif (4 == sscanf(_addr, \"%d.%d.%d.%d%c\", &a0, &a1, &a2, &a3, &dummy)\n\t\t&&  a0 <= 0xff\n\t\t&&  a1 <= 0xff\n\t\t&&  a2 <= 0xff\n\t\t&&  a3 <= 0xff)\n\t\t{\n\t\t\treturn (a0<<24) | (a1<<16) | (a2<<8) | a3;\n\t\t}\n\n\t\tuint32_t ip = 0;\n\t\tstruct addrinfo* result = NULL;\n\t\tstruct addrinfo hints;\n\t\tbx::memSet(&hints, 0, sizeof(hints) );\n\t\thints.ai_family = AF_UNSPEC;\n\n\t\tint res = getaddrinfo(_addr, NULL, &hints, &result);\n\n\t\tif (0 == res)\n\t\t{\n\t\t\twhile (result)\n\t\t\t{\n\t\t\t\tsockaddr_in* addr = (sockaddr_in*)result->ai_addr;\n\t\t\t\tif (AF_INET == result->ai_family\n\t\t\t\t&&  INADDR_LOOPBACK != addr->sin_addr.s_addr)\n\t\t\t\t{\n\t\t\t\t\tip = ntohl(addr->sin_addr.s_addr);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tresult = result->ai_next;\n\t\t\t}\n\t\t}\n\n\t\tif (NULL != result)\n\t\t{\n\t\t\tfreeaddrinfo(result);\n\t\t}\n\n\t\treturn ip;\n\t}\n\n} // namespace bnet\n"
  },
  {
    "path": "src/bnet_p.h",
    "content": "/*\n * Copyright 2010-2026 Branimir Karadzic. All rights reserved.\n * License: https://github.com/bkaradzic/bnet/blob/master/LICENSE\n */\n\n#ifndef BNET_P_H_HEADER_GUARD\n#define BNET_P_H_HEADER_GUARD\n\n#include <bnet/bnet.h>\n#include <bx/bx.h>\n\n#ifndef BNET_CONFIG_OPENSSL\n#\tdefine BNET_CONFIG_OPENSSL 0 //(BX_PLATFORM_WINDOWS && BX_COMPILER_MSVC) || BX_PLATFORM_ANDROID || BX_PLATFORM_LINUX\n#endif // BNET_CONFIG_OPENSSL\n\n#ifndef BNET_CONFIG_CONNECT_TIMEOUT_SECONDS\n#\tdefine BNET_CONFIG_CONNECT_TIMEOUT_SECONDS 5\n#endif // BNET_CONFIG_CONNECT_TIMEOUT_SECONDS\n\n#ifndef BNET_CONFIG_MAX_INCOMING_BUFFER_SIZE\n#\tdefine BNET_CONFIG_MAX_INCOMING_BUFFER_SIZE (64<<10)\n#endif // BNET_CONFIG_MAX_INCOMING_BUFFER_SIZE\n\n#if BX_PLATFORM_WINDOWS\n#\tif BX_PLATFORM_WINDOWS\n#\t\tif !defined(_WIN32_WINNT)\n#\t\t\tdefine _WIN32_WINNT 0x0501\n#\t\tendif\n#\t\tinclude <ws2tcpip.h>\n#\tendif\n#\tdefine socklen_t int32_t\n#\tinclude \"inet_socket.h\"\n#elif  BX_PLATFORM_LINUX \\\n\t|| BX_PLATFORM_ANDROID \\\n\t|| BX_PLATFORM_EMSCRIPTEN \\\n\t|| BX_PLATFORM_OSX \\\n\t|| BX_PLATFORM_IOS\n#\tinclude <memory.h>\n#\tinclude <errno.h> // errno\n#\tinclude <fcntl.h>\n#\tinclude <netdb.h>\n#\tinclude <unistd.h>\n#\tinclude <sys/socket.h>\n#\tinclude <sys/time.h> // gettimeofday\n#\tinclude <arpa/inet.h> // inet_addr\n#\tinclude <netinet/in.h>\n#\tinclude <netinet/tcp.h>\n\ttypedef int SOCKET;\n\ttypedef linger LINGER;\n\ttypedef hostent HOSTENT;\n\ttypedef in_addr IN_ADDR;\n\n#\tdefine SOCKET_ERROR (-1)\n#\tdefine INVALID_SOCKET (-1)\n#\tdefine closesocket close\n#\tinclude \"inet_socket.h\"\n#endif // BX_PLATFORM_\n\n#include <bx/debug.h>\n#include <bx/handlealloc.h>\n#include <bx/ringbuffer.h>\n#include <bx/timer.h>\n#include <bx/allocator.h>\n\n#include <new> // placement new\n#include <stdio.h> // sscanf\n\n#if BNET_CONFIG_OPENSSL\n#\tinclude <openssl/err.h>\n#\tinclude <openssl/ssl.h>\n#\tinclude <openssl/crypto.h>\n#else\n#\tdefine SSL_CTX void\n#\tdefine X509 void\n#\tdefine EVP_PKEY void\n#endif // BNET_CONFIG_OPENSSL\n\n#include <list>\n\nnamespace bnet\n{\n\tstruct Internal\n\t{\n\t\tenum Enum\n\t\t{\n\t\t\tNone,\n\t\t\tDisconnect,\n\t\t\tNotify,\n\t\t};\n\t};\n\n\textern bx::AllocatorI* g_allocator;\n\n\tHandle ctxAccept(Handle _listenHandle, SOCKET _socket, uint32_t _ip, uint16_t _port, bool _raw, X509* _cert, EVP_PKEY* _key);\n\tvoid ctxPush(Handle _handle, MessageId::Enum _id);\n\tvoid ctxPush(Message* _msg);\n\tMessage* msgAlloc(Handle _handle, uint16_t _size, bool _incoming = false, Internal::Enum _type = Internal::None);\n\tvoid msgRelease(Message* _msg);\n\n\ttemplate<typename Ty>\n\tclass FreeList\n\t{\n\tpublic:\n\t\tFreeList(uint16_t _max)\n\t\t{\n\t\t\tm_memBlock = bx::alloc(g_allocator, _max*sizeof(Ty) );\n\t\t\tm_handleAlloc = bx::createHandleAlloc(g_allocator, _max);\n\t\t}\n\n\t\t~FreeList()\n\t\t{\n\t\t\tbx::destroyHandleAlloc(g_allocator, m_handleAlloc);\n\t\t\tbx::free(g_allocator, m_memBlock);\n\t\t}\n\n\t\tTy* create()\n\t\t{\n\t\t\tuint16_t handle = m_handleAlloc->alloc();\n\t\t\tif (handle == bx::kInvalidHandle) {\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\tTy* first = reinterpret_cast<Ty*>(m_memBlock);\n\t\t\tTy* obj = &first[handle];\n\t\t\tobj = ::new (obj) Ty;\n\t\t\treturn obj;\n\t\t}\n\n\t\ttemplate<typename Arg0> Ty* create(Arg0 _a0)\n\t\t{\n\t\t\tuint16_t handle = m_handleAlloc->alloc();\n\t\t\tif (handle == bx::kInvalidHandle) {\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\tTy* first = reinterpret_cast<Ty*>(m_memBlock);\n\t\t\tTy* obj = &first[handle];\n\t\t\tobj = ::new (obj) Ty(_a0);\n\t\t\treturn obj;\n\t\t}\n\n\t\ttemplate<typename Arg0, typename Arg1> Ty* create(Arg0 _a0, Arg1 _a1)\n\t\t{\n\t\t\tuint16_t handle = m_handleAlloc->alloc();\n\t\t\tif (handle == bx::kInvalidHandle) {\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\tTy* first = reinterpret_cast<Ty*>(m_memBlock);\n\t\t\tTy* obj = &first[handle];\n\t\t\tobj = ::new (obj) Ty(_a0, _a1);\n\t\t\treturn obj;\n\t\t}\n\n\t\ttemplate<typename Arg0, typename Arg1, typename Arg2> Ty* create(Arg0 _a0, Arg1 _a1, Arg2 _a2)\n\t\t{\n\t\t\tuint16_t handle = m_handleAlloc->alloc();\n\t\t\tif (handle == bx::kInvalidHandle) {\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\tTy* first = reinterpret_cast<Ty*>(m_memBlock);\n\t\t\tTy* obj = &first[handle];\n\t\t\tobj = ::new (obj) Ty(_a0, _a1, _a2);\n\t\t\treturn obj;\n\t\t}\n\n\t\tvoid destroy(Ty* _obj)\n\t\t{\n\t\t\t_obj->~Ty();\n\t\t\tm_handleAlloc->free(getHandle(_obj) );\n\t\t}\n\n\t\tuint16_t getHandle(Ty* _obj) const\n\t\t{\n\t\t\tTy* first = reinterpret_cast<Ty*>(m_memBlock);\n\t\t\treturn (uint16_t)(_obj - first);\n\t\t}\n\n\t\tTy* getFromHandle(uint16_t _index)\n\t\t{\n\t\t\tTy* first = reinterpret_cast<Ty*>(m_memBlock);\n\t\t\treturn &first[_index];\n\t\t}\n\n\t\tuint16_t getNumHandles() const\n\t\t{\n\t\t\treturn m_handleAlloc->getNumHandles();\n\t\t}\n\n\t\tuint16_t getMaxHandles() const\n\t\t{\n\t\t\treturn m_handleAlloc->getMaxHandles();\n\t\t}\n\n\t\tTy* getFromHandleAt(uint16_t _at)\n\t\t{\n\t\t\tuint16_t handle = m_handleAlloc->getHandleAt(_at);\n\t\t\treturn getFromHandle(handle);\n\t\t}\n\n\tprivate:\n\t\tvoid* m_memBlock;\n\t\tbx::HandleAlloc* m_handleAlloc;\n\t};\n\n\tclass RecvRingBuffer\n\t{\n\t\tBX_CLASS(RecvRingBuffer\n\t\t\t, NO_DEFAULT_CTOR\n\t\t\t, NO_COPY\n\t\t\t);\n\n\tpublic:\n\t\tRecvRingBuffer(bx::RingBufferControl& _control, char* _buffer)\n\t\t\t: m_control(_control)\n\t\t\t, m_write(_control.m_current)\n\t\t\t, m_reserved(0)\n\t\t\t, m_buffer(_buffer)\n\t\t{\n\t\t}\n\n\t\t~RecvRingBuffer()\n\t\t{\n\t\t}\n\n\t\tint recv(SOCKET _socket)\n\t\t{\n\t\t\tm_reserved += m_control.reserve(UINT32_MAX);\n\t\t\tuint32_t end = (m_write + m_reserved) % m_control.m_size;\n\t\t\tuint32_t wrap = end < m_write ? m_control.m_size - m_write : m_reserved;\n\t\t\tchar* to = &m_buffer[m_write];\n\n\t\t\tint bytes = ::recv(_socket\n\t\t\t\t\t\t\t  , to\n\t\t\t\t\t\t\t  , wrap\n\t\t\t\t\t\t\t  , 0\n\t\t\t\t\t\t\t  );\n\n\t\t\tif (0 < bytes)\n\t\t\t{\n\t\t\t\tm_write += bytes;\n\t\t\t\tm_write %= m_control.m_size;\n\t\t\t\tm_reserved -= bytes;\n\t\t\t\tm_control.commit(bytes);\n\t\t\t}\n\n\t\t\treturn bytes;\n\t\t}\n\n#if BNET_CONFIG_OPENSSL\n\t\tint recv(SSL* _ssl)\n\t\t{\n\t\t\tm_reserved += m_control.reserve(-1);\n\t\t\tuint32_t end = (m_write + m_reserved) % m_control.m_size;\n\t\t\tuint32_t wrap = end < m_write ? m_control.m_size - m_write : m_reserved;\n\t\t\tchar* to = &m_buffer[m_write];\n\n\t\t\tint bytes = SSL_read(_ssl\n\t\t\t\t\t\t\t\t, to\n\t\t\t\t\t\t\t\t, wrap\n\t\t\t\t\t\t\t\t);\n\n\t\t\tif (0 < bytes)\n\t\t\t{\n\t\t\t\tm_write += bytes;\n\t\t\t\tm_write %= m_control.m_size;\n\t\t\t\tm_reserved -= bytes;\n\t\t\t\tm_control.commit(bytes);\n\t\t\t}\n\n\t\t\treturn bytes;\n\t\t}\n#endif // BNET_CONFIG_OPENSSL\n\n\tprivate:\n\t\tbx::RingBufferControl& m_control;\n\t\tuint32_t m_write;\n\t\tuint32_t m_reserved;\n\t\tchar* m_buffer;\n\t};\n\n\tclass MessageQueue\n\t{\n\tpublic:\n\t\tMessageQueue()\n\t\t{\n\t\t}\n\n\t\t~MessageQueue()\n\t\t{\n\t\t}\n\n\t\tvoid push(Message* _msg)\n\t\t{\n\t\t\tm_queue.push_back(_msg);\n\t\t}\n\n\t\tMessage* peek()\n\t\t{\n\t\t\tif (!m_queue.empty() )\n\t\t\t{\n\t\t\t\treturn m_queue.front();\n\t\t\t}\n\n\t\t\treturn NULL;\n\t\t}\n\n\t\tMessage* pop()\n\t\t{\n\t\t\tif (!m_queue.empty() )\n\t\t\t{\n\t\t\t\tMessage* msg = m_queue.front();\n\t\t\t\tm_queue.pop_front();\n\t\t\t\treturn msg;\n\t\t\t}\n\n\t\t\treturn NULL;\n\t\t}\n\n\tprivate:\n\t\tstd::list<Message*> m_queue;\n\t};\n\n} // namespace bnet\n\n#endif // BNET_P_H_HEADER_GUARD\n"
  },
  {
    "path": "src/inet_socket.h",
    "content": "/*\n * Copyright 2010-2026 Branimir Karadzic. All rights reserved.\n * License: https://github.com/bkaradzic/bnet/blob/master/LICENSE\n */\n\n#ifndef BNET_INET_SOCKET_H_HEADER_GUARD\n#define BNET_INET_SOCKET_H_HEADER_GUARD\n\nstatic int connectsocket(SOCKET socket, uint32_t _ip, uint16_t _port, bool /*_secure*/)\n{\n\tsockaddr_in addr;\n\tbx::memSet(&addr, 0, sizeof(addr) );\n\taddr.sin_family      = AF_INET;\n\taddr.sin_addr.s_addr = htonl(_ip);\n\taddr.sin_port        = htons(_port);\n\n\tunion\n\t{\n\t\tsockaddr*    sa;\n\t\tsockaddr_in* sain;\n\t} saintosa;\n\n\tsaintosa.sain = &addr;\n\t\n\treturn ::connect(socket, saintosa.sa, sizeof(addr) );\n}\n\nstatic bool issocketready(SOCKET socket)\n{\n\tfd_set rfds;\n\tFD_ZERO(&rfds);\n\tfd_set wfds;\n\tFD_ZERO(&wfds);\n\tFD_SET(socket, &rfds);\n\tFD_SET(socket, &wfds);\n\n\ttimeval timeout;\n\ttimeout.tv_sec  = 0;\n\ttimeout.tv_usec = 0;\n\n\tint result = ::select( (int)socket + 1 /*nfds is ignored on windows*/, &rfds, &wfds, NULL, &timeout);\n\treturn result > 0;\n}\n\n#endif // BNET_INET_SOCKET_H_HEADER_GUARD\n"
  }
]