diff --git a/.circleci/config.yml b/.circleci/config.yml index da2e481..5356968 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,20 +1,17 @@ -# Golang CircleCI 2.0 configuration file -# -# Check https://circleci.com/docs/2.0/language-go/ for more details -version: 2 +version: 2.1 + jobs: - build: + hello-job: docker: - - image: golang:1.20 - environment: - GO111MODULE: "on" - REDIS_HOST: "localhost:6379" - - image: redis:latest - working_directory: /go/src/github.com/iguagile/iguagile-engine + - image: cimg/node:17.2.0 # the primary container, where your job's commands are run + auth: + username: mydockerhub-user + password: $DOCKERHUB_PASSWORD # context / project UI env-var reference steps: - - checkout - - run: go get golang.org/x/tools/cmd/goimports && diff <(goimports -d $(find . -type f -name '*.go' -not -path "./vendor/*" -not -path "./lib/*")) <(printf "") - - run: go install golang.org/x/lint/golint@latest && golint -set_exit_status ./... - - run: go test -bench=. -v ./... - - run: bash ./fail_test.bash + - checkout # check out the code in the project directory + - run: echo "hello world" # run the `echo` command +workflows: + my-workflow: + jobs: + - hello-job diff --git a/.env b/.env new file mode 100644 index 0000000..81d197d --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +REDIS_HOST=localhost:6379 +GRPC_PORT=4000 \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index d921d0f..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,7 +0,0 @@ -version: 2 -updates: -- package-ecosystem: gomod - directory: "/" - schedule: - interval: daily - open-pull-requests-limit: 10 diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml new file mode 100644 index 0000000..1e324ef --- /dev/null +++ b/.github/workflows/actionlint.yml @@ -0,0 +1,23 @@ +name: actionlint + +on: + push: + paths: + - .github/workflows/** + pull_request: + paths: + - .github/workflows/** + +jobs: + actionlint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Download actionlint + id: get_actionlint + run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) + shell: bash + - name: Check workflow files + run: ${{ steps.get_actionlint.outputs.executable }} -color + shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..79ecb36 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +on: [push] + +name: CI + +jobs: + build_and_test: + name: Rust project + runs-on: ubuntu-latest + steps: + - name: + run: | + sudo apt-get install protobuf-compiler + - uses: actions/checkout@v2 + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - uses: actions-rs/cargo@v1 + with: + command: test + args: + services: + redis: + image: redis + ports: + - 6379:6379 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 500546b..0000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,71 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -name: "CodeQL" - -on: - push: - branches: [master, prototype] - pull_request: - # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 23 * * 3' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - # Override automatic language detection by changing the below list - # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] - language: ['go'] - # Learn more... - # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 - - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # â„šī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 diff --git a/.gitignore b/.gitignore index f1c181e..576e4fe 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,8 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out + + +# Added by cargo + +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..901a480 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1189 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" + +[[package]] +name = "async-stream" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad445822218ce64be7a341abfb0b1ea43b5c23aa83902542a4542e78309d8e5e" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-trait" +version = "0.1.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ea188f25f0255d8f92797797c97ebf5631fa88178beb1a46fdf5622c9a00e4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.2", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "axum" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d8068b6ccb8b34db9de397c7043f91db8b4c66414952c6db944f238c4d3db3" +dependencies = [ + "async-trait", + "axum-core", + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2f958c80c248b34b9a877a643811be8dbca03ca5ba827f2b63baf3a81e5fc4e" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitmaps" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703642b98a00b3b90513279a8ede3fcfa479c126c5fb46e78f3051522f021403" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" + +[[package]] +name = "futures-sink" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" + +[[package]] +name = "futures-task" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" + +[[package]] +name = "futures-util" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "h2" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "iguagile_engine" +version = "0.1.0" +dependencies = [ + "anyhow", + "bitmaps", + "prost", + "redis", + "serde_json", + "tokio", + "tonic", + "tonic-build", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd6da19f25979c7270e70fa95ab371ec3b701cd0eefc47667a09785b3c59155" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matchit" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.45.0", +] + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "petgraph" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "proc-macro2" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48e50df39172a3e7eb17e14642445da64996989bc212b583015435d39a58537" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c828f93f5ca4826f97fedcbd3f9a536c16b12cff3dbbb4a007f932bbad95b12" +dependencies = [ + "bytes", + "heck", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 1.0.109", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea9b0f8cbe5e15a8a042d030bd96668db28ecb567ec37d691971ff5731d2b1b" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-types" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "379119666929a1afd7a043aa6cf96fa67a6dce9af60c88095a4686dbce4c9c88" +dependencies = [ + "prost", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redis" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa8455fa3621f6b41c514946de66ea0531f57ca017b2e6c7cc368035ea5b46df" +dependencies = [ + "combine", + "itoa", + "percent-encoding", + "ryu", + "sha1_smol", + "url", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "rustix" +version = "0.36.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fe885c3a125aa45213b68cc1472a49880cb5923dc23f522ad2791b882228778" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "serde" +version = "1.0.157" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707de5fcf5df2b5788fca98dd7eab490bc2fd9b7ef1404defc462833b83f25ca" + +[[package]] +name = "serde_json" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59d3276aee1fa0c33612917969b5172b5be2db051232a6e4826f1a1a9191b045" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "tempfile" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.42.0", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.45.0", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tokio-stream" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tonic" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "prost-derive", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tower-layer", + "tower-service", + "tracing", + "tracing-futures", +] + +[[package]] +name = "tonic-build" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "unicode-bidi" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d502c968c6a838ead8e69b2ee18ec708802f99db92a0d156705ec9ef801993b" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "which" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +dependencies = [ + "either", + "libc", + "once_cell", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5e3566f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "iguagile_engine" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tonic = "0.8" +prost = "0.11" +redis = "0.22.3" +anyhow = "1.0" +serde_json = "1.0" +bitmaps = "3.2.0" +tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } + +[build-dependencies] +tonic-build = "0.8" diff --git a/Dockerfile b/Dockerfile index fcc5397..8b5d201 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,10 @@ FROM golang:alpine AS build -ENV GO111MODULE=on -RUN apk add --no-cache git -RUN \ - cd $GOPATH/src/ && \ - mkdir -p github.com/iguagile && \ - cd github.com/iguagile && \ - git clone https://github.com/iguagile/iguagile-engine.git && \ - cd ./iguagile-engine && \ - GOOS=linux CGO_ENABLED=0 go build -a -o out cmd/relay/main.go && \ - cp out /app +WORKDIR $GOPATH/src/github.com/iguagile/iguagile-engine + +COPY . . + +RUN CGO_ENABLED=0 go build -o /app cmd/relay/main.go FROM alpine RUN apk add --no-cache tzdata ca-certificates diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..ce5f635 --- /dev/null +++ b/build.rs @@ -0,0 +1,4 @@ +fn main() -> Result<(), Box> { + tonic_build::compile_protos("src/room.proto")?; + Ok(()) +} diff --git a/cmd/relay/main.go b/cmd/relay/main.go index 20eac1d..bfe50e1 100644 --- a/cmd/relay/main.go +++ b/cmd/relay/main.go @@ -1,10 +1,15 @@ package main import ( + "context" + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "encoding/pem" "log" - "net" + "math/big" "os" - "strconv" "github.com/iguagile/iguagile-engine/iguagile" ) @@ -12,29 +17,48 @@ import ( func main() { factory := &iguagile.RelayServiceFactory{} address := os.Getenv("ROOM_HOST") - if address == "" { - address = "localhost:0" - } + apiAddr := os.Getenv("GRPC_HOST") store, err := iguagile.NewRedis(os.Getenv("REDIS_HOST")) if err != nil { log.Fatal(err) } - server, err := iguagile.NewRoomServer(factory, store, address) + engine := iguagile.New(factory, store) + + tlsConf, err := generateTLSConfig() if err != nil { log.Fatal(err) } - listener, err := net.Listen("tcp", address) - if err != nil { + if err := engine.Start(context.Background(), address, apiAddr, tlsConf); err != nil { log.Fatal(err) } +} - port, err := strconv.Atoi(os.Getenv("GRPC_PORT")) +func generateTLSConfig() (*tls.Config, error) { + key, err := rsa.GenerateKey(rand.Reader, 1024) if err != nil { - log.Fatal(err) + return nil, err + } + + template := x509.Certificate{SerialNumber: big.NewInt(1)} + certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key) + if err != nil { + return nil, err + } + + keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}) + certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) + + tlsCert, err := tls.X509KeyPair(certPEM, keyPEM) + if err != nil { + return nil, err } - log.Fatal(server.Run(listener, port)) + return &tls.Config{ + Certificates: []tls.Certificate{tlsCert}, + NextProtos: []string{"iguagile-example"}, + InsecureSkipVerify: true, + }, nil } diff --git a/docker-compose.yml b/docker-compose.yml index 4305f99..229a603 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,8 +4,7 @@ services: redis: image: redis:latest ports: - - 6379:6379 - + - "6379:6379" iguagile-api: image: iguagile/iguagile-api:latest diff --git a/go.mod b/go.mod deleted file mode 100644 index e3f2fe4..0000000 --- a/go.mod +++ /dev/null @@ -1,21 +0,0 @@ -module github.com/iguagile/iguagile-engine - -go 1.19 - -require ( - github.com/golang/protobuf v1.5.3 - github.com/gomodule/redigo v2.0.0+incompatible - github.com/google/uuid v1.3.0 - github.com/iguagile/iguagile-room-proto v0.0.0-20230309035241-e4f3a4080a3b - github.com/minami14/idgo v1.1.1 - google.golang.org/grpc v1.53.0 -) - -require ( - github.com/minami14/go-bitarray v1.1.2 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect - google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect - google.golang.org/protobuf v1.28.1 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index c472c09..0000000 --- a/go.sum +++ /dev/null @@ -1,37 +0,0 @@ -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/iguagile/iguagile-room-proto v0.0.0-20230208040055-3970215b35de h1:BsN82dGwiDeX6YoJfD71tMCScsejGgWLX+AximGyhXQ= -github.com/iguagile/iguagile-room-proto v0.0.0-20230208040055-3970215b35de/go.mod h1:N8jTkjDzHwk4pRJxowS7vpo7wBXnZDNa/p56YOChl40= -github.com/iguagile/iguagile-room-proto v0.0.0-20230225070552-d9cae34a9883 h1:9UcZkPyEM+mgE782lX2pUySYJ1GMawETdXh+VTAgQrQ= -github.com/iguagile/iguagile-room-proto v0.0.0-20230225070552-d9cae34a9883/go.mod h1:tYs2ria+UzXfahEFLlYKm+hsDUUW7XmBEPqzW+Fdqss= -github.com/iguagile/iguagile-room-proto v0.0.0-20230309035241-e4f3a4080a3b h1:y0WfWuYeM/Wd0Bvptv7+0ljU0HPNCc0wBcElFLcPe5s= -github.com/iguagile/iguagile-room-proto v0.0.0-20230309035241-e4f3a4080a3b/go.mod h1:jZO/C8zSu5BWoGpl4Uo0Z6c7quCMtTYGXrTKL54eIcM= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/minami14/go-bitarray v1.1.2 h1:E+Nd3dGG+aLhpVlSaJCC7Fh+3xGsaT0zbuvrrJC+dyg= -github.com/minami14/go-bitarray v1.1.2/go.mod h1:i6OBYFrV3uyrMpN1jwB2fAwLypdT9FIPAOiognNQ8wc= -github.com/minami14/idgo v1.1.1 h1:hxokBHDQUqTMQhv2GOPFnLQ2QHHi5rGOKO7/woni17s= -github.com/minami14/idgo v1.1.1/go.mod h1:oxMlMROuiDEbZbOHzGw5D0mpsv8oGuLsBjUrwiRn9po= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= diff --git a/iguagile/binary.go b/iguagile/binary.go deleted file mode 100644 index 8def582..0000000 --- a/iguagile/binary.go +++ /dev/null @@ -1,55 +0,0 @@ -package iguagile - -import "errors" - -// Traffic -const ( - Inbound = iota - Outbound -) - -// Message types -const ( - NewConnect = iota - ExitConnect -) - -// BinaryData is client and server data transfer format. -type BinaryData struct { - Traffic int - ID []byte - Target byte - MessageType byte - Payload []byte -} - -// ErrInvalidDataFormat is when given unknown data. -var ErrInvalidDataFormat = errors.New("invalid data length") - -// NewInBoundData return a BinaryData struct parsed and formatted binary. -func NewInBoundData(b []byte) (*BinaryData, error) { - if len(b) < 2 { - return nil, ErrInvalidDataFormat - } - - return &BinaryData{ - Traffic: Inbound, - Target: b[0], - MessageType: b[1], - Payload: b[2:], - }, nil -} - -// NewOutBoundData return a BinaryData struct parsed and formatted binary. -func NewOutBoundData(b []byte) (*BinaryData, error) { - if len(b) < 3 { - return nil, ErrInvalidDataFormat - } - - return &BinaryData{ - Traffic: Outbound, - ID: b[:2], - MessageType: b[2], - Payload: b[3:], - }, nil -} diff --git a/iguagile/binary_test.go b/iguagile/binary_test.go deleted file mode 100644 index 2dca6d0..0000000 --- a/iguagile/binary_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package iguagile - -import ( - "reflect" - "testing" -) - -func TestInbound(t *testing.T) { - testData := []struct { - send []byte - want BinaryData - }{ - {append([]byte{1, 2}, []byte("hello")...), - BinaryData{ - Traffic: Inbound, - Target: byte(1), - MessageType: byte(2), - Payload: []byte("hello")}, - }, - {append([]byte{1, 3}, []byte("MSG")...), BinaryData{ - Traffic: Inbound, - Target: byte(1), - MessageType: byte(3), - Payload: []byte("MSG")}, - }, - {append([]byte{1, 4}, []byte("HOGE")...), BinaryData{ - Traffic: Inbound, - Target: byte(1), - MessageType: byte(4), - Payload: []byte("HOGE")}, - }, - } - for _, v := range testData { - d, err := NewInBoundData(v.send) - if err != nil { - t.Error(err) - } - if !reflect.DeepEqual(d.Payload, v.want.Payload) { - t.Errorf("missmatch Payload get: %v , want: %v", d.Payload, v.want.Payload) - } - if !reflect.DeepEqual(d.MessageType, v.want.MessageType) { - t.Errorf("missmatch MessageType get: %v , want: %v", d.Payload, v.want.Payload) - } - if !reflect.DeepEqual(d.Target, v.want.Target) { - t.Errorf("missmatch Target get: %v , want: %v", d.Target, v.want.Target) - } - if !reflect.DeepEqual(d.Traffic, v.want.Traffic) { - t.Errorf("missmatch Traffic get: %v , want: %v", d.Traffic, v.want.Traffic) - } - } -} - -func TestOutbound(t *testing.T) { - cid := make([]byte, 2) - cid[0] = 1 - cid[1] = 2 - - testData := []struct { - send []byte - want BinaryData - }{ - {append(cid, append([]byte{2}, []byte("hello")...)...), - BinaryData{ - Traffic: Outbound, - ID: cid, - MessageType: byte(2), - Payload: []byte("hello")}, - }, - {append(cid, append([]byte{NewConnect}, []byte("MSG")...)...), BinaryData{ - Traffic: Outbound, - ID: cid, - MessageType: byte(NewConnect), - Payload: []byte("MSG")}, - }, - {append(cid, append([]byte{ExitConnect}, []byte("HOGE")...)...), BinaryData{ - Traffic: Outbound, - ID: cid, - MessageType: byte(ExitConnect), - Payload: []byte("HOGE")}, - }, - } - for _, v := range testData { - d, err := NewOutBoundData(v.send) - if err != nil { - t.Error(err) - } - - if !reflect.DeepEqual(d.Payload, v.want.Payload) { - t.Errorf("missmatch Payload get: %v , want: %v", d.Payload, v.want.Payload) - } - if !reflect.DeepEqual(d.MessageType, v.want.MessageType) { - t.Errorf("missmatch MessageType get: %v , want: %v", d.Payload, v.want.Payload) - } - if !reflect.DeepEqual(d.ID, v.want.ID) { - t.Errorf("missmatch UUID get: %v , want: %v", d.Target, v.want.Target) - } - if !reflect.DeepEqual(d.Traffic, v.want.Traffic) { - t.Errorf("missmatch Traffic get: %v , want: %v", d.Traffic, v.want.Traffic) - } - } -} diff --git a/iguagile/client.go b/iguagile/client.go index aa4d7b1..68b4788 100644 --- a/iguagile/client.go +++ b/iguagile/client.go @@ -1,25 +1,25 @@ package iguagile import ( + "context" "encoding/binary" "errors" "fmt" - "io" "sync" ) // Client is a middleman between the connection and the room. type Client struct { - id int - idByte []byte - conn io.ReadWriteCloser - room *Room - send chan []byte + id int + idByte []byte + conn *quicConn + streams map[string]*quicStream + room *Room } // NewClient is Client constructed. -func NewClient(room *Room, conn io.ReadWriteCloser) (*Client, error) { - id, err := room.generator.Generate() +func NewClient(room *Room, conn *quicConn) (*Client, error) { + id, err := room.generator.generate() if err != nil { return nil, err } @@ -28,72 +28,43 @@ func NewClient(room *Room, conn io.ReadWriteCloser) (*Client, error) { binary.LittleEndian.PutUint16(idByte, uint16(id)) client := &Client{ - id: id, - idByte: idByte, - conn: conn, - room: room, - send: make(chan []byte), + id: id, + idByte: idByte, + conn: conn, + room: room, + streams: map[string]*quicStream{}, } return client, nil } -func (c *Client) read(buf []byte) (int, error) { - _, err := c.conn.Read(buf[:2]) - if err != nil { - return 0, err - } - - size := int(binary.LittleEndian.Uint16(buf)) - receivedSizeSum := 0 - for receivedSizeSum < size { - receivedSize, err := c.conn.Read(buf[receivedSizeSum:size]) - if err != nil { - return 0, err - } - - receivedSizeSum += receivedSize - } - - return size, nil -} - -func (c *Client) readStart() { - buf := make([]byte, maxMessageSize) - for { - n, err := c.read(buf) - if err != nil { - c.room.log.Println(err) - c.room.CloseConnection(c) - break - } - - if err = c.room.service.Receive(c.id, buf[:n]); err != nil { - c.room.log.Println(err) - c.room.CloseConnection(c) - break - } - } -} - -func (c *Client) write(message []byte) error { - size := len(message) - sizeByte := make([]byte, 2, size+2) - binary.LittleEndian.PutUint16(sizeByte, uint16(size)) - message = append(sizeByte, message...) - if _, err := c.conn.Write(message); err != nil { - return err - } - return nil -} - -func (c *Client) writeStart() { - for { - if err := c.write(<-c.send); err != nil { - c.room.log.Println(err) - c.room.CloseConnection(c) - break - } +func (c *Client) readStart(ctx context.Context) { + for _, stream := range c.streams { + go func(stream *quicStream) { + buf := make([]byte, maxMessageSize) + receive, err := c.room.service.ReceiveFunc(stream.name) + if err != nil { + c.room.log.Println(err) + return + } + for { + select { + case <-ctx.Done(): + return + default: + } + + n, err := stream.Read(buf) + if err != nil { + c.room.log.Println(err) + break + } + + if err := receive(c.id, buf[:n]); err != nil { + c.room.log.Println(err) + } + } + }(stream) } } @@ -107,11 +78,6 @@ func (c *Client) GetIDByte() []byte { return c.idByte } -// Send is enqueue outbound messages. -func (c *Client) Send(message []byte) { - c.send <- message -} - // Close closes the connection. func (c *Client) Close() error { return c.conn.Close() diff --git a/iguagile/conn.go b/iguagile/conn.go new file mode 100644 index 0000000..83eb820 --- /dev/null +++ b/iguagile/conn.go @@ -0,0 +1,72 @@ +package iguagile + +import ( + "context" + "encoding/binary" + "io" + + "github.com/lucas-clemente/quic-go" +) + +type quicStream struct { + stream quic.Stream + name string +} + +func (q *quicStream) Read(buf []byte) (int, error) { + if _, err := q.stream.Read(buf[:2]); err != nil { + return 0, err + } + + size := int(binary.LittleEndian.Uint16(buf)) + return io.ReadAtLeast(q.stream, buf[:size], size) +} + +func (q *quicStream) Write(buf []byte) (int, error) { + size := len(buf) + sizeByte := make([]byte, 2, size+2) + binary.LittleEndian.PutUint16(sizeByte, uint16(size)) + buf = append(sizeByte, buf...) + if _, err := q.stream.Write(buf); err != nil { + return 0, err + } + return size, nil +} + +func (q *quicStream) Close() error { + return q.stream.Close() +} + +type quicConn struct { + sess quic.Connection +} + +func (q *quicConn) AcceptStream() (*quicStream, error) { + stream, err := q.sess.AcceptStream(context.Background()) + if err != nil { + return nil, err + } + + return &quicStream{stream: stream}, nil +} + +func (q *quicConn) OpenStream() (*quicStream, error) { + stream, err := q.sess.OpenStream() + if err != nil { + return nil, err + } + + return &quicStream{stream: stream}, nil +} + +func (q *quicConn) ReceiveMessage() ([]byte, error) { + return q.sess.ReceiveMessage() +} + +func (q *quicConn) SendMessage(message []byte) error { + return q.sess.SendMessage(message) +} + +func (q *quicConn) Close() error { + return nil +} diff --git a/iguagile/server.go b/iguagile/engine.go similarity index 55% rename from iguagile/server.go rename to iguagile/engine.go index e237b8b..f589587 100644 --- a/iguagile/server.go +++ b/iguagile/engine.go @@ -3,9 +3,9 @@ package iguagile import ( "bytes" "context" + "crypto/tls" "encoding/binary" "fmt" - "io" "log" "net" "os" @@ -13,110 +13,128 @@ import ( "sync" "time" + "github.com/lucas-clemente/quic-go" + "github.com/google/uuid" pb "github.com/iguagile/iguagile-room-proto/room" "google.golang.org/grpc" ) -// RoomServer is server manages rooms. -type RoomServer struct { +const ( + userStreamPrefix = "U" +) + +// Engine is engine manages rooms. +type Engine struct { serverID int rooms *sync.Map factory RoomServiceFactory store Store - idGenerator *IDGenerator + idGenerator *idGenerator logger *log.Logger serverProto *pb.Server RoomUpdateDuration time.Duration ServerUpdateDuration time.Duration } -// ErrPortIsOutOfRange is invalid ports request. -var ErrPortIsOutOfRange = fmt.Errorf("port is out of range") +// New is a constructor of Engine. +func New(factory RoomServiceFactory, store Store) *Engine { + return &Engine{ + rooms: &sync.Map{}, + factory: factory, + store: store, + logger: log.New(os.Stdout, "iguagile-engine ", log.Lshortfile), + RoomUpdateDuration: time.Minute * 3, + ServerUpdateDuration: time.Minute * 3, + } +} -// NewRoomServer is a constructor of RoomServer. -func NewRoomServer(factory RoomServiceFactory, store Store, address string) (*RoomServer, error) { - host, portStr, err := net.SplitHostPort(address) +// Start api and room engine. +func (e *Engine) Start(ctx context.Context, address, apiAddress string, tlsConf *tls.Config) error { + listener, err := quic.ListenAddr(address, tlsConf, nil) if err != nil { - return nil, err + return err } - port, err := strconv.Atoi(portStr) + host, portStr, err := net.SplitHostPort(address) if err != nil { - return nil, err - } - if port > 65535 || port < 0 { - return nil, ErrPortIsOutOfRange + return err } - serverID, err := store.GenerateServerID() + port, err := strconv.Atoi(portStr) if err != nil { - return nil, err + return err } - token := uuid.New() + if port < 0 && port > 65535 { + return fmt.Errorf("port number is out of valid range %v", port) + } - server := &pb.Server{ - Host: host, - Port: int32(port), - ServerId: int32(serverID), - Token: token[:], + _, apiPortStr, err := net.SplitHostPort(address) + if err != nil { + return err } - idGenerator, err := NewIDGenerator() + apiPort, err := strconv.Atoi(apiPortStr) if err != nil { - return nil, err + return err } - return &RoomServer{ - serverID: serverID, - rooms: &sync.Map{}, - factory: factory, - store: store, - logger: log.New(os.Stdout, "iguagile-server ", log.Lshortfile), - serverProto: server, - RoomUpdateDuration: time.Minute * 3, - ServerUpdateDuration: time.Minute * 3, - idGenerator: idGenerator, - }, nil -} + if apiPort < 0 && apiPort > 65535 { + return fmt.Errorf("api port number is out of valid range %v", apiPort) + } -// Run starts api and room server. -func (s *RoomServer) Run(roomListener net.Listener, apiPort int) error { - if apiPort > 65535 || apiPort < 0 { - return ErrPortIsOutOfRange + serverID, err := e.store.GenerateServerID() + if err != nil { + return err } - s.serverProto.ApiPort = int32(apiPort) - server := grpc.NewServer() - apiListener, err := net.Listen("tcp", fmt.Sprintf(":%v", apiPort)) + grpcServer := grpc.NewServer() + apiListener, err := net.Listen("tcp", apiAddress) if err != nil { return err } - pb.RegisterRoomServiceServer(server, s) + pb.RegisterRoomServiceServer(grpcServer, e) go func() { - _ = server.Serve(apiListener) + _ = grpcServer.Serve(apiListener) }() - if err := s.store.RegisterServer(s.serverProto); err != nil { + token := uuid.New() + + e.serverProto = &pb.Server{ + Host: host, + Port: int32(port), + ServerId: int32(serverID), + ApiPort: int32(apiPort), + Token: token[:], + } + + e.idGenerator, err = newIDGenerator() + if err != nil { + return err + } + + if err := e.store.RegisterServer(e.serverProto); err != nil { return err } - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(ctx) defer cancel() go func(ctx context.Context) { - serverTicker := time.NewTicker(s.ServerUpdateDuration) - roomTicker := time.NewTicker(s.RoomUpdateDuration) + serverTicker := time.NewTicker(e.ServerUpdateDuration) + roomTicker := time.NewTicker(e.RoomUpdateDuration) + defer serverTicker.Stop() + defer roomTicker.Stop() for { select { case <-serverTicker.C: - if err := s.store.RegisterServer(s.serverProto); err != nil { - s.logger.Println(err) + if err := e.store.RegisterServer(e.serverProto); err != nil { + e.logger.Println(err) } case <-roomTicker.C: - s.rooms.Range(func(_, value interface{}) bool { + e.rooms.Range(func(_, value interface{}) bool { room, ok := value.(*Room) if !ok { return true @@ -124,8 +142,8 @@ func (s *RoomServer) Run(roomListener net.Listener, apiPort int) error { if !room.creatorConnected { return true } - if err := s.store.RegisterRoom(room.roomProto); err != nil { - s.logger.Println(err) + if err := e.store.RegisterRoom(room.roomProto); err != nil { + e.logger.Println(err) } return true }) @@ -136,23 +154,29 @@ func (s *RoomServer) Run(roomListener net.Listener, apiPort int) error { }(ctx) for { - conn, err := roomListener.Accept() + sess, err := listener.Accept(ctx) if err != nil { - s.logger.Println(err) + e.logger.Println(err) continue } - if err := s.Serve(conn); err != nil { - s.logger.Println(err) - } + conn := &quicConn{sess: sess} + go func() { + if err := e.serve(ctx, conn); err != nil { + e.logger.Println(err) + } + }() } } -// Serve handles requests from the peer. -func (s *RoomServer) Serve(conn io.ReadWriteCloser) error { - client := &Client{conn: conn} +func (e *Engine) serve(ctx context.Context, conn *quicConn) error { + stream, err := conn.AcceptStream() + if err != nil { + return err + } + buf := make([]byte, maxMessageSize) - n, err := client.read(buf) + n, err := stream.Read(buf) if err != nil { return err } @@ -162,7 +186,7 @@ func (s *RoomServer) Serve(conn io.ReadWriteCloser) error { } roomID := int(binary.LittleEndian.Uint32(buf[:4])) - r, ok := s.rooms.Load(roomID) + r, ok := e.rooms.Load(roomID) if !ok { return fmt.Errorf("the room does not exist %v", roomID) } @@ -176,7 +200,7 @@ func (s *RoomServer) Serve(conn io.ReadWriteCloser) error { return fmt.Errorf("connected clients exceed room capacity %v %v", room.config.MaxUser, room.clientManager.count) } - n, err = client.read(buf) + n, err = stream.Read(buf) if err != nil { return err } @@ -186,7 +210,7 @@ func (s *RoomServer) Serve(conn io.ReadWriteCloser) error { return fmt.Errorf("invalid application name %v %v", applicationName, room.config.ApplicationName) } - n, err = client.read(buf) + n, err = stream.Read(buf) if err != nil { return err } @@ -196,18 +220,18 @@ func (s *RoomServer) Serve(conn io.ReadWriteCloser) error { return fmt.Errorf("invalid version %v %v", version, room.config.Version) } - n, err = client.read(buf) + n, err = stream.Read(buf) if err != nil { return err } password := string(buf[:n]) if room.config.Password != "" && password != room.config.Password { - return fmt.Errorf("invalid password %v %v", password, room.config.Password) + return fmt.Errorf("invalid password") } if !room.creatorConnected { - n, err := client.read(buf) + n, err = stream.Read(buf) if err != nil { return err } @@ -218,30 +242,30 @@ func (s *RoomServer) Serve(conn io.ReadWriteCloser) error { room.roomProto.ConnectedUser = 1 - if err := s.store.RegisterRoom(room.roomProto); err != nil { + if err := e.store.RegisterRoom(room.roomProto); err != nil { return err } room.creatorConnected = true } - return room.serve(conn) + return room.serve(ctx, conn) } -var errInvalidToken = fmt.Errorf("invalid room server api token") +var errInvalidToken = fmt.Errorf("invalid room engine api token") // CreateRoom creates new room. -func (s *RoomServer) CreateRoom(ctx context.Context, request *pb.CreateRoomRequest) (*pb.CreateRoomResponse, error) { - if !bytes.Equal(request.ServerToken, s.serverProto.Token) { +func (e *Engine) CreateRoom(ctx context.Context, request *pb.CreateRoomRequest) (*pb.CreateRoomResponse, error) { + if !bytes.Equal(request.ServerToken, e.serverProto.Token) { return nil, errInvalidToken } - roomID, err := s.idGenerator.Generate() + roomID, err := e.idGenerator.generate() if err != nil { return nil, err } - roomID |= s.serverID + roomID |= e.serverID config := &RoomConfig{ RoomID: roomID, @@ -253,25 +277,25 @@ func (s *RoomServer) CreateRoom(ctx context.Context, request *pb.CreateRoomReque Info: request.Information, } - r, err := newRoom(s, config) + r, err := newRoom(e, config) if err != nil { return nil, err } - service, err := s.factory.Create(r) + service, err := e.factory.Create(r) if err != nil { return nil, err } r.service = service - s.rooms.Store(roomID, r) + e.rooms.Store(roomID, r) r.roomProto = &pb.Room{ RoomId: int32(roomID), RequirePassword: request.Password != "", MaxUser: request.MaxUser, ConnectedUser: 0, - Server: s.serverProto, + Server: e.serverProto, ApplicationName: request.ApplicationName, Version: request.Version, Information: request.Information, diff --git a/iguagile/gameobject.go b/iguagile/gameobject.go deleted file mode 100644 index 212d2c1..0000000 --- a/iguagile/gameobject.go +++ /dev/null @@ -1,79 +0,0 @@ -package iguagile - -import ( - "fmt" - "sync" -) - -// GameObject is object to be synchronized. -type GameObject struct { - id int - owner *Client - lifetime byte - resourcePath []byte -} - -// lifetime -const ( - roomExist = iota - ownerExist -) - -// GameObjectManager manages GameObjects. -type GameObjectManager struct { - gameObjects map[int]*GameObject - *sync.Mutex -} - -// NewGameObjectManager is GameObjectManager constructed. -func NewGameObjectManager() *GameObjectManager { - return &GameObjectManager{ - gameObjects: make(map[int]*GameObject), - Mutex: &sync.Mutex{}, - } -} - -// Get GameObject. -func (m *GameObjectManager) Get(objectID int) (*GameObject, error) { - gameObject, ok := m.gameObjects[objectID] - if !ok { - return nil, fmt.Errorf("object not exists %v", objectID) - } - - return gameObject, nil -} - -// Add GameObject. -func (m *GameObjectManager) Add(gameObject *GameObject) error { - if _, ok := m.gameObjects[gameObject.id]; ok { - return fmt.Errorf("object exist %v", gameObject.id) - } - - m.gameObjects[gameObject.id] = gameObject - return nil -} - -// Remove GameObject. -func (m *GameObjectManager) Remove(objectID int) { - if _, ok := m.gameObjects[objectID]; !ok { - return - } - - delete(m.gameObjects, objectID) -} - -// Exist checks the GameObject exists. -func (m *GameObjectManager) Exist(objectID int) bool { - _, ok := m.gameObjects[objectID] - return ok -} - -// GetAllGameObjects returns all GameObjects. -func (m *GameObjectManager) GetAllGameObjects() map[int]*GameObject { - return m.gameObjects -} - -// Clear all GameObjects. -func (m *GameObjectManager) Clear() { - m.gameObjects = make(map[int]*GameObject) -} diff --git a/iguagile/id.go b/iguagile/id.go index ed806b3..db7cec8 100644 --- a/iguagile/id.go +++ b/iguagile/id.go @@ -6,13 +6,13 @@ import ( "github.com/minami14/idgo" ) -// IDGenerator generates id. -type IDGenerator struct { +// idGenerator generates id. +type idGenerator struct { generator *idgo.IDGenerator } -// NewIDGenerator is IDGenerator constructed. -func NewIDGenerator() (*IDGenerator, error) { +// newIDGenerator is idGenerator constructed. +func newIDGenerator() (*idGenerator, error) { store, err := idgo.NewLocalStore(math.MaxInt16) if err != nil { return nil, err @@ -23,15 +23,15 @@ func NewIDGenerator() (*IDGenerator, error) { return nil, err } - return &IDGenerator{generator}, nil + return &idGenerator{generator}, nil } -// Generate generates a id. -func (g *IDGenerator) Generate() (int, error) { +// generate generates a id. +func (g *idGenerator) generate() (int, error) { return g.generator.Generate() } -// Free deallocates the id. -func (g *IDGenerator) Free(id int) error { +// free deallocates the id. +func (g *idGenerator) free(id int) error { return g.generator.Free(id) } diff --git a/iguagile/relay_test.go b/iguagile/relay_test.go index 6030362..dd12e04 100644 --- a/iguagile/relay_test.go +++ b/iguagile/relay_test.go @@ -2,16 +2,24 @@ package iguagile import ( "bytes" + "context" + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" "encoding/binary" + "encoding/pem" "io" - "net" + "math/big" "os" "testing" + + "github.com/lucas-clemente/quic-go" ) const ( - address = "localhost:12345" - grpcPort = 11111 + address = "localhost:8080" + apiAddr = "localhost:8081" serverID = 1 << 16 roomID = 1 | serverID @@ -21,26 +29,11 @@ const ( ) var ( - roomServer *RoomServer - roomToken = []byte{1} - testData = []byte("test data") + engine *Engine + roomToken = []byte{1} + testData = []byte("test data") ) -func setupServer() error { - factory := &RelayServiceFactory{} - store, err := NewRedis(os.Getenv("REDIS_HOST")) - if err != nil { - return err - } - - roomServer, err = NewRoomServer(factory, store, address) - if err != nil { - return err - } - - return nil -} - func createRoom() (*Room, error) { conf := &RoomConfig{ RoomID: roomID, @@ -51,32 +44,37 @@ func createRoom() (*Room, error) { Token: roomToken, } - room, err := newRoom(roomServer, conf) + room, err := newRoom(engine, conf) if err != nil { return nil, err } - service, err := roomServer.factory.Create(room) + service, err := engine.factory.Create(room) if err != nil { return nil, err } room.service = service - roomServer.rooms.Store(roomID, room) + engine.rooms.Store(roomID, room) return room, nil } func startServer() error { - if err := setupServer(); err != nil { + factory := new(RelayServiceFactory) + store, err := NewRedis(os.Getenv("REDIS_HOST")) + if err != nil { return err } - listener, err := net.Listen("tcp", address) + engine = New(factory, store) + + tlsConf, err := generateTLSConfig() if err != nil { return err } + go func() { - _ = roomServer.Run(listener, grpcPort) + _ = engine.Start(context.Background(), address, apiAddr, tlsConf) }() return nil @@ -121,21 +119,44 @@ func TestRelayService(t *testing.T) { t.Fatal(err) } - conn, err := net.Dial("tcp", address) + tlsConf := &tls.Config{ + NextProtos: []string{"iguagile-test"}, + InsecureSkipVerify: true, + } + + sess, err := quic.DialAddr(address, tlsConf, nil) if err != nil { t.Fatal(err) } - if err := verify(conn); err != nil { + stream, err := sess.OpenStream() + + if err := verify(stream); err != nil { t.Fatal(err) } + _ = stream.Close() - if err := send(conn, testData); err != nil { + stream, err = sess.AcceptStream(context.Background()) + if err != nil { t.Fatal(err) } buf := make([]byte, maxMessageSize) - n, err := receive(conn, buf) + + n, err := receive(stream, buf) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(buf[:n], []byte("Urelay")) { + t.Errorf("invalid stream name %v", string(buf[:n])) + } + + if err := send(stream, testData); err != nil { + t.Fatal(err) + } + + n, err = receive(stream, buf) if err != nil { t.Fatal(err) } @@ -146,3 +167,30 @@ func TestRelayService(t *testing.T) { t.Logf("%v, %v", buf[:n], testData) } + +func generateTLSConfig() (*tls.Config, error) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + if err != nil { + return nil, err + } + + template := x509.Certificate{SerialNumber: big.NewInt(1)} + certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key) + if err != nil { + return nil, err + } + + keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}) + certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) + + tlsCert, err := tls.X509KeyPair(certPEM, keyPEM) + if err != nil { + return nil, err + } + + return &tls.Config{ + Certificates: []tls.Certificate{tlsCert}, + NextProtos: []string{"iguagile-test"}, + InsecureSkipVerify: true, + }, nil +} diff --git a/iguagile/room.go b/iguagile/room.go index 278a856..d95c01f 100644 --- a/iguagile/room.go +++ b/iguagile/room.go @@ -1,7 +1,9 @@ package iguagile import ( - "io" + "context" + "errors" + "fmt" "log" "math" "os" @@ -13,16 +15,17 @@ import ( // clients. type Room struct { clientManager *ClientManager - rpcBufferManager *RPCBufferManager - generator *IDGenerator + generator *idGenerator log *log.Logger host *Client config *RoomConfig creatorConnected bool roomProto *pb.Room store Store - server *RoomServer + engine *Engine service RoomService + streams map[string]*Stream + ready bool } // RoomConfig is room config. @@ -36,25 +39,25 @@ type RoomConfig struct { Token []byte } -func newRoom(server *RoomServer, config *RoomConfig) (*Room, error) { - gen, err := NewIDGenerator() +func newRoom(engine *Engine, config *RoomConfig) (*Room, error) { + gen, err := newIDGenerator() if err != nil { return nil, err } return &Room{ - clientManager: NewClientManager(), - rpcBufferManager: NewRPCBufferManager(), - generator: gen, - log: log.New(os.Stdout, "iguagile-engine ", log.Lshortfile), - config: config, - store: server.store, - roomProto: &pb.Room{}, - server: server, + clientManager: NewClientManager(), + generator: gen, + log: log.New(os.Stdout, "iguagile-engine ", log.Lshortfile), + config: config, + store: engine.store, + roomProto: &pb.Room{}, + engine: engine, + streams: map[string]*Stream{}, }, nil } -func (r *Room) serve(conn io.ReadWriteCloser) error { +func (r *Room) serve(ctx context.Context, conn *quicConn) error { client, err := NewClient(r, conn) if err != nil { return err @@ -64,7 +67,7 @@ func (r *Room) serve(conn io.ReadWriteCloser) error { if err := r.store.RegisterRoom(r.roomProto); err != nil { r.log.Println(err) } - return r.register(client) + return r.register(ctx, client) } const ( @@ -73,24 +76,37 @@ const ( ) // register requests from the clients. -func (r *Room) register(client *Client) error { +func (r *Room) register(ctx context.Context, client *Client) error { if err := r.clientManager.Add(client); err != nil { return err } - go client.writeStart() if r.clientManager.Count() == 1 { r.host = client } - go client.readStart() + for name, s := range r.streams { + stream, err := client.conn.OpenStream() + if err != nil { + return err + } + + if _, err := stream.Write([]byte(name)); err != nil { + return err + } + + s.streams[client.id] = stream + client.streams[name] = stream + } + + client.readStart(ctx) return r.service.OnRegisterClient(client.id) } // unregister requests from clients. func (r *Room) unregister(client *Client) error { - if err := r.generator.Free(client.GetID()); err != nil { + if err := r.generator.free(client.GetID()); err != nil { r.log.Println(err) } @@ -106,40 +122,27 @@ func (r *Room) unregister(client *Client) error { return r.service.OnUnregisterClient(client.id) } -// SendToHost sends outbound message to the host. -func (r *Room) SendToHost(senderID int, message []byte) { - r.host.Send(message) -} - -// SendToClient sends outbound message to the client. -func (r *Room) SendToClient(targetID, senderID int, message []byte) { - client, err := r.clientManager.Get(targetID) - if err != nil { - r.log.Println(err) - return +// CreateStream creates new stream. +// Call in the Create method implemented in RoomServiceFactory. +func (r *Room) CreateStream(streamName string) (*Stream, error) { + if r.ready { + return nil, errors.New("call CreateStream in the Create method implemented in RoomServiceFactory") } - client.Send(message) -} + streamName = userStreamPrefix + streamName -// SendToAllClients sends outbound message to all registered clients. -func (r *Room) SendToAllClients(senderID int, message []byte) { - r.clientManager.Lock() - defer r.clientManager.Unlock() - for _, client := range r.clientManager.GetAllClients() { - client.Send(message) + if _, ok := r.streams[streamName]; ok { + return nil, fmt.Errorf("%v has already been created", streamName) } -} -// SendToOtherClients sends outbound message to other registered clients. -func (r *Room) SendToOtherClients(senderID int, message []byte) { - r.clientManager.Lock() - defer r.clientManager.Unlock() - for id, client := range r.clientManager.GetAllClients() { - if id != senderID { - client.Send(message) - } + stream := &Stream{ + r: r, + streams: map[int]*quicStream{}, } + + r.streams[streamName] = stream + + return stream, nil } // CloseConnection closes the connection and unregisters the client. @@ -162,6 +165,6 @@ func (r *Room) Close() error { } } - r.server.rooms.Delete(r.config.RoomID) + r.engine.rooms.Delete(r.config.RoomID) return r.service.Destroy() } diff --git a/iguagile/rpc-buffer.go b/iguagile/rpc-buffer.go deleted file mode 100644 index 94d3000..0000000 --- a/iguagile/rpc-buffer.go +++ /dev/null @@ -1,53 +0,0 @@ -package iguagile - -import ( - "sync" -) - -// RPCBufferManager manages buffered rpc messages. -type RPCBufferManager struct { - buffer map[*[]byte]*Client - sync.Mutex -} - -// NewRPCBufferManager is RPCBufferManger constructed. -func NewRPCBufferManager() *RPCBufferManager { - return &RPCBufferManager{ - buffer: make(map[*[]byte]*Client), - Mutex: sync.Mutex{}, - } -} - -// Add new rpc message. -func (m *RPCBufferManager) Add(message []byte, sender *Client) { - m.Lock() - m.buffer[&message] = sender - m.Unlock() -} - -// Remove rpc messages. -func (m *RPCBufferManager) Remove(client *Client) { - m.Lock() - for buffer, sender := range m.buffer { - if sender == client { - delete(m.buffer, buffer) - } - } - m.Unlock() -} - -// Clear all rpc messages. -func (m *RPCBufferManager) Clear() { - m.Lock() - m.buffer = make(map[*[]byte]*Client) - m.Unlock() -} - -// SendRPCBuffer sends all buffered rpc messages. -func (m *RPCBufferManager) SendRPCBuffer(client *Client) { - m.Lock() - for buffer := range m.buffer { - client.Send(*buffer) - } - m.Unlock() -} diff --git a/iguagile/service.go b/iguagile/service.go index 0b6c445..e6202cf 100644 --- a/iguagile/service.go +++ b/iguagile/service.go @@ -1,9 +1,13 @@ package iguagile +// ReceiveFunc processes data send from the client to the engine. +type ReceiveFunc func(senderID int, data []byte) error + // RoomService implements the processing performed by the room type RoomService interface { - // Receive processes data sent from the client to the server. - Receive(senderID int, data []byte) error + // ReceiveFunc returns function processes data sent from the client to the engine. + // This method is called once every time the client connects. + ReceiveFunc(streamName string) (ReceiveFunc, error) // OnRegisterClient is called when the client connects to the room. OnRegisterClient(clientID int) error @@ -26,13 +30,16 @@ type RoomServiceFactory interface { // RelayService is a service relays data. type RelayService struct { - room *Room + room *Room + stream *Stream } -// Receive receives data and sends to all clients. -func (s *RelayService) Receive(senderID int, data []byte) error { - s.room.SendToAllClients(senderID, data) - return nil +// ReceiveFunc is function receives data and sends to all clients. +func (s *RelayService) ReceiveFunc(_ string) (ReceiveFunc, error) { + return func(senderID int, data []byte) error { + s.stream.SendToAllClients(data) + return nil + }, nil } // OnRegisterClient for implement RoomService. @@ -60,5 +67,13 @@ type RelayServiceFactory struct{} // Create creates a EmptyRoomService. func (f RelayServiceFactory) Create(room *Room) (RoomService, error) { - return &RelayService{room: room}, nil + stream, err := room.CreateStream("relay") + if err != nil { + return nil, err + } + + return &RelayService{ + room: room, + stream: stream, + }, nil } diff --git a/iguagile/stream.go b/iguagile/stream.go new file mode 100644 index 0000000..d21335f --- /dev/null +++ b/iguagile/stream.go @@ -0,0 +1,41 @@ +package iguagile + +// Stream is a collection of streams with the same name that all clients have. +type Stream struct { + r *Room + streams map[int]*quicStream +} + +// SendToAllClients sends message to all clients through the stream. +func (s *Stream) SendToAllClients(message []byte) { + for _, stream := range s.streams { + if _, err := stream.Write(message); err != nil { + s.r.log.Println(err) + } + } +} + +// SendToOtherClients sends message to all clients except the sender through the stream. +func (s *Stream) SendToOtherClients(senderID int, message []byte) { + for id, stream := range s.streams { + if id == senderID { + continue + } + + if _, err := stream.Write(message); err != nil { + s.r.log.Println(err) + } + } +} + +// SendToClient sends message to target client through the stream. +func (s *Stream) SendToClient(targetID int, message []byte) { + stream, ok := s.streams[targetID] + if !ok { + return + } + + if _, err := stream.Write(message); err != nil { + s.r.log.Println(err) + } +} diff --git a/src/id.rs b/src/id.rs new file mode 100644 index 0000000..93606b7 --- /dev/null +++ b/src/id.rs @@ -0,0 +1,125 @@ +use std::collections::HashSet; + +pub trait IdPool { + fn is_empty(&self) -> bool; + fn len(&self) -> usize; + fn clear(&mut self); + fn get_id(&mut self) -> Option; + fn return_id(&mut self, id: u16) -> bool; +} + +#[derive(Clone, Debug)] +pub struct MemoryIdPool { + available_ids: HashSet, +} + +impl MemoryIdPool { + pub fn new() -> Self { + let available_ids: HashSet = (0..=u16::MAX).collect(); + Self { available_ids } + } +} + +impl IdPool for MemoryIdPool { + fn is_empty(&self) -> bool { + self.available_ids.is_empty() + } + + fn len(&self) -> usize { + self.available_ids.len() + } + + fn clear(&mut self) { + self.available_ids.clear(); + for i in 0..=u16::MAX { + self.available_ids.insert(i); + } + } + + fn get_id(&mut self) -> Option { + let id = self.available_ids.iter().next().cloned(); + if let Some(id) = id { + self.available_ids.remove(&id); + } + id + } + + fn return_id(&mut self, id: u16) -> bool { + self.available_ids.insert(id) + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + + #[test] + fn memory_id_pool_new() { + let pool = MemoryIdPool::new(); + assert_eq!(pool.available_ids.len(), 65536); + for i in 0..=65535 { + assert!(pool.available_ids.contains(&i)); + } + } + + #[test] + fn memory_id_pool_is_empty() { + let pool = MemoryIdPool { + available_ids: [0].into(), + }; + assert!(!pool.is_empty()); + let pool = MemoryIdPool { + available_ids: Default::default(), + }; + assert!(pool.is_empty()); + } + + #[test] + fn memory_id_pool_len() { + let mut pool = MemoryIdPool::new(); + assert_eq!(pool.len(), 65536); + assert!(pool.get_id().is_some()); + assert_eq!(pool.len(), 65535); + assert!(pool.get_id().is_some()); + assert_eq!(pool.len(), 65534); + pool.available_ids.clear(); + assert_eq!(pool.len(), 0); + } + + #[test] + fn memory_id_pool_clear() { + let mut pool = MemoryIdPool { + available_ids: Default::default(), + }; + assert_eq!(pool.available_ids.len(), 0); + pool.clear(); + assert_eq!(pool.available_ids.len(), 65536); + } + + #[test] + fn memory_id_pool_get_id() { + let mut pool = MemoryIdPool::new(); + for _ in 0..=u16::MAX { + let id = pool.get_id().unwrap(); + assert!(!pool.available_ids.contains(&id)); + } + assert!(pool.get_id().is_none()); + } + + #[test] + fn memory_id_pool_return_id() { + let mut pool = MemoryIdPool { + available_ids: Default::default(), + }; + for id in 0..=u16::MAX { + assert!(!pool.available_ids.contains(&id)); + assert!(pool.return_id(id)); + assert!(pool.available_ids.contains(&id)); + } + for id in 0..=u16::MAX { + assert!(pool.available_ids.contains(&id)); + assert!(!pool.return_id(id)); + assert!(pool.available_ids.contains(&id)); + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..ede5eea --- /dev/null +++ b/src/main.rs @@ -0,0 +1,15 @@ +use crate::store::MemoryStore; + +mod id; +mod store; + +fn main() { + let redis = redis::Client::open("redis://127.0.0.1"); + if let Err(e) = redis { + println!("redis error: {}", e); + return; + } + let m = MemoryStore::new(redis.unwrap()); + let _ = m; + println!("Hello, world!"); +} diff --git a/src/room.proto b/src/room.proto new file mode 100644 index 0000000..a12159b --- /dev/null +++ b/src/room.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; + +package iguagile; + +service RoomService { + rpc CreateRoom (CreateRoomRequest) returns (CreateRoomResponse); +} + +message CreateRoomRequest { + string application_name = 1; + string version = 2; + string password = 3; + int32 max_user = 4; + bytes room_token = 5; + bytes server_token = 6; + map information = 7; +} + +message CreateRoomResponse { + Room room = 1; +} + +message Room { + int32 room_id = 1; + bool require_password = 2; + int32 max_user = 3; + int32 connected_user = 4; + Server server = 5; + string application_name = 6; + string version = 7; + map information = 8; +} + +message Server { + string host = 1; + int32 port = 2; + int32 server_id = 3; + bytes token = 4; + int32 api_port = 5; +} diff --git a/src/store.rs b/src/store.rs new file mode 100644 index 0000000..cf4469e --- /dev/null +++ b/src/store.rs @@ -0,0 +1,187 @@ +use iguagile::{Room, Server}; +use serde_json::json; + +pub mod iguagile { + tonic::include_proto!("iguagile"); +} + +const REGISTER_SERVER: &str = "server_register"; +const UNREGISTER_SERVER: &str = "server_unregister"; +const REGISTER_ROOM: &str = "room_register"; +const UNREGISTER_ROOM: &str = "room_unregister"; + +trait Store { + fn generate_server_id(&self) -> Result; + fn register_server(&self, s: Server) -> Result<(), anyhow::Error>; + fn unregister_server(&self, s: Server) -> Result<(), anyhow::Error>; + fn register_room(&self, r: Room) -> Result<(), anyhow::Error>; + fn unregister_room(&self, r: Room) -> Result<(), anyhow::Error>; +} + +pub struct MemoryStore { + redis: redis::Client, +} + +impl MemoryStore { + pub fn new(c: redis::Client) -> Self { + MemoryStore { redis: c } + } +} + +impl Store for MemoryStore { + fn generate_server_id(&self) -> Result { + let con = self.redis.get_connection(); + if let Err(e) = con { + return Err(anyhow::anyhow!(e)); + } + + let id = redis::cmd("incr") + .arg("server_id") + .query::(&mut con.unwrap()) + .unwrap(); + Ok(id) + } + fn register_server(&self, s: Server) -> Result<(), anyhow::Error> { + let con = self.redis.get_connection(); + if let Err(e) = con { + return Err(anyhow::anyhow!(e)); + } + let mut con = con.unwrap(); + + let payload = json!({ + "host": s.host, + "port": s.port, + "server_id": s.server_id, + "token" : s.token, + "api_port": s.api_port, + }); + let result = redis::cmd("publish") + .arg(REGISTER_SERVER) + .arg(payload.to_string()) + .query::(&mut con); + if let Err(e) = result { + return Err(anyhow::anyhow!(e)); + } + Ok(()) + } + fn unregister_server(&self, s: Server) -> Result<(), anyhow::Error> { + let con = self.redis.get_connection(); + if let Err(e) = con { + return Err(anyhow::anyhow!(e)); + } + let mut con = con.unwrap(); + + let payload = json!({ + "host": s.host, + "port": s.port, + "server_id": s.server_id, + "token" : s.token, + "api_port": s.api_port, + }); + let result = redis::cmd("publish") + .arg(UNREGISTER_SERVER) + .arg(payload.to_string()) + .query::(&mut con); + if let Err(e) = result { + return Err(anyhow::anyhow!(e)); + } + Ok(()) + } + fn register_room(&self, r: Room) -> Result<(), anyhow::Error> { + let con = self.redis.get_connection(); + if let Err(e) = con { + return Err(anyhow::anyhow!(e)); + } + let mut con = con.unwrap(); + + if r.server.is_none() { + return Err(anyhow::anyhow!("server is none")); + } + + let server = r.server.unwrap(); + let payload = json!({ + "room_id": r.room_id, + "require_password": r.require_password, + "max_user": r.max_user, + "connected_user": r.connected_user, + "server": { + "host": server.host, + "port": server.port, + "server_id": server.server_id, + "token" : server.token, + "api_port": server.api_port, + }, + "application_name": r.application_name, + "version": r.version, + "information": r.information, + }); + let result = redis::cmd("publish") + .arg(REGISTER_ROOM) + .arg(payload.to_string()) + .query::(&mut con); + if let Err(e) = result { + return Err(anyhow::anyhow!(e)); + } + Ok(()) + } + fn unregister_room(&self, r: Room) -> Result<(), anyhow::Error> { + let con = self.redis.get_connection(); + if let Err(e) = con { + return Err(anyhow::anyhow!(e)); + } + let mut con = con.unwrap(); + + if r.server.is_none() { + return Err(anyhow::anyhow!("server is none")); + } + + let server = r.server.unwrap(); + let payload = json!({ + "room_id": r.room_id, + "require_password": r.require_password, + "max_user": r.max_user, + "connected_user": r.connected_user, + "server": { + "host": server.host, + "port": server.port, + "server_id": server.server_id, + "token" : server.token, + "api_port": server.api_port, + }, + "application_name": r.application_name, + "version": r.version, + "information": r.information, + }); + let result = redis::cmd("publish") + .arg(UNREGISTER_ROOM) + .arg(payload.to_string()) + .query::(&mut con); + if let Err(e) = result { + return Err(anyhow::anyhow!(e)); + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_generate_server_id() { + let client = redis::Client::open("redis://127.0.0.1/").unwrap(); + let mut con = client.get_connection().unwrap(); + + redis::cmd("del") + .arg("server_id") + .query::(&mut con) + .unwrap(); + + let m = MemoryStore::new(client); + let id = m.generate_server_id().unwrap(); + assert_eq!(id, 1); + let id = m.generate_server_id().unwrap(); + assert_eq!(id, 2); + } +}