[
  {
    "path": ".devcontainer/Dockerfile",
    "content": "FROM mcr.microsoft.com/vscode/devcontainers/rust:0-1\n\n# Install socat needed for TCP proxy\nRUN apt update && apt install -y socat\n\nCOPY ./ci/cert/ca.crt /usr/local/share/ca-certificates/\n\nRUN update-ca-certificates\n"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:\n// https://github.com/microsoft/vscode-dev-containers/tree/v0.187.0/containers/rust\n{\n    \"name\": \"Rust\",\n    \"dockerComposeFile\": [\n        \"./docker-compose.yaml\"\n    ],\n    \"service\": \"rust-client\",\n    \"workspaceFolder\": \"/workspace/rust-socketio\",\n    \"shutdownAction\": \"stopCompose\",\n    \"customizations\": {\n        \"vscode\": {\n            // Set *default* container specific settings.json values on container create.\n            \"settings\": {\n                \"lldb.executable\": \"/usr/bin/lldb\",\n                // VS Code don't watch files under ./target\n                \"files.watcherExclude\": {\n                    \"**/target/**\": true\n                },\n                \"rust-analyzer.cargo.features\": [\n                    \"async\"\n                ]\n                /*,\n                // If you prefer rust-analzyer to be less noisy consider these settings to your settings.json\n                \"editor.semanticTokenColorCustomizations\": {\n                    \"rules\": {\n                        \"*.mutable\": {\n                            \"underline\": false\n                        }\n                    }\n                },\n                \"rust-analyzer.inlayHints.parameterHints\": false\n                */\n            },\n            // Add the IDs of extensions you want installed when the container is created.\n            \"extensions\": [\n                \"rust-lang.rust-analyzer\",\n                \"bungcip.better-toml\",\n                \"vadimcn.vscode-lldb\",\n                \"eamodio.gitlens\",\n                \"streetsidesoftware.code-spell-checker\"\n            ]\n        }\n    },\n    \"remoteUser\": \"vscode\",\n    // Start a TCP proxy from to the testing node-socket-io server so doc tests can pass.\n    \"postAttachCommand\": {\n        \"SocketIOProxy\": \"socat TCP-LISTEN:4200,fork,reuseaddr TCP:node-socket-io:4200\",\n        \"EngineIOProxy\": \"socat TCP-LISTEN:4201,fork,reuseaddr TCP:node-engine-io:4201\",\n        \"SocketIOAuthProxy\": \"socat TCP-LISTEN:4204,fork,reuseaddr TCP:node-socket-io-auth:4204\"\n    }\n}\n"
  },
  {
    "path": ".devcontainer/docker-compose.yaml",
    "content": "version: '3'\nservices:\n    node-engine-io-secure:\n        build:\n            context: ../ci\n        command: [ \"node\", \"/test/engine-io-secure.js\" ]\n        ports:\n            - \"4202:4202\"\n        environment:\n            - \"DEBUG=*\"\n    node-engine-io:\n        build:\n            context: ../ci\n        command: [ \"node\", \"/test/engine-io.js\" ]\n        ports:\n            - \"4201:4201\"\n        environment:\n            - \"DEBUG=*\"\n    node-engine-io-polling:\n        build:\n            context: ../ci\n        command: [ \"node\", \"/test/engine-io-polling.js\" ]\n        ports:\n            - \"4203:4203\"\n        environment:\n            - \"DEBUG=*\"\n    node-socket-io:\n        build:\n            context: ../ci\n        command: [ \"node\", \"/test/socket-io.js\" ]\n        ports:\n            - \"4200:4200\"\n        environment:\n            - \"DEBUG=*\"\n    node-socket-io-auth:\n        build:\n            context: ../ci\n        command: [ \"node\", \"/test/socket-io-auth.js\" ]\n        ports:\n            - \"4204:4204\"\n        environment:\n            - \"DEBUG=*\"\n    node-socket-restart:\n        build:\n            context: ../ci\n        command: [ \"node\", \"/test/socket-io-restart.js\" ]\n        ports:\n            - \"4205:4205\"\n        environment:\n            - \"DEBUG=*\"\n    node-socket-restart-url-auth:\n        build:\n            context: ../ci\n        command: [ \"node\", \"/test/socket-io-restart-url-auth.js\" ]\n        ports:\n            - \"4206:4206\"\n        environment:\n            - \"DEBUG=*\"\n    rust-client:\n        build: \n            context: .. \n            dockerfile: ./.devcontainer/Dockerfile\n        command: /bin/sh -c \"while sleep 10000d; do :; done\"\n        security_opt:\n            - seccomp:unconfined\n        volumes: \n            - \"..:/workspace/rust-socketio\"\n        environment:\n            - \"SOCKET_IO_SERVER=http://node-socket-io:4200\"\n            - \"SOCKET_IO_AUTH_SERVER=http://node-socket-io-auth:4204\"\n            - \"ENGINE_IO_SERVER=http://node-engine-io:4201\"\n            - \"ENGINE_IO_SECURE_SERVER=https://node-engine-io-secure:4202\"\n            - \"ENGINE_IO_SECURE_HOST=node-engine-io-secure\"\n            - \"ENGINE_IO_POLLING_SERVER=http://node-engine-io-polling:4203\"\n            - \"SOCKET_IO_RESTART_SERVER=http://node-socket-restart:4205\"\n            - \"SOCKET_IO_RESTART_URL_AUTH_SERVER=http://node-socket-restart-url-auth:4206\"\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"cargo\" # See documentation for possible values\n    directory: \"/\" # Location of package manifests\n    schedule:\n      interval: \"monthly\"\n    groups:\n      patches:\n        # Group cargo patch updates together to minimize PR management faff\n        applies-to: version-updates\n        update-types:\n        - patch\n"
  },
  {
    "path": ".github/workflows/benchmark.yml",
    "content": "on:\n   pull_request:\n     types: [opened]\n   issue_comment:\n     types: [created]\n\nname: benchmark engine.io\njobs:\n   runBenchmark:\n     name: run benchmark\n     runs-on: ubuntu-latest\n     steps:\n      - uses: khan/pull-request-comment-trigger@master\n        id: check\n        with:\n          trigger: '/benchmark'\n          reaction: rocket\n        env:\n          GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'\n      - name: Checkout repository\n        if: steps.check.outputs.triggered == 'true'\n        uses: actions/checkout@v2\n      - name: Setup rust environment \n        if: steps.check.outputs.triggered == 'true'\n        uses: actions-rs/toolchain@v1\n        with:\n             profile: minimal\n             toolchain: stable\n             override: true\n\n      - name: Setup docker\n        if: steps.check.outputs.triggered == 'true'\n        id: buildx\n        uses: docker/setup-buildx-action@v1\n\n      - name: Generate keys\n        if: steps.check.outputs.triggered == 'true'\n        run: make keys\n\n      - name: Build docker container\n        if: steps.check.outputs.triggered == 'true'\n        run: |\n          cd ci && docker build -t test_suite:latest .\n          docker run -d -p 4200:4200 -p 4201:4201 -p 4202:4202 -p 4203:4203 -p 4204:4204 -p 4205:4205 -p 4206:4206 test_suite:latest\n      - name: Extract branch name\n        if: steps.check.outputs.triggered == 'true'\n        shell: bash\n        run: echo \"##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})\"\n        id: extract_branch\n      - uses: actions/checkout@master\n        if: steps.check.outputs.triggered == 'true'\n      - uses: boa-dev/criterion-compare-action@v3.2.0\n        if: steps.check.outputs.triggered == 'true'\n        with:\n          cwd: \"engineio\"\n          branchName: ${{ steps.extract_branch.outputs.branch }}\n          token: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build and code style\n\non:\n  push:\n    branches: [main, refactoring]\n  pull_request:\n    branches: [main]\n\nenv:\n  CARGO_TERM_COLOR: always\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v2\n\n      - name: Setup rust environment\n        uses: actions-rs/toolchain@v1\n        with:\n          profile: minimal\n          toolchain: stable\n          override: true\n\n      - name: Generate Cargo.lock\n        run: cargo generate-lockfile\n\n      - uses: actions/cache@v2\n        with:\n          path: |\n            ~/.cargo/bin/\n            ~/.cargo/registry/index/\n            ~/.cargo/registry/cache/\n            ~/.cargo/git/db/\n          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}\n\n      - name: Build\n        run: cargo build --verbose --all-features\n      - name: Linting\n        run: cargo clippy --verbose --all-features\n      - name: Check formatting\n        run: cargo fmt --all -- --check\n"
  },
  {
    "path": ".github/workflows/coverage.yml",
    "content": "on:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\nname: generate coverage\n\njobs:\n  check:\n    name: Setup Rust project\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v2\n\n      - name: Setup rust environment\n        uses: actions-rs/toolchain@v1\n        with:\n          profile: minimal\n          toolchain: stable\n          override: true\n\n      - name: Install cargo-llvm-cov\n        uses: taiki-e/install-action@cargo-llvm-cov\n\n      - name: Setup docker\n        id: buildx\n        uses: docker/setup-buildx-action@v1\n\n      - name: Generate keys\n        run: make keys\n\n      - name: Build docker container\n        run: |\n          cd ci && docker build -t test_suite:latest .\n          docker run -d --name test_suite -p 4200:4200 -p 4201:4201 -p 4202:4202 -p 4203:4203 -p 4204:4204 -p 4205:4205 -p 4206:4206 test_suite:latest\n\n      - uses: actions/cache@v2\n        with:\n          path: |\n            ~/.cargo/bin/\n            ~/.cargo/registry/index/\n            ~/.cargo/registry/cache/\n            ~/.cargo/git/db/\n          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}\n\n      - name: Generate code coverage\n        run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info\n\n      - name: Upload to codecov.io\n        uses: codecov/codecov-action@v1.5.2\n        with:\n          token: ${{secrets.CODECOV_TOKEN}}\n          files: lcov.info\n          fail_ci_if_error: true\n\n      - name: Collect docker logs\n        if: always()\n        run: docker logs test_suite > my_logs.txt 2>&1\n\n      - name: Upload docker logs\n        uses: actions/upload-artifact@v4\n        if: always()\n        with:\n          name: docker logs\n          path: my_logs.txt\n"
  },
  {
    "path": ".github/workflows/publish-dry-run.yml",
    "content": "name: Publish dry run\n\non:\n  workflow_dispatch\n\njobs:\n  publish:\n    name: Publish dry run\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions-rs/toolchain@v1\n        with:\n            toolchain: stable\n            override: true\n      # Publish the engine.io crate\n      - uses: katyo/publish-crates@v1\n        with:\n            path: './engineio'\n            dry-run: true\n      # Publish the socket.io crate\n      - uses: katyo/publish-crates@v1\n        with:\n            path: './socketio'\n            dry-run: true\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish\n\non:\n  workflow_dispatch\n\njobs:\n  publish:\n    name: Publish\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions-rs/toolchain@v1\n        with:\n            toolchain: stable\n            override: true\n      # Publish the engine.io crate\n      - uses: katyo/publish-crates@v1\n        with:\n            path: './engineio'\n            registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}\n      # Publish the socket.io crate\n      - uses: katyo/publish-crates@v1\n        with:\n            path: './socketio'\n            registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Test\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main, refactoring]\n\nenv:\n  CARGO_TERM_COLOR: always\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: checkout\n        uses: actions/checkout@v2\n\n      - name: Setup rust environment  \n        uses: actions-rs/toolchain@v1\n        with:\n             profile: minimal\n             toolchain: stable\n             override: true\n\n      - name: Setup docker\n        id: buildx\n        uses: docker/setup-buildx-action@v1\n\n      - name: Generate keys\n        run: make keys\n\n      - name: Build docker container\n        run: |\n          cd ci && docker build -t test_suite:latest .\n          docker run -d -p 4200:4200 -p 4201:4201 -p 4202:4202 -p 4203:4203 -p 4204:4204 -p 4205:4205 -p 4206:4206 test_suite:latest\n\n      - name: Generate Cargo.lock\n        run: cargo generate-lockfile\n\n      - uses: actions/cache@v2\n        with:\n          path: |\n            ~/.cargo/bin/\n            ~/.cargo/registry/index/\n            ~/.cargo/registry/cache/\n            ~/.cargo/git/db/\n          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}\n\n      - name: Run testsuite\n        run: cargo test --verbose --features \"async\"\n"
  },
  {
    "path": ".gitignore",
    "content": "target\n.vscode\n.idea\nci/node_modules\nci/package-lock.json\nci/cert\n\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project are documented in this file.\n\nThe format is based on [Keep a Changelog], and this project adheres to\n[Semantic Versioning]. The file is auto-generated using [Conventional Commits].\n\n[keep a changelog]: https://keepachangelog.com/en/1.0.0/\n[semantic versioning]: https://semver.org/spec/v2.0.0.html\n[conventional commits]: https://www.conventionalcommits.org/en/v1.0.0-beta.4/\n\n## Overview\n\n* [unreleased](#unreleased)\n* [`0.5.0`](#060) - _2024.04.16_\n* [`0.5.0`](#050) - _2024.03.31_\n* [`0.4.4`](#044) - _2023.11.18_\n* [`0.4.3`](#043) - _2023.07.08_\n* [`0.4.2`](#042) - _2023.06.25_\n* [`0.4.1-alpha.2`](#041a2) - _2023.03.26_\n* [`0.4.1-alpha.1`](#041a1) - _2023.01.15_\n* [`0.4.0`](#041) - _2023.01.15_\n* [`0.4.0`](#040) - _2022.10.20_\n* [`0.3.1`](#031) - _2022.03.19_\n* [`0.3.0`](#030) - _2021.12.16_\n* [`0.3.0-alpha.2`](#030a3) - _2021.12.04_\n* [`0.3.0-alpha.2`](#030a2) - _2021.10.14_\n* [`0.3.0-alpha.1`](#030a1) - _2021.09.20_\n* [`0.2.4`](#024) - _2021.05.25_\n* [`0.2.3`](#023) - _2021.05.24_\n* [`0.2.2`](#022) - _2021.05.13\n* [`0.2.1`](#021) - _2021.04.27_\n* [`0.2.0`](#020) – _2021.03.13_\n* [`0.1.1`](#011) – _2021.01.10_\n* [`0.1.0`](#010) – _2021.01.05_\n\n## _[Unreleased]_\n\n_nothing new to show for… yet!_>\n\n## <a name=\"060\">[0.6.0] - _Multi-payload fix and http 1.0_ </a>\n\n_2024.04.16_\n\n- Fix issues with processing multi-payload messages ([#392](https://github.com/1c3t3a/rust-socketio/pull/392)).\n  Credits to shenjackyuanjie@.\n- Bump http to 1.0 and all dependencies that use http to a version that also uses http 1.0 ([#418](https://github.com/1c3t3a/rust-socketio/pull/418)).\n  Bumping those dependencies makes this a breaking change.\n\n## <a name=\"050\">[0.5.0] - _Packed with changes!_ </a>\n\n_2024.03.31_\n\n- Support multiple arguments to the payload through a new Payload variant called\n  `Text` that holds a JSON value ([#384](https://github.com/1c3t3a/rust-socketio/pull/384)).\n  Credits to ctrlaltf24@ and SalahaldinBilal@!\n  Please note: This is a breaking change: `Payload::String` is deprecated and will be removed soon.\n- Async reconnections: Support for automatic reconnection in the async version of the crate!\n  ([#400](https://github.com/1c3t3a/rust-socketio/pull/400)). Credits to rageshkrishna@.\n- Add an `on_reconnect` callback that allows to change the connection configuration\n  ([#405](https://github.com/1c3t3a/rust-socketio/pull/405)). Credits to rageshkrishna@.\n- Fix bug that ignored the ping interval ([#359](https://github.com/1c3t3a/rust-socketio/pull/359)).\n  Credits to sirkrypt0@. This is a breaking change that removes the engine.io's stream impl.\n  It is however replaced by a method called `as_stream` on the engine.io socket.\n- Add macro `async_callback` and `async_any_callback` for async callbacks ([#399](https://github.com/1c3t3a/rust-socketio/pull/399).\n  Credits to shenjackyuanjie@.\n\n## <a name=\"044\">[0.4.4] - _Bump dependencies_ </a>\n\n_2023.11.18_\n\n- Bump tungstenite version to v0.20.1 (avoiding security vulnerability) [#368](https://github.com/1c3t3a/rust-socketio/pull/368)\n- Updating other dependencies\n\n\n## <a name=\"043\">[0.4.3] - _Bugfix!_ </a>\n\n_2023.07.08_\n\n- Fix of [#323](https://github.com/1c3t3a/rust-socketio/issues/323)\n- Marking the async feature optional\n\n## <a name=\"042\">[0.4.2] - _Stabilizing the async interface!_ </a>\n\n_2023.06.25_\n\n- Fix \"Error while parsing an incomplete packet socketio\" on first heartbeat killing the connection async client\n([#311](https://github.com/1c3t3a/rust-socketio/issues/311)). Credits to [@sirkrypt0](https://github.com/sirkrypt0)\n- Fix allow awaiting async callbacks ([#313](https://github.com/1c3t3a/rust-socketio/issues/313)). Credits to [@felix-gohla](https://github.com/felix-gohla)\n- Various performance improvements especially in packet parsing. Credits to [@MaxOhn](https://github.com/MaxOhn)\n- API for setting the reconnect URL on a connected client ([#251](https://github.com/1c3t3a/rust-socketio/issues/251)).\nCredits to [@tyilo](https://github.com/tyilo)\n\n## <a name=\"041a2\">[0.4.0-alpha.2] - _Async socket.io fixes_ </a>\n\n_2023.03.26_\n\n- Add `on_any` method for async `ClientBuilder`. This adds the capability to\n  react to all incoming events (custom and otherwise).\n- Add `auth` option to async `ClientBuilder`. This allows for specifying JSON\n  data that is sent with the first open packet, which is commonly used for\n  authentication.\n- Bump dependencies and remove calls to deprecated library functions.\n\n## <a name=\"041a1\">[0.4.0-alpha.1] - _Async socket.io version_ </a>\n\n_2023.01.05_\n\n- Add an async socket.io interface under the `async` feature flag, relevant PR: [#180](https://github.com/1c3t3a/rust-socketio/pull/180).\n- See example code under `socketio/examples/async.rs` and in the `async` section of the README.\n\n## <a name=\"041\">[0.4.1] - _Minor enhancements_ </a>\n\n_2023.01.05_\n\n- As of [#264](https://github.com/1c3t3a/rust-socketio/pull/264), the callbacks\n  are now allowed to be `?Sync`.\n- As of [#265](https://github.com/1c3t3a/rust-socketio/pull/265), the `Payload`\n  type now implements `AsRef<u8>`.\n\n## <a name=\"040\">[0.4.0] - _Bugfixes and Reconnection feature_ </a>\n\n_2022.10.20_\n\n### Changes\n- Fix [#214](https://github.com/1c3t3a/rust-socketio/issues/214).\n- Fix [#215](https://github.com/1c3t3a/rust-socketio/issues/215).\n- Fix [#219](https://github.com/1c3t3a/rust-socketio/issues/219).\n- Fix [#221](https://github.com/1c3t3a/rust-socketio/issues/221).\n- Fix [#222](https://github.com/1c3t3a/rust-socketio/issues/222).\n- BREAKING: The default Client returned by the builder will automatically reconnect to the server unless stopped manually.\n  The new `ReconnectClient` encapsulates this behaviour.\n\nSpecial thanks to [@SSebo](https://github.com/SSebo) for his major contribution to this release.\n\n## <a name=\"031\">[0.3.1] - _Bugfix_ </a>\n\n_2022.03.19_\n\n### Changes\n- Fixes regarding [#166](https://github.com/1c3t3a/rust-socketio/issues/166).\n\n## <a name=\"030\">[0.3.0] - _Stabilize alpha version_ </a>\n\n_2021.12.16_\n\n### Changes\n- Stabilized alpha features.\n- Fixes regarding [#133](https://github.com/1c3t3a/rust-socketio/issues/133).\n\n## <a name=\"030a3\">[0.3.0-alpha.3] - _Bugfixes_ </a>\n\n_2021.12.04_\n\n### Changes\n\n- fix a bug that resulted in a blocking `emit` method (see [#133](https://github.com/1c3t3a/rust-socketio/issues/133)).\n- Bump dependencies.\n\n## <a name=\"030a2\">[0.3.0-alpha.2] - _Further refactoring_ </a>\n\n_2021.10.14_\n\n### Changes\n\n* Rename `Socket` to `Client` and `SocketBuilder` to `ClientBuilder`\n* Removed headermap from pub use, internal type only\n\n* Deprecations:\n    * crate::payload (use crate::Payload instead)\n    * crate::error (use crate::Error instead)\n    * crate::event (use crate::Event instead)\n\n## <a name=\"030a1\">[0.3.0-alpha.1] - _Refactoring_ </a>\n\n_2021.09.20_\n\n### Changes\n\n* Refactored Errors\n    * Renamed EmptyPacket to EmptyPacket()\n    * Renamed IncompletePacket to IncompletePacket()\n    * Renamed InvalidPacket to InvalidPacket()\n    * Renamed Utf8Error to InvalidUtf8()\n    * Renamed Base64Error to InvalidBase64\n    * Renamed InvalidUrl to InvalidUrlScheme\n    * Renamed ReqwestError to IncompleteResponseFromReqwest\n    * Renamed HttpError to IncompleteHttp\n    * Renamed HandshakeError to InvalidHandshake\n    * Renamed ~ActionBeforeOpen to IllegalActionBeforeOpen()~\n    * Renamed DidNotReceiveProperAck to MissingAck\n    * Renamed PoisonedLockError to InvalidPoisonedLock\n    * Renamed FromWebsocketError to IncompleteResponseFromWebsocket\n    * Renamed FromWebsocketParseError to InvalidWebsocketURL\n    * Renamed FromIoError to IncompleteIo\n    * New error type InvalidUrl(UrlParseError)\n    * New error type InvalidInteger(ParseIntError)\n    * New error type IncompleteResponseFromEngineIo(rust_engineio::Error)\n    * New error type InvalidAttachmentPacketType(u8)\n    * Removed EmptyPacket\n* Refactored Packet\n    * Renamed encode to From<&Packet>\n    * Renamed decode to TryFrom<&Bytes>\n    * Renamed attachments to attachments_count\n    * New struct member attachments: Option<Vec<Bytes>>\n* Refactor PacketId\n    * Renamed u8_to_packet_id to TryFrom<u8> for PacketId\n* Refactored SocketBuilder\n    * Renamed set_namespace to namespace\n    * Renamed set_tls_config to tls_config\n    * Renamed set_opening_header to opening_header\n    * namespace returns Self rather than Result<Self>\n    * opening_header accepts a Into<HeaderValue> rather than HeaderValue\n* Allows for pure websocket connections\n* Refactor EngineIO module\n\n## <a name=\"024\">[0.2.4] - _Bugfixes_ </a>\n\n_2021.05.25_\n\n### Changes\n\n* Fixed a bug that prevented the client from receiving data for a message event issued on the server.\n\n## <a name=\"023\">[0.2.3] - _Disconnect methods on the Socket struct_ </a>\n\n_2021.05.24_\n\n### Changes\n\n* Added a `disconnect` method to the `Socket` struct as requested in [#43](https://github.com/1c3t3a/rust-socketio/issues/43).\n\n## <a name=\"022\">[0.2.2] - _Safe websockets and custom headers_ </a>\n\n_2021.05.13_\n\n### Changes\n\n* Added websocket communication over TLS when either `wss`, or `https` are specified in the URL.\n* Added the ability to configure the TLS connection by providing an own `TLSConnector`.\n* Added the ability to set custom headers as requested in [#35](https://github.com/1c3t3a/rust-socketio/issues/35).\n\n## <a name=\"021\">[0.2.1] - _Bugfixes_ </a>\n\n_2021.04.27_\n\n### Changes\n\n* Corrected memory ordering issues which might have become an issue on certain platforms.\n* Added this CHANGELOG to keep track of all changes.\n* Small stylistic changes to the codebase in general.\n\n## <a name=\"020\">[0.2.0] - _Fully implemented the socket.io protocol 🎉_ </a>\n\n_2021.03.13_\n\n\n### Changes\n\n* Moved from async rust to sync rust.\n* Implemented the missing protocol features.\n    * Websocket as a transport layer.\n    * Binary payload.\n* Added a `SocketBuilder` class to easily configure a connected client.\n* Added a new `Payload` type to manage the binary and string payload.\n\n\n## <a name=\"011\">[0.1.1] - _Update to tokio 1.0_</a>\n\n_2021.01.10_\n\n### Changes\n\n* Bumped `tokio` to version `1.0.*`, and therefore reqwest to `0.11.*`.\n* Removed all `unsafe` code.\n\n## <a name=\"010\">[0.1.0] - _First release of rust-socketio 🎉_</a>\n\n_2021.01.05_\n\n* First version of the library written in async rust. The features included:\n    * connecting to a server.\n    * register callbacks for the following event types:\n        * open, close, error, message\n    * custom events like \"foo\", \"on_payment\", etc.\n    * send json-data to the server (recommended to use serde_json as it provides safe handling of json data).\n    * send json-data to the server and receive an ack with a possible message.\n\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Introduction\n\nContributions to this project are welcome! \n\nThis project is still being developed, our goal is to have a well-designed\nthought-out project, so when you make a contribution, please think in those\nterms. That is:\n\n- For code:\n    - Is the contribution in the scope of this crate?\n    - Is it well documented?\n    - Is it well tested?\n- For documentation:\n    - Is it clear and well-written?\n    - Can others understand it easily?\n- For bugs:\n    - Does it test functionality of this crate?\n    - Do you have a minimal crate that causes the issue that we can use and test?\n- For feature requests:\n    - Is the request within the scope of this crate?\n    - Is the request clearly explained?\n\n## Licensing and other property rights.\n\nAll contributions that are made to this project are only accepted under the\nterms in the [LICENSE](LICENSE) file. That is, if you contribute to this\nproject, you are certifying that you have all the necessary rights to make the\ncontribution under the terms of the [LICENSE](LICENSE) file, and you are in fact\nlicensing them to this project and anyone that uses this project under the terms\nin the [LICENSE](LICENSE) file.\n\n## Misc\n- We are looking at adopting the [conventional commits 1.0](https://www.conventionalcommits.org/en/v1.0.0/) standard.\n    - This would make it easier for us to use tools like [jilu](https://crates.io/crates/jilu) to create change logs.\n    - Read [keep a changelog](https://keepachangelog.com/en/1.0.0/) for more information.\n\n## Git hooks\n\n> Git hooks are scripts that Git executes before or after events such as: commit, push, and receive. Git hooks are a built-in feature - no need to download anything. Git hooks are run locally.\n- [githooks.com](https://githooks.com/)\n\nThese example hooks enforce some of these contributing guidelines so you don't need to remember them.\n\n### pre-commit\n\nPut the contents below in ./.git/hooks/pre-commit\n```bash\n#!/bin/bash\nset -e \nset -o pipefail\n# Clippy recomendations\ncargo clippy --verbose\n# Check formatting\ncargo fmt --all -- --check || {\n    cargo fmt\n    echo \"Formatted some files make sure to check them in.\"\n    exit 1\n}\n# Make sure our build passes\ncargo build --verbose\n```\n\n### commit-msg\n\n\nPut the contents below in ./.git/hooks/commit-msg\n```bash\n#!/bin/bash\n\n# https://dev.to/craicoverflow/enforcing-conventional-commits-using-git-hooks-1o5p\n\nregexp=\"^(revert: )?(fix|feat|docs|ci|refactor|style|test)(\\(.*?\\))?(\\!)?: .+$\"\nmsg=\"$(head -1 $1)\"\n\nif [[ ! $msg =~ $regexp ]]\nthen\n    echo -e \"INVALID COMMIT MESSAGE\"\n    echo -e \"------------------------\"\n    echo -e \"Valid types: fix, feat, docs, ci, style, test, refactor\"\n    echo -e \"Such as: 'feat: add new feature'\"\n    echo -e \"See https://www.conventionalcommits.org/en/v1.0.0/ for details\"\n    echo\n    # exit with an error\n    exit 1\nfi\n\nwhile read line\ndo\n    if [[ $(echo \"$line\" | wc -c) -gt 51 ]]\n    then\n        echo \"Line '$line' is longer than 50 characters.\"\n        echo \"Consider splitting into these two lines\"\n        echo \"$line\" | head -c 50\n        echo\n        echo \"$line\" | tail -c +51\n        echo\n        exit 1\n    fi\ndone < $1\n\n```\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nmembers = [\"engineio\", \"socketio\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Bastian Kersting\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": ".PHONY: build test-fast run-test-servers test-all clippy format checks pipeline\n\nbuild:\n\t@cargo build --verbose --all-features\n\nkeys:\n\t@./ci/keygen.sh node-engine-io-secure 127.0.0.1\n\ntest-fast: keys\n\t@cargo test --verbose --package rust_socketio --lib -- engineio::packet && cargo test --verbose --package rust_socketio --lib -- socketio::packet\n\nrun-test-servers:\n\tcd ci && docker build -t test_suite:latest . && cd ..\n\tdocker run -d -p 4200:4200 -p 4201:4201 -p 4202:4202 -p 4203:4203 -p 4204:4204 -p 4205:4205 -p 4206:4206  --name socketio_test test_suite:latest\n\ntest-all: keys run-test-servers\n\t@cargo test --verbose --all-features\n\tdocker stop socketio_test\n\nclippy:\n\t@cargo clippy --verbose --all-features\n\nformat:\n\t@cargo fmt --all -- --check\n\nchecks: build test-fast clippy format\n\t@echo \"### Don't forget to add untracked files! ###\"\n\t@git status\n\t@echo \"### Awesome work! 😍 ###\"\"\"\n\npipeline: build test-all clippy format\n\t@echo \"### Don't forget to add untracked files! ###\"\n\t@git status\n\t@echo \"### Awesome work! 😍 ###\"\"\"\n"
  },
  {
    "path": "README.md",
    "content": "[![Latest Version](https://img.shields.io/crates/v/rust_socketio)](https://crates.io/crates/rust_socketio)\n[![docs.rs](https://docs.rs/rust_socketio/badge.svg)](https://docs.rs/rust_socketio)\n[![Build and code style](https://github.com/1c3t3a/rust-socketio/actions/workflows/build.yml/badge.svg)](https://github.com/1c3t3a/rust-socketio/actions/workflows/build.yml)\n[![Test](https://github.com/1c3t3a/rust-socketio/actions/workflows/test.yml/badge.svg)](https://github.com/1c3t3a/rust-socketio/actions/workflows/test.yml)\n[![codecov](https://codecov.io/gh/1c3t3a/rust-socketio/branch/main/graph/badge.svg?token=GUF406K0KL)](https://codecov.io/gh/1c3t3a/rust-socketio)\n\n# Rust-socketio-client\n\nAn implementation of a socket.io client written in the rust programming language. This implementation currently supports revision 5 of the socket.io protocol and therefore revision 4 of the engine.io protocol. If you have any connection issues with this client, make sure the server uses at least revision 4 of the engine.io protocol.\nInformation on the [`async`](#async) version can be found below.\n\n## Example usage\n\nAdd the following to your `Cargo.toml` file:\n\n```toml\nrust_socketio = \"*\"\n```\n\nThen you're able to run the following example code:\n\n``` rust\nuse rust_socketio::{ClientBuilder, Payload, RawClient};\nuse serde_json::json;\nuse std::time::Duration;\n\n// define a callback which is called when a payload is received\n// this callback gets the payload as well as an instance of the\n// socket to communicate with the server\nlet callback = |payload: Payload, socket: RawClient| {\n       match payload {\n           Payload::String(str) => println!(\"Received: {}\", str),\n           Payload::Binary(bin_data) => println!(\"Received bytes: {:#?}\", bin_data),\n       }\n       socket.emit(\"test\", json!({\"got ack\": true})).expect(\"Server unreachable\")\n};\n\n// get a socket that is connected to the admin namespace\nlet socket = ClientBuilder::new(\"http://localhost:4200\")\n     .namespace(\"/admin\")\n     .on(\"test\", callback)\n     .on(\"error\", |err, _| eprintln!(\"Error: {:#?}\", err))\n     .connect()\n     .expect(\"Connection failed\");\n\n// emit to the \"foo\" event\nlet json_payload = json!({\"token\": 123});\nsocket.emit(\"foo\", json_payload).expect(\"Server unreachable\");\n\n// define a callback, that's executed when the ack got acked\nlet ack_callback = |message: Payload, _| {\n    println!(\"Yehaa! My ack got acked?\");\n    println!(\"Ack data: {:#?}\", message);\n};\n\nlet json_payload = json!({\"myAckData\": 123});\n// emit with an ack\nsocket\n    .emit_with_ack(\"test\", json_payload, Duration::from_secs(2), ack_callback)\n    .expect(\"Server unreachable\");\n\nsocket.disconnect().expect(\"Disconnect failed\")\n\n```\n\nThe main entry point for using this crate is the `ClientBuilder` which provides a way to easily configure a socket in the needed way. When the `connect` method is called on the builder, it returns a connected client which then could be used to emit messages to certain events. One client can only be connected to one namespace. If you need to listen to the messages in different namespaces you need to allocate multiple sockets.\n\n## Documentation\n\nDocumentation of this crate can be found up on [docs.rs](https://docs.rs/rust_socketio).\n\n## Current features\n\nThis implementation now supports all of the features of the socket.io protocol mentioned [here](https://github.com/socketio/socket.io-protocol).\nIt generally tries to make use of websockets as often as possible. This means most times\nonly the opening request uses http and as soon as the server mentions that he is able to upgrade to\nwebsockets, an upgrade  is performed. But if this upgrade is not successful or the server\ndoes not mention an upgrade possibility, http-long polling is used (as specified in the protocol specs).\nHere's an overview of possible use-cases:\n- connecting to a server.\n- register callbacks for the following event types:\n    - open\n    - close\n    - error\n    - message\n    - custom events like \"foo\", \"on_payment\", etc.\n- send JSON data to the server (via `serde_json` which provides safe\nhandling).\n- send JSON data to the server and receive an `ack`.\n- send and handle Binary data.\n\n## <a name=\"async\"> Async version\nThis library provides an ability for being executed in an asynchronous context using `tokio` as\nthe execution runtime.\nPlease note that the current async implementation is still experimental, the interface can be object to\nchanges at any time.\nThe async `Client` and `ClientBuilder` support a similar interface to the sync version and live\nin the `asynchronous` module. In order to enable the support, you need to enable the `async`\nfeature flag:\n```toml\nrust_socketio = { version = \"*\", features = [\"async\"] }\n```\n\nThe following code shows the example above in async fashion:\n``` rust\nuse futures_util::FutureExt;\nuse rust_socketio::{\n    asynchronous::{Client, ClientBuilder},\n    Payload,\n};\nuse serde_json::json;\nuse std::time::Duration;\n\n#[tokio::main]\nasync fn main() {\n    // define a callback which is called when a payload is received\n    // this callback gets the payload as well as an instance of the\n    // socket to communicate with the server\n    let callback = |payload: Payload, socket: Client| {\n        async move {\n            match payload {\n                Payload::String(str) => println!(\"Received: {}\", str),\n                Payload::Binary(bin_data) => println!(\"Received bytes: {:#?}\", bin_data),\n            }\n            socket\n                .emit(\"test\", json!({\"got ack\": true}))\n                .await\n                .expect(\"Server unreachable\");\n        }\n        .boxed()\n    };\n\n    // get a socket that is connected to the admin namespace\n    let socket = ClientBuilder::new(\"http://localhost:4200/\")\n        .namespace(\"/admin\")\n        .on(\"test\", callback)\n        .on(\"error\", |err, _| {\n            async move { eprintln!(\"Error: {:#?}\", err) }.boxed()\n        })\n        .connect()\n        .await\n        .expect(\"Connection failed\");\n\n    // emit to the \"foo\" event\n    let json_payload = json!({\"token\": 123});\n    socket\n        .emit(\"foo\", json_payload)\n        .await\n        .expect(\"Server unreachable\");\n\n    // define a callback, that's executed when the ack got acked\n    let ack_callback = |message: Payload, _: Client| {\n        async move {\n            println!(\"Yehaa! My ack got acked?\");\n            println!(\"Ack data: {:#?}\", message);\n        }\n        .boxed()\n    };\n\n    let json_payload = json!({\"myAckData\": 123});\n    // emit with an ack\n    socket\n        .emit_with_ack(\"test\", json_payload, Duration::from_secs(2), ack_callback)\n        .await\n        .expect(\"Server unreachable\");\n\n    socket.disconnect().await.expect(\"Disconnect failed\");\n}\n```\n\n## Content of this repository\n\nThis repository contains a rust implementation of the socket.io protocol as well as the underlying engine.io protocol.\n\nThe details about the engine.io protocol can be found here:\n\n* <https://github.com/socketio/engine.io-protocol>\n\nThe specification for the socket.io protocol here:\n\n* <https://github.com/socketio/socket.io-protocol>\n\nLooking at the component chart, the following parts are implemented (Source: https://socket.io/images/dependencies.jpg):\n\n<img src=\"docs/res/dependencies.jpg\" width=\"50%\"/>\n\n## Licence\n\nMIT\n"
  },
  {
    "path": "Roadmap.md",
    "content": "# Roadmap\n- 0.2.5 deprecation begins\n- 0.3.0 refactor with breaking changes (Conform to api recommendations wherever reasonable)\n- 0.4.0 Release with basic server\n- 0.5.0 Release with async\n- 0.6.0 Release with redis\n- ????? Rooms\n- ????? Refactor Engine.IO to separate crate\n- 1.0.0 Stable?\n"
  },
  {
    "path": "ci/.dockerignore",
    "content": "node_modules\n"
  },
  {
    "path": "ci/Dockerfile",
    "content": "FROM node:12.18.1\nWORKDIR /test\nCOPY . ./\nCOPY start_test_server.sh ./\n\nRUN cp cert/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates\n\nRUN npm install\n\nRUN chmod u+x start_test_server.sh\nCMD ./start_test_server.sh\n"
  },
  {
    "path": "ci/README.md",
    "content": "# How the CI pipelines are set up\n\nThis document explains how the CI pipeline is set up. Generally, the pipeline runs on ever push to the `main` branch or on a pull request.\nThere are three different pipelines:\n\n  *  Build and Codestyle: Tries to build the application and checks the code quality and formatting through `cargo clippy` and `cargo fmt`. \n     If you'd like to trigger this manually, run `make checks` in the project root.\n  *  Build and test: Builds the code and kicks of a docker container containing the socket.io and engine.io servers. Then the tests run against the servers. The servers code should not be changed as the clients' tests assume certain events to fire e.g. an ack gets acked or a certain namespace exists. Two servers are started:\n      * An engine.io server with some callbacks that send normal string data.\n      * A _safe_ engine.io server with some callbacks that send normal string data. Generate keys for TLS with `./ci/keygen.sh localhost 127.0.0.1`. This server is used for tests using `wss://` and `https://`.\n      * A socket.io server which sends string and binary data, handles acks, etc.\n  * Generate coverage: This action acts like the `Build and test` action, but generates a coverage report as well. Afterward the coverage report is uploaded to codecov.io.\n    This action also collects the docker server logs and uploads them as an artifact.\n\n# How to run the tests locally\n\nTo run the tests locally, simply use `cargo test`, or the various Make targets in the `Makefile`. For example\n`make pipeline` runs all tests, but also `clippy` and `rustfmt`.\n\nAs some tests depend on a running engine.io/socket.io server, you will need to provide those locally, too. See the\nsections below for multiple options to do this.\n\nYou will also need to create a self-signed certificate for the secure engine.io server. This can be done with the\nhelper script `/ci/keygen.sh`. Please make sure to also import the generated ca and server certificates into your\nsystem (i.e. Keychain Access for MacOS, /usr/local/share/ca-certificates/ for linux) and make sure they are \"always\ntrusted\".\n\n## Running server processes manually via nodejs\n\nIf you'd like to run the full test suite locally, you need to run the five server instances as well (see files in `ci/`\nfolder). You could do this manually by running them all with node:\n\n```\nnode engine-io.js \nnode engine-io-polling.js\nnode engine-io-secure.js\nnode socket-io.js\nnode socket-io-auth.js \nnode socket-io-restart.js\nnode socket-io-restart-url-auth.js\n```\n\nIf you'd like to see debug log as well, export this environment variable beforehand:\n\n```\nexport DEBUG=*\n```\n\nYou will need to have the two node packages `socket.io` and `engine.io` installed, if this is not the case, fetch them\nvia:\n\n```\nnpm install socket.io engine.io\n```\n\n## Running server processes in a Docker container\n\nAs running all the node scripts manually is pretty tedious, you can also use a prepared docker container, which can be\nbuilt with the Dockerfile located in the `ci/` folder:\n\n```\ndocker build -t test_suite:latest ci\n```\n\nThen you can run the container and forward all the needed ports with the following command:\n\n```\ndocker run -d --name test_suite -p 4200:4200 -p 4201:4201 -p 4202:4202 -p 4203:4203 -p 4204:4204 -p 4205:4205 -p 4206:4206 test_suite:latest\n```\n\nThe docker container runs a shell script that starts the two servers in the background and checks if the processes are\nstill alive.\n\n## Using the Visual Studio Code devcontainer\n\nIf you are using Visual Studio Code, the easiest method to get up and running would be to simply use the devcontainer\nprepared in the `.devcontainer/` directory. This will also launch the needed server processes and set up networking etc.\nPlease refer to the vscode [documentation](https://code.visualstudio.com/docs/remote/containers) for more information\non how to use devcontainers.\n\n# Polling vs. Websockets\n\nThe underlying engine.io protocol provides two mechanisms for transporting: polling and websockets. In order to test both in the pipeline, the two servers are configured differently. The socket.io test suite always upgrades to websockets as fast as possible while one of the engine.io suites just uses long-polling, the other one uses websockets but is reachable via `https://` and `wss://`. This assures that both the websocket connection code and the long-polling code gets tested (as seen on codecov.io). Keep that in mind while expanding the tests.\n"
  },
  {
    "path": "ci/engine-io-polling.js",
    "content": "/**\n * This is an example server, used to test the current code.\n */\nconst engine = require('engine.io');\nconst http = require('http').createServer().listen(4203);\n// the engine.io client runs on port 4203\nconst server = engine.attach(http, {\n    allowUpgrades: false,\n    transports: [\"polling\"]\n});\n\nconsole.log(\"Started\")\nserver.on('connection', socket => {\n    console.log(\"Connected\");\n\n    socket.on('message', message => {\n        if (message !== undefined) {\n            console.log(message.toString());\n            if (message == \"respond\") {\n                socket.send(\"Roger Roger\");\n            } else if (message == \"close\") {\n                socket.close();\n            }\n        } else {\n            console.log(\"empty message received\")\n        }\n    });\n\n    socket.on('heartbeat', () => {\n        console.log(\"heartbeat\");\n    });\n\n    socket.on('error', message => {\n        // Notify the client if there is an error so it's tests will fail\n        socket.send(\"ERROR: Received error\")\n        console.log(message.toString());\n    });\n\n    socket.on('close', () => {\n        console.log(\"Close\");\n        socket.close();\n    });\n\n    socket.send('hello client');\n});\n"
  },
  {
    "path": "ci/engine-io-secure.js",
    "content": "const fs = require('fs');\nconst https = require('https');\nconst eio = require('engine.io');\n\nconst serverOpts = {\n    key: fs.readFileSync(\"cert/server.key\"),\n    cert: fs.readFileSync(\"cert/server.crt\"),\n    ca: fs.readFileSync(\"cert/ca.crt\"),\n};\n\nconst http = https.createServer(serverOpts);\n\nconst server = eio.attach(http);\n\nconsole.log(\"Started\")\nhttp.listen(4202, () => {\n    server.on('connection', socket => {\n        console.log(\"Connected\");\n    \n        socket.on('message', message => {\n            if (message !== undefined) {\n                console.log(message.toString());\n                if (message == \"respond\") {\n                    socket.send(\"Roger Roger\");\n                } else if (message == \"close\") {\n                    socket.close();\n                }\n            } else {\n                console.log(\"empty message received\")\n            }\n        });\n    \n        socket.on('heartbeat', () => {\n            console.log(\"heartbeat\");\n        });\n    \n        socket.on('error', message => {\n            // Notify the client if there is an error so it's tests will fail\n            socket.send(\"ERROR: Received error\")\n            console.log(message.toString());\n        });\n    \n        socket.on('close', () => {\n            console.log(\"Close\");\n            socket.close();\n        });\n    \n        socket.send('hello client');\n    });\n});\n"
  },
  {
    "path": "ci/engine-io.js",
    "content": "/**\n * This is an example server, used to test the current code.\n */\nconst engine = require('engine.io');\nconst http = require('http').createServer().listen(4201);\n// the engine.io client runs on port 4201\nconst server = engine.attach(http);\n\nconsole.log(\"Started\")\nserver.on('connection', socket => {\n    console.log(\"Connected\");\n\n    socket.on('message', message => {\n        if (message !== undefined) {\n            console.log(message.toString());\n            if (message == \"respond\") {\n                socket.send(\"Roger Roger\");\n            } else if (message == \"close\") {\n                socket.close();\n            }\n        } else {\n            console.log(\"empty message received\")\n        }\n    });\n\n    socket.on('heartbeat', () => {\n        console.log(\"heartbeat\");\n    });\n\n    socket.on('error', message => {\n        // Notify the client if there is an error so it's tests will fail\n        socket.send(\"ERROR: Received error\")\n        console.log(message.toString());\n    });\n\n    socket.on('close', () => {\n        console.log(\"Close\");\n        socket.close();\n    });\n\n    socket.send('hello client');\n});\n"
  },
  {
    "path": "ci/keygen.sh",
    "content": "#!/bin/sh\ncd $(dirname $0)\n\nif [ \"$1\" = \"\" ] || [ \"$2\" = \"\" ]\nthen\n    echo \"Usage: keygen.sh [DOMAIN] [IP]\"\n    exit 1\nfi\n\nDOMAIN=\"$1\"\nIP=\"$2\"\nCA_NAME=${CA_NAME:-\"rust-socketio-dev\"}\n\nmkdir cert || true\ncd cert\n# Credit https://scriptcrunch.com/create-ca-tls-ssl-certificates-keys/\n\nif [ ! -f ca.key ]\nthen\n    echo \"Generating CA key\"\n    openssl genrsa -out ca.key 4096\nfi\n\nif [ ! -f \"ca.crt\" ]\nthen\n    echo \"Generating CA cert\"\n    openssl req -x509 -new -nodes -key ca.key -subj \"/CN=${CA_NAME}/C=??/L=Varius\" -out ca.crt\nfi\n\nif [ ! -f \"server.key\" ]\nthen\n    echo \"Generating server key\"\n    openssl genrsa -out server.key 4096\nfi\n\nif [ ! -f \"csr.conf\" ]\nthen\n    echo \"\"\"\n[ req ]\ndefault_bits = 4096\nprompt = no\ndefault_md = sha256\nreq_extensions = req_ext\ndistinguished_name = dn\n\n[ dn ]\nC = ??\nST = Varius\nL = Varius\nO = ${DOMAIN}\nOU = ${DOMAIN}\nCN = ${DOMAIN}\n\n[ req_ext ]\nsubjectAltName = @alt_names\n\n[ alt_names ]\nDNS.1 = ${DOMAIN}\nDNS.2 = localhost\nIP.1 = ${IP}\n\"\"\" > csr.conf\nfi\n\nif [ ! -f \"server.csr\" ]\nthen\n    echo \"Generating server signing request\"\n    openssl req -new -key server.key -out server.csr -config csr.conf\nfi\n\nif [ ! -f \"server.crt\" ]\nthen\n    echo \"Generating signed server certifcicate\"\n    openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -extfile csr.conf\nfi\n"
  },
  {
    "path": "ci/package.json",
    "content": "{\n  \"name\": \"rust-socketio-test\",\n  \"version\": \"1.0.0\",\n  \"description\": \"A test environment for a socketio client\",\n  \"author\": \"Bastian Kersting\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"engine.io\": \"6.4.2\",\n    \"socket.io\": \"4.0.0\"\n  }\n}\n"
  },
  {
    "path": "ci/socket-io-auth.js",
    "content": "const server = require('http').createServer();\nconst io = require('socket.io')(server);\n\nconsole.log('Started');\nvar callback = client => {\n    console.log('Connected!');\n\n    client.emit('auth', client.handshake.auth.password === '123' ? 'success' : 'failed')\n};\nio.on('connection', callback);\nio.of('/admin').on('connection', callback);\n// the socket.io client runs on port 4204\nserver.listen(4204);\n"
  },
  {
    "path": "ci/socket-io-restart-url-auth.js",
    "content": "let createServer = require(\"http\").createServer;\nlet server = createServer();\nconst io = require(\"socket.io\")(server);\nconst port = 4206;\nconst timeout = 200;\n\nconst TIMESTAMP_SLACK_ALLOWED = 1000;\n\nfunction isValidTimestamp(timestampStr) {\n  if (timestampStr === undefined) return false;\n  const timestamp = parseInt(timestampStr);\n  if (isNaN(timestamp)) return false;\n\n  const diff = Date.now() - timestamp;\n  return Math.abs(diff) <= TIMESTAMP_SLACK_ALLOWED;\n}\n\nconsole.log(\"Started\");\nvar callback = (client) => {\n  const timestamp = client.request._query.timestamp;\n  console.log(\"Connected, timestamp:\", timestamp);\n  if (!isValidTimestamp(timestamp)) {\n    console.log(\"Invalid timestamp!\");\n    client.disconnect();\n    return;\n  }\n  client.emit(\"message\", \"test\");\n  client.on(\"restart_server\", () => {\n    console.log(\"will restart in \", timeout, \"ms\");\n    io.close();\n    setTimeout(() => {\n      server = createServer();\n      server.listen(port);\n      io.attach(server);\n      console.log(\"do restart\");\n    }, timeout);\n  });\n};\nio.on(\"connection\", callback);\nserver.listen(port);\n"
  },
  {
    "path": "ci/socket-io-restart.js",
    "content": "let createServer = require(\"http\").createServer;\nlet server = createServer();\nconst io = require(\"socket.io\")(server);\nconst port = 4205;\nconst timeout = 2000;\n\nconsole.log(\"Started\");\nvar callback = (client) => {\n  const headers = client.request.headers;\n  console.log(\"headers\", headers);\n  const message = headers.message_back || \"test\";\n\n  console.log(\"Connected!\");\n  client.emit(\"message\", message);\n  client.on(\"restart_server\", () => {\n    console.log(\"will restart in \", timeout, \"ms\");\n    io.close();\n    setTimeout(() => {\n      server = createServer();\n      server.listen(port);\n      io.attach(server);\n      console.log(\"do restart\");\n    }, timeout);\n  });\n};\nio.on(\"connection\", callback);\nserver.listen(port);\n"
  },
  {
    "path": "ci/socket-io.js",
    "content": "const server = require('http').createServer();\nconst io = require('socket.io')(server);\n\nconsole.log('Started');\nvar callback = client => {\n    console.log('Connected!');\n    client.on('test', data => {\n        // Send a message back to the server to confirm the message was received\n        client.emit('test-received', data);\n        console.log(['test', data]);\n    });\n    client.on('message', data => {\n        client.emit('message-received', data);\n        console.log(['message', data]);\n    });\n    client.on('test', function (arg, ack) {\n        console.log('Ack received')\n        if (ack) {\n            ack('woot');\n        }\n    });\n\n    client.on('binary', data => {\n        var bufView = new Uint8Array(data);\n        console.log(['binary', 'Yehaa binary payload!']);\n        for (elem in bufView) {\n            console.log(['binary', elem]);\n        }\n        client.emit('binary-received', data);\n        console.log(['binary', data]);\n    });\n\n    client.on('binary', function (arg, ack) {\n        console.log(['binary', 'Ack received, answer with binary'])\n        if (ack) {\n            ack(Buffer.from([1, 2, 3]));\n        }\n    });\n\n    // This event allows the test framework to arbitrarily close the underlying connection\n    client.on('close_transport', data => {\n        console.log(['close_transport', 'Request to close transport received'])\n        // Close underlying websocket connection\n        client.client.conn.close();\n    })\n\n    client.emit('Hello from the message event!');\n    client.emit('test', 'Hello from the test event!');\n    client.emit(Buffer.from([4, 5, 6]));\n    client.emit('test', Buffer.from([1, 2, 3]));\n    client.emit('This is the first argument', 'This is the second argument', {\n        argCount: 3\n    });\n    client.emit('on_abc_event', '', {\n        abc: 0,\n        some_other: 'value',\n    });\n};\nio.on('connection', callback);\nio.of('/admin').on('connection', callback);\n// the socket.io client runs on port 4201\nserver.listen(4200);\n"
  },
  {
    "path": "ci/start_test_server.sh",
    "content": "echo \"Starting test environment\"\nDEBUG=* node engine-io.js &\nstatus=$?\nif [ $status -ne 0 ]; then\n  echo \"Failed to start engine.io: $status\"\n  exit $status\nfi\necho \"Successfully started engine.io instance\"\n\nDEBUG=* node engine-io-polling.js &\nstatus=$?\nif [ $status -ne 0 ]; then\n  echo \"Failed to start polling engine.io: $status\"\n  exit $status\nfi\necho \"Successfully started polling engine.io instance\"\n\nDEBUG=* node socket-io.js &\nstatus=$?\nif [ $status -ne 0 ]; then\n  echo \"Failed to start socket.io: $status\"\n  exit $status\nfi\necho \"Successfully started socket.io instance\"\n\nDEBUG=* node socket-io-auth.js &\nstatus=$?\nif [ $status -ne 0 ]; then\n  echo \"Failed to start socket.io auth: $status\"\n  exit $status\nfi\necho \"Successfully started socket.io auth instance\"\n\nDEBUG=* node socket-io-restart.js &\nstatus=$?\nif [ $status -ne 0 ]; then\n  echo \"Failed to start socket.io restart: $status\"\n  exit $status\nfi\necho \"Successfully started socket.io restart instance\"\n\nDEBUG=* node socket-io-restart-url-auth.js &\nstatus=$?\nif [ $status -ne 0 ]; then\n  echo \"Failed to start socket.io restart url auth: $status\"\n  exit $status\nfi\necho \"Successfully started socket.io restart url auth instance\"\n\nDEBUG=* node engine-io-secure.js &\nstatus=$?\nif [ $status -ne 0 ]; then\n  echo \"Failed to start secure engine.io: $status\"\n  exit $status\nfi\necho \"Successfully started secure engine.io instance\"\n\nwhile sleep 60; do\n  ps aux | grep socket | grep -q -v grep\n  PROCESS_1_STATUS=$?\n  ps aux | grep engine-io.js | grep -q -v grep\n  PROCESS_2_STATUS=$?\n  ps aux | grep engine-io-secure.js | grep -q -v grep\n  PROCESS_3_STATUS=$?\n  # If the greps above find anything, they exit with 0 status\n  # If they are not both 0, then something is wrong\n  if [ $PROCESS_1_STATUS -ne 0 -o $PROCESS_2_STATUS -ne 0 -o $PROCESS_3_STATUS -ne 0 ]; then\n    echo \"One of the processes has already exited.\"\n    exit 1\n  fi\ndone\n"
  },
  {
    "path": "codecov.yml",
    "content": "coverage:\n  range: 50..90 # coverage lower than 50 is red, higher than 90 green, between color code\n\n  status:\n    project: # settings affecting project coverage\n      default:\n        target: auto # auto % coverage target\n        threshold: 5%  # allow for 5% reduction of coverage without failing\n\n    # do not run coverage on patch nor changes\n    patch: false\n"
  },
  {
    "path": "engineio/Cargo.toml",
    "content": "[package]\nname = \"rust_engineio\"\nversion = \"0.6.0\"\nauthors = [\"Bastian Kersting <bastian@cmbt.de>\"]\nedition = \"2021\"\ndescription = \"An implementation of a engineio client written in rust.\"\nreadme = \"README.md\"\nrepository = \"https://github.com/1c3t3a/rust-socketio\"\nkeywords = [\"engineio\", \"network\", \"protocol\", \"client\"]\ncategories = [\"network-programming\", \"web-programming\", \"web-programming::websocket\"]\nlicense = \"MIT\"\n\n[package.metadata.docs.rs]\nall-features = true\n\n[dependencies]\nbase64 = \"0.22.1\"\nbytes = \"1\"\nreqwest = { version = \"0.12.4\", features = [\"blocking\", \"native-tls\", \"stream\"] }\nadler32 = \"1.2.0\"\nserde = { version = \"1.0\", features = [\"derive\"] }\nserde_json = \"1.0\"\nhttp = \"1.1.0\"\ntokio-tungstenite = { version = \"0.21.0\", features = [\"native-tls\"] }\ntungstenite = \"0.21.0\"\ntokio = \"1.40.0\"\nfutures-util = { version = \"0.3\", default-features = false, features = [\"sink\"] }\nasync-trait = \"0.1.83\"\nasync-stream = \"0.3.6\"\nthiserror = \"1.0\"\nnative-tls = \"0.2.12\"\nurl = \"2.5.4\"\n\n[dev-dependencies]\ncriterion = { version = \"0.5.1\", features = [\"async_tokio\"] }\nlazy_static = \"1.4.0\"\n\n[dev-dependencies.tokio]\nversion = \"1.40.0\"\n# we need the `#[tokio::test]` macro\nfeatures = [\"macros\"]\n\n[[bench]]\nname = \"engineio\"\nharness = false\n\n# needs to be present in order to support the benchmark\n# ci job\n# source: https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options\n[lib]\nbench = false\n\n[features]\ndefault = [\"async\"]\nasync-callbacks = []\nasync = [\"async-callbacks\"]\n"
  },
  {
    "path": "engineio/README.md",
    "content": "[![Latest Version](https://img.shields.io/crates/v/rust_engineio)](https://crates.io/crates/rust_engineio)\n[![docs.rs](https://docs.rs/rust_engineio/badge.svg)](https://docs.rs/rust_engineio)\n\n# Rust-engineio-client\n\nAn implementation of a engine.io client written in the rust programming language. This implementation currently supports revision 4 of the engine.io protocol. If you have any connection issues with this client, make sure the server uses at least revision 4 of the engine.io protocol.\n\n## Example usage\n\n``` rust\nuse rust_engineio::{ClientBuilder, Client, packet::{Packet, PacketId}};\nuse url::Url;\nuse bytes::Bytes;\n\n// get a client with an `on_open` callback\nlet client: Client = ClientBuilder::new(Url::parse(\"http://localhost:4201\").unwrap())\n     .on_open(|_| println!(\"Connection opened!\"))\n     .build()\n     .expect(\"Connection failed\");\n\n// connect to the server\nclient.connect().expect(\"Connection failed\");\n\n// create a packet, in this case a message packet and emit it\nlet packet = Packet::new(PacketId::Message, Bytes::from_static(b\"Hello World\"));\nclient.emit(packet).expect(\"Server unreachable\");\n\n// disconnect from the server\nclient.disconnect().expect(\"Disconnect failed\")\n```\n\nThe main entry point for using this crate is the `ClientBuilder` (or `asynchronous::ClientBuilder` respectively)\nwhich provides the opportunity to define how you want to connect to a certain endpoint. \nThe following connection methods are available:\n* `build`: Build websocket if allowed, if not fall back to polling. Standard configuration.\n* `build_polling`: enforces a `polling` transport.\n* `build_websocket_with_upgrade`: Build socket with a polling transport then upgrade to websocket transport (if possible).\n* `build_websocket`: Build socket with only a websocket transport, crashes when websockets are not allowed.\n\n\n## Current features\n\nThis implementation now supports all of the features of the engine.io protocol mentioned [here](https://github.com/socketio/engine.io-protocol).\nThis includes various transport options, the possibility of sending engine.io packets and registering the\ncommon engine.io event callbacks:\n* on_open\n* on_close\n* on_data\n* on_error\n* on_packet\n\nIt is also possible to pass in custom tls configurations via the `TlsConnector` as well\nas custom headers for the opening request.\n\n## Documentation\n\nDocumentation of this crate can be found up on [docs.rs](https://docs.rs/rust_engineio).\n\n## Async version\n\nThe crate also ships with an asynchronous version that can be enabled with a feature flag.\nThe async version implements the same features mentioned above.\nThe asynchronous version has a similar API, just with async functions. Currently the futures\ncan only be executed with [`tokio`](https://tokio.rs). In the first benchmarks the async version\nshowed improvements of up to 93% in speed.\nTo make use of the async version, import the crate as follows:\n```toml\n[depencencies]\nrust-engineio = { version = \"0.3.1\", features = [\"async\"] }\n```\n"
  },
  {
    "path": "engineio/benches/engineio.rs",
    "content": "use criterion::{criterion_group, criterion_main};\nuse native_tls::Certificate;\nuse native_tls::TlsConnector;\nuse rust_engineio::error::Error;\nuse std::fs::File;\nuse std::io::Read;\nuse url::Url;\n\npub use criterion_wrappers::*;\npub use tests::*;\npub use util::*;\n\npub mod util {\n    use super::*;\n    pub fn engine_io_url() -> Result<Url, Error> {\n        let url = std::env::var(\"ENGINE_IO_SERVER\")\n            .unwrap_or_else(|_| \"http://localhost:4201\".to_owned());\n        Ok(Url::parse(&url)?)\n    }\n\n    pub fn engine_io_url_secure() -> Result<Url, Error> {\n        let url = std::env::var(\"ENGINE_IO_SECURE_SERVER\")\n            .unwrap_or_else(|_| \"https://localhost:4202\".to_owned());\n        Ok(Url::parse(&url)?)\n    }\n\n    pub fn tls_connector() -> Result<TlsConnector, Error> {\n        let cert_path = \"../\".to_owned()\n            + &std::env::var(\"CA_CERT_PATH\").unwrap_or_else(|_| \"ci/cert/ca.crt\".to_owned());\n        let mut cert_file = File::open(cert_path)?;\n        let mut buf = vec![];\n        cert_file.read_to_end(&mut buf)?;\n        let cert: Certificate = Certificate::from_pem(&buf[..]).unwrap();\n        Ok(TlsConnector::builder()\n            // ONLY USE FOR TESTING!\n            .danger_accept_invalid_hostnames(true)\n            .add_root_certificate(cert)\n            .build()\n            .unwrap())\n    }\n}\n\n/// sync benches\n\n#[cfg(not(feature = \"async\"))]\npub mod tests {\n    use bytes::Bytes;\n    use reqwest::Url;\n    use rust_engineio::{Client, ClientBuilder, Error, Packet, PacketId};\n\n    use crate::tls_connector;\n\n    pub fn engine_io_socket_build(url: Url) -> Result<Client, Error> {\n        ClientBuilder::new(url).build()\n    }\n\n    pub fn engine_io_socket_build_polling(url: Url) -> Result<Client, Error> {\n        ClientBuilder::new(url).build_polling()\n    }\n\n    pub fn engine_io_socket_build_polling_secure(url: Url) -> Result<Client, Error> {\n        ClientBuilder::new(url)\n            .tls_config(tls_connector()?)\n            .build_polling()\n    }\n\n    pub fn engine_io_socket_build_websocket(url: Url) -> Result<Client, Error> {\n        ClientBuilder::new(url).build_websocket()\n    }\n\n    pub fn engine_io_socket_build_websocket_secure(url: Url) -> Result<Client, Error> {\n        ClientBuilder::new(url)\n            .tls_config(tls_connector()?)\n            .build_websocket()\n    }\n\n    pub fn engine_io_packet() -> Packet {\n        Packet::new(PacketId::Message, Bytes::from(\"hello world\"))\n    }\n\n    pub fn engine_io_emit(socket: &Client, packet: Packet) -> Result<(), Error> {\n        socket.emit(packet)\n    }\n}\n\n#[cfg(not(feature = \"async\"))]\nmod criterion_wrappers {\n    use criterion::{black_box, Criterion};\n\n    use super::*;\n\n    pub fn criterion_engine_io_socket_build(c: &mut Criterion) {\n        let url = engine_io_url().unwrap();\n        c.bench_function(\"engine io build\", |b| {\n            b.iter(|| {\n                engine_io_socket_build(black_box(url.clone()))\n                    .unwrap()\n                    .close()\n            })\n        });\n    }\n\n    pub fn criterion_engine_io_socket_build_polling(c: &mut Criterion) {\n        let url = engine_io_url().unwrap();\n        c.bench_function(\"engine io build polling\", |b| {\n            b.iter(|| {\n                engine_io_socket_build_polling(black_box(url.clone()))\n                    .unwrap()\n                    .close()\n            })\n        });\n    }\n\n    pub fn criterion_engine_io_socket_build_polling_secure(c: &mut Criterion) {\n        let url = engine_io_url_secure().unwrap();\n        c.bench_function(\"engine io build polling secure\", |b| {\n            b.iter(|| {\n                engine_io_socket_build_polling_secure(black_box(url.clone()))\n                    .unwrap()\n                    .close()\n            })\n        });\n    }\n\n    pub fn criterion_engine_io_socket_build_websocket(c: &mut Criterion) {\n        let url = engine_io_url().unwrap();\n        c.bench_function(\"engine io build websocket\", |b| {\n            b.iter(|| {\n                engine_io_socket_build_websocket(black_box(url.clone()))\n                    .unwrap()\n                    .close()\n            })\n        });\n    }\n\n    pub fn criterion_engine_io_socket_build_websocket_secure(c: &mut Criterion) {\n        let url = engine_io_url_secure().unwrap();\n        c.bench_function(\"engine io build websocket secure\", |b| {\n            b.iter(|| {\n                engine_io_socket_build_websocket_secure(black_box(url.clone()))\n                    .unwrap()\n                    .close()\n            })\n        });\n    }\n\n    pub fn criterion_engine_io_packet(c: &mut Criterion) {\n        c.bench_function(\"engine io packet\", |b| b.iter(|| engine_io_packet()));\n    }\n\n    pub fn criterion_engine_io_emit_polling(c: &mut Criterion) {\n        let url = engine_io_url().unwrap();\n        let socket = engine_io_socket_build_polling(url).unwrap();\n        socket.connect().unwrap();\n        let packet = engine_io_packet();\n\n        c.bench_function(\"engine io polling emit\", |b| {\n            b.iter(|| engine_io_emit(black_box(&socket), black_box(packet.clone())).unwrap())\n        });\n        socket.close().unwrap();\n    }\n\n    pub fn criterion_engine_io_emit_polling_secure(c: &mut Criterion) {\n        let url = engine_io_url_secure().unwrap();\n        let socket = engine_io_socket_build_polling_secure(url).unwrap();\n        socket.connect().unwrap();\n        let packet = engine_io_packet();\n\n        c.bench_function(\"engine io polling secure emit\", |b| {\n            b.iter(|| engine_io_emit(black_box(&socket), black_box(packet.clone())).unwrap())\n        });\n        socket.close().unwrap();\n    }\n\n    pub fn criterion_engine_io_emit_websocket(c: &mut Criterion) {\n        let url = engine_io_url().unwrap();\n        let socket = engine_io_socket_build_websocket(url).unwrap();\n        socket.connect().unwrap();\n        let packet = engine_io_packet();\n\n        c.bench_function(\"engine io websocket emit\", |b| {\n            b.iter(|| engine_io_emit(black_box(&socket), black_box(packet.clone())).unwrap())\n        });\n        socket.close().unwrap();\n    }\n\n    pub fn criterion_engine_io_emit_websocket_secure(c: &mut Criterion) {\n        let url = engine_io_url_secure().unwrap();\n        let socket = engine_io_socket_build_websocket_secure(url).unwrap();\n        socket.connect().unwrap();\n        let packet = engine_io_packet();\n\n        c.bench_function(\"engine io websocket secure emit\", |b| {\n            b.iter(|| engine_io_emit(black_box(&socket), black_box(packet.clone())).unwrap())\n        });\n        socket.close().unwrap();\n    }\n}\n\n/// async benches\n\n#[cfg(feature = \"async\")]\npub mod tests {\n    use bytes::Bytes;\n    use rust_engineio::{\n        asynchronous::{Client, ClientBuilder},\n        Error, Packet, PacketId,\n    };\n    use url::Url;\n\n    use crate::tls_connector;\n\n    pub async fn engine_io_socket_build(url: Url) -> Result<Client, Error> {\n        ClientBuilder::new(url).build().await\n    }\n\n    pub async fn engine_io_socket_build_polling(url: Url) -> Result<Client, Error> {\n        ClientBuilder::new(url).build_polling().await\n    }\n\n    pub async fn engine_io_socket_build_polling_secure(url: Url) -> Result<Client, Error> {\n        ClientBuilder::new(url)\n            .tls_config(tls_connector()?)\n            .build_polling()\n            .await\n    }\n\n    pub async fn engine_io_socket_build_websocket(url: Url) -> Result<Client, Error> {\n        ClientBuilder::new(url).build_websocket().await\n    }\n\n    pub async fn engine_io_socket_build_websocket_secure(url: Url) -> Result<Client, Error> {\n        ClientBuilder::new(url)\n            .tls_config(tls_connector()?)\n            .build_websocket()\n            .await\n    }\n\n    pub fn engine_io_packet() -> Packet {\n        Packet::new(PacketId::Message, Bytes::from(\"hello world\"))\n    }\n\n    pub async fn engine_io_emit(socket: &Client, packet: Packet) -> Result<(), Error> {\n        socket.emit(packet).await\n    }\n}\n\n#[cfg(feature = \"async\")]\nmod criterion_wrappers {\n    use std::sync::Arc;\n\n    use bytes::Bytes;\n    use criterion::{black_box, Criterion};\n    use lazy_static::lazy_static;\n    use rust_engineio::{Packet, PacketId};\n    use tokio::runtime::{Builder, Runtime};\n\n    use super::tests::{\n        engine_io_emit, engine_io_packet, engine_io_socket_build, engine_io_socket_build_polling,\n        engine_io_socket_build_polling_secure, engine_io_socket_build_websocket,\n        engine_io_socket_build_websocket_secure,\n    };\n    use super::util::{engine_io_url, engine_io_url_secure};\n\n    lazy_static! {\n        static ref RUNTIME: Arc<Runtime> =\n            Arc::new(Builder::new_multi_thread().enable_all().build().unwrap());\n    }\n\n    pub fn criterion_engine_io_socket_build(c: &mut Criterion) {\n        let url = engine_io_url().unwrap();\n        c.bench_function(\"engine io build\", move |b| {\n            b.to_async(RUNTIME.as_ref()).iter(|| async {\n                engine_io_socket_build(black_box(url.clone()))\n                    .await\n                    .unwrap()\n                    .close()\n                    .await\n            })\n        });\n    }\n\n    pub fn criterion_engine_io_socket_build_polling(c: &mut Criterion) {\n        let url = engine_io_url().unwrap();\n        c.bench_function(\"engine io build polling\", move |b| {\n            b.to_async(RUNTIME.as_ref()).iter(|| async {\n                engine_io_socket_build_polling(black_box(url.clone()))\n                    .await\n                    .unwrap()\n                    .close()\n                    .await\n            })\n        });\n    }\n\n    pub fn criterion_engine_io_socket_build_polling_secure(c: &mut Criterion) {\n        let url = engine_io_url_secure().unwrap();\n        c.bench_function(\"engine io build polling secure\", move |b| {\n            b.to_async(RUNTIME.as_ref()).iter(|| async {\n                engine_io_socket_build_polling_secure(black_box(url.clone()))\n                    .await\n                    .unwrap()\n                    .close()\n                    .await\n            })\n        });\n    }\n\n    pub fn criterion_engine_io_socket_build_websocket(c: &mut Criterion) {\n        let url = engine_io_url().unwrap();\n        c.bench_function(\"engine io build websocket\", move |b| {\n            b.to_async(RUNTIME.as_ref()).iter(|| async {\n                engine_io_socket_build_websocket(black_box(url.clone()))\n                    .await\n                    .unwrap()\n                    .close()\n                    .await\n            })\n        });\n    }\n\n    pub fn criterion_engine_io_socket_build_websocket_secure(c: &mut Criterion) {\n        let url = engine_io_url_secure().unwrap();\n        c.bench_function(\"engine io build websocket secure\", move |b| {\n            b.to_async(RUNTIME.as_ref()).iter(|| async {\n                engine_io_socket_build_websocket_secure(black_box(url.clone()))\n                    .await\n                    .unwrap()\n                    .close()\n                    .await\n            })\n        });\n    }\n\n    pub fn criterion_engine_io_packet(c: &mut Criterion) {\n        c.bench_function(\"engine io packet\", move |b| {\n            b.iter(|| Packet::new(PacketId::Message, Bytes::from(\"hello world\")))\n        });\n    }\n\n    pub fn criterion_engine_io_emit_polling(c: &mut Criterion) {\n        let url = engine_io_url().unwrap();\n        let socket = RUNTIME.block_on(async {\n            let socket = engine_io_socket_build_polling(url).await.unwrap();\n            socket.connect().await.unwrap();\n            socket\n        });\n\n        let packet = engine_io_packet();\n\n        c.bench_function(\"engine io polling emit\", |b| {\n            b.to_async(RUNTIME.as_ref()).iter(|| async {\n                engine_io_emit(black_box(&socket), black_box(packet.clone()))\n                    .await\n                    .unwrap()\n            })\n        });\n    }\n\n    pub fn criterion_engine_io_emit_polling_secure(c: &mut Criterion) {\n        let url = engine_io_url_secure().unwrap();\n\n        let socket = RUNTIME.block_on(async {\n            let socket = engine_io_socket_build_polling_secure(url).await.unwrap();\n            socket.connect().await.unwrap();\n            socket\n        });\n\n        let packet = engine_io_packet();\n\n        c.bench_function(\"engine io polling secure emit\", |b| {\n            b.to_async(RUNTIME.as_ref()).iter(|| async {\n                engine_io_emit(black_box(&socket), black_box(packet.clone()))\n                    .await\n                    .unwrap()\n            })\n        });\n    }\n\n    pub fn criterion_engine_io_emit_websocket(c: &mut Criterion) {\n        let url = engine_io_url().unwrap();\n\n        let socket = RUNTIME.block_on(async {\n            let socket = engine_io_socket_build_websocket(url).await.unwrap();\n            socket.connect().await.unwrap();\n            socket\n        });\n\n        let packet = engine_io_packet();\n\n        c.bench_function(\"engine io websocket emit\", |b| {\n            b.to_async(RUNTIME.as_ref()).iter(|| async {\n                engine_io_emit(black_box(&socket), black_box(packet.clone()))\n                    .await\n                    .unwrap()\n            })\n        });\n    }\n\n    pub fn criterion_engine_io_emit_websocket_secure(c: &mut Criterion) {\n        let url = engine_io_url_secure().unwrap();\n        let socket = RUNTIME.block_on(async {\n            let socket = engine_io_socket_build_websocket_secure(url).await.unwrap();\n            socket.connect().await.unwrap();\n            socket\n        });\n\n        let packet = engine_io_packet();\n\n        c.bench_function(\"engine io websocket secure emit\", |b| {\n            b.to_async(RUNTIME.as_ref()).iter(|| async {\n                engine_io_emit(black_box(&socket), black_box(packet.clone()))\n                    .await\n                    .unwrap()\n            })\n        });\n    }\n}\n\ncriterion_group!(\n    benches,\n    criterion_engine_io_socket_build_polling,\n    criterion_engine_io_socket_build_polling_secure,\n    criterion_engine_io_socket_build_websocket,\n    criterion_engine_io_socket_build_websocket_secure,\n    criterion_engine_io_socket_build,\n    criterion_engine_io_packet,\n    criterion_engine_io_emit_polling,\n    criterion_engine_io_emit_polling_secure,\n    criterion_engine_io_emit_websocket,\n    criterion_engine_io_emit_websocket_secure\n);\ncriterion_main!(benches);\n"
  },
  {
    "path": "engineio/src/asynchronous/async_socket.rs",
    "content": "use std::{\n    fmt::Debug,\n    pin::Pin,\n    sync::{\n        atomic::{AtomicBool, Ordering},\n        Arc,\n    },\n};\n\nuse async_stream::try_stream;\nuse bytes::Bytes;\nuse futures_util::{stream, Stream, StreamExt};\nuse tokio::{runtime::Handle, sync::Mutex, time::Instant};\n\nuse crate::{\n    asynchronous::{callback::OptionalCallback, transport::AsyncTransportType},\n    error::Result,\n    packet::{HandshakePacket, Payload},\n    Error, Packet, PacketId,\n};\n\n#[derive(Clone)]\npub struct Socket {\n    handle: Handle,\n    transport: Arc<Mutex<AsyncTransportType>>,\n    transport_raw: AsyncTransportType,\n    on_close: OptionalCallback<()>,\n    on_data: OptionalCallback<Bytes>,\n    on_error: OptionalCallback<String>,\n    on_open: OptionalCallback<()>,\n    on_packet: OptionalCallback<Packet>,\n    connected: Arc<AtomicBool>,\n    last_ping: Arc<Mutex<Instant>>,\n    last_pong: Arc<Mutex<Instant>>,\n    connection_data: Arc<HandshakePacket>,\n    max_ping_timeout: u64,\n}\n\nimpl Socket {\n    pub(crate) fn new(\n        transport: AsyncTransportType,\n        handshake: HandshakePacket,\n        on_close: OptionalCallback<()>,\n        on_data: OptionalCallback<Bytes>,\n        on_error: OptionalCallback<String>,\n        on_open: OptionalCallback<()>,\n        on_packet: OptionalCallback<Packet>,\n    ) -> Self {\n        let max_ping_timeout = handshake.ping_interval + handshake.ping_timeout;\n\n        Socket {\n            handle: Handle::current(),\n            on_close,\n            on_data,\n            on_error,\n            on_open,\n            on_packet,\n            transport: Arc::new(Mutex::new(transport.clone())),\n            transport_raw: transport,\n            connected: Arc::new(AtomicBool::default()),\n            last_ping: Arc::new(Mutex::new(Instant::now())),\n            last_pong: Arc::new(Mutex::new(Instant::now())),\n            connection_data: Arc::new(handshake),\n            max_ping_timeout,\n        }\n    }\n\n    /// Opens the connection to a specified server. The first Pong packet is sent\n    /// to the server to trigger the Ping-cycle.\n    pub async fn connect(&self) -> Result<()> {\n        // SAFETY: Has valid handshake due to type\n        self.connected.store(true, Ordering::Release);\n\n        if let Some(on_open) = self.on_open.as_ref() {\n            let on_open = on_open.clone();\n            self.handle.spawn(async move { on_open(()).await });\n        }\n\n        // set the last ping to now and set the connected state\n        *self.last_ping.lock().await = Instant::now();\n\n        // emit a pong packet to keep trigger the ping cycle on the server\n        self.emit(Packet::new(PacketId::Pong, Bytes::new())).await?;\n\n        Ok(())\n    }\n\n    /// A helper method that distributes\n    pub(super) async fn handle_incoming_packet(&self, packet: Packet) -> Result<()> {\n        // check for the appropriate action or callback\n        self.handle_packet(packet.clone());\n        match packet.packet_id {\n            PacketId::MessageBinary => {\n                self.handle_data(packet.data.clone());\n            }\n            PacketId::Message => {\n                self.handle_data(packet.data.clone());\n            }\n            PacketId::Close => {\n                self.handle_close();\n            }\n            PacketId::Upgrade => {\n                // this is already checked during the handshake, so just do nothing here\n            }\n            PacketId::Ping => {\n                self.pinged().await;\n                self.emit(Packet::new(PacketId::Pong, Bytes::new())).await?;\n            }\n            PacketId::Pong | PacketId::Open => {\n                // this will never happen as the pong and open\n                // packets are only sent by the client\n                return Err(Error::InvalidPacket());\n            }\n            PacketId::Noop => (),\n        }\n        Ok(())\n    }\n\n    /// Helper method that parses bytes and returns an iterator over the elements.\n    fn parse_payload(bytes: Bytes) -> impl Stream<Item = Result<Packet>> {\n        try_stream! {\n            let payload = Payload::try_from(bytes);\n\n            for elem in payload?.into_iter() {\n                yield elem;\n            }\n        }\n    }\n\n    /// Creates a stream over the incoming packets, uses the streams provided by the\n    /// underlying transport types.\n    fn stream(\n        mut transport: AsyncTransportType,\n    ) -> Pin<Box<impl Stream<Item = Result<Packet>> + 'static + Send>> {\n        // map the byte stream of the underlying transport\n        // to a packet stream\n        Box::pin(try_stream! {\n            for await payload in transport.as_pin_box() {\n                for await packet in Self::parse_payload(payload?) {\n                    yield packet?;\n                }\n            }\n        })\n    }\n\n    pub async fn disconnect(&self) -> Result<()> {\n        if let Some(on_close) = self.on_close.as_ref() {\n            let on_close = on_close.clone();\n            self.handle.spawn(async move { on_close(()).await });\n        }\n\n        self.emit(Packet::new(PacketId::Close, Bytes::new()))\n            .await?;\n\n        self.connected.store(false, Ordering::Release);\n\n        Ok(())\n    }\n\n    /// Sends a packet to the server.\n    pub async fn emit(&self, packet: Packet) -> Result<()> {\n        if !self.connected.load(Ordering::Acquire) {\n            let error = Error::IllegalActionBeforeOpen();\n            self.call_error_callback(format!(\"{}\", error));\n            return Err(error);\n        }\n\n        let is_binary = packet.packet_id == PacketId::MessageBinary;\n\n        // send a post request with the encoded payload as body\n        // if this is a binary attachment, then send the raw bytes\n        let data: Bytes = if is_binary {\n            packet.data\n        } else {\n            packet.into()\n        };\n\n        let lock = self.transport.lock().await;\n        let fut = lock.as_transport().emit(data, is_binary);\n\n        if let Err(error) = fut.await {\n            self.call_error_callback(error.to_string());\n            return Err(error);\n        }\n\n        Ok(())\n    }\n\n    /// Calls the error callback with a given message.\n    #[inline]\n    fn call_error_callback(&self, text: String) {\n        if let Some(on_error) = self.on_error.as_ref() {\n            let on_error = on_error.clone();\n            self.handle.spawn(async move { on_error(text).await });\n        }\n    }\n\n    // Check if the underlying transport client is connected.\n    pub(crate) fn is_connected(&self) -> bool {\n        self.connected.load(Ordering::Acquire)\n    }\n\n    pub(crate) async fn pinged(&self) {\n        *self.last_ping.lock().await = Instant::now();\n    }\n\n    /// Returns the time in milliseconds that is left until a new ping must be received.\n    /// This is used to detect whether we have been disconnected from the server.\n    /// See https://socket.io/docs/v4/how-it-works/#disconnection-detection\n    async fn time_to_next_ping(&self) -> u64 {\n        match Instant::now().checked_duration_since(*self.last_ping.lock().await) {\n            Some(since_last_ping) => {\n                let since_last_ping = since_last_ping.as_millis() as u64;\n                if since_last_ping > self.max_ping_timeout {\n                    0\n                } else {\n                    self.max_ping_timeout - since_last_ping\n                }\n            }\n            None => 0,\n        }\n    }\n\n    pub(crate) fn handle_packet(&self, packet: Packet) {\n        if let Some(on_packet) = self.on_packet.as_ref() {\n            let on_packet = on_packet.clone();\n            self.handle.spawn(async move { on_packet(packet).await });\n        }\n    }\n\n    pub(crate) fn handle_data(&self, data: Bytes) {\n        if let Some(on_data) = self.on_data.as_ref() {\n            let on_data = on_data.clone();\n            self.handle.spawn(async move { on_data(data).await });\n        }\n    }\n\n    pub(crate) fn handle_close(&self) {\n        if let Some(on_close) = self.on_close.as_ref() {\n            let on_close = on_close.clone();\n            self.handle.spawn(async move { on_close(()).await });\n        }\n\n        self.connected.store(false, Ordering::Release);\n    }\n\n    /// Returns the packet stream for the client.\n    pub(crate) fn as_stream<'a>(\n        &'a self,\n    ) -> Pin<Box<dyn Stream<Item = Result<Packet>> + Send + 'a>> {\n        stream::unfold(\n            Self::stream(self.transport_raw.clone()),\n            |mut stream| async {\n                // Wait for the next payload or until we should have received the next ping.\n                match tokio::time::timeout(\n                    std::time::Duration::from_millis(self.time_to_next_ping().await),\n                    stream.next(),\n                )\n                .await\n                {\n                    Ok(result) => result.map(|result| (result, stream)),\n                    // We didn't receive a ping in time and now consider the connection as closed.\n                    Err(_) => {\n                        // Be nice and disconnect properly.\n                        if let Err(e) = self.disconnect().await {\n                            Some((Err(e), stream))\n                        } else {\n                            Some((Err(Error::PingTimeout()), stream))\n                        }\n                    }\n                }\n            },\n        )\n        .boxed()\n    }\n}\n\n#[cfg_attr(tarpaulin, ignore)]\nimpl Debug for Socket {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Socket\")\n            .field(\"transport\", &self.transport)\n            .field(\"on_close\", &self.on_close)\n            .field(\"on_data\", &self.on_data)\n            .field(\"on_error\", &self.on_error)\n            .field(\"on_open\", &self.on_open)\n            .field(\"on_packet\", &self.on_packet)\n            .field(\"connected\", &self.connected)\n            .field(\"last_ping\", &self.last_ping)\n            .field(\"last_pong\", &self.last_pong)\n            .field(\"connection_data\", &self.connection_data)\n            .finish()\n    }\n}\n"
  },
  {
    "path": "engineio/src/asynchronous/async_transports/mod.rs",
    "content": "mod polling;\nmod websocket;\nmod websocket_general;\nmod websocket_secure;\n\npub use self::polling::PollingTransport;\npub use self::websocket::WebsocketTransport;\npub use self::websocket_secure::WebsocketSecureTransport;\n"
  },
  {
    "path": "engineio/src/asynchronous/async_transports/polling.rs",
    "content": "use adler32::adler32;\nuse async_stream::try_stream;\nuse async_trait::async_trait;\nuse base64::{engine::general_purpose, Engine as _};\nuse bytes::{BufMut, Bytes, BytesMut};\nuse futures_util::{Stream, StreamExt};\nuse http::HeaderMap;\nuse native_tls::TlsConnector;\nuse reqwest::{Client, ClientBuilder, Response};\nuse std::fmt::Debug;\nuse std::time::SystemTime;\nuse std::{pin::Pin, sync::Arc};\nuse tokio::sync::RwLock;\nuse url::Url;\n\nuse crate::asynchronous::generator::StreamGenerator;\nuse crate::{asynchronous::transport::AsyncTransport, error::Result, Error};\n\n/// An asynchronous polling type. Makes use of the nonblocking reqwest types and\n/// methods.\n#[derive(Clone)]\npub struct PollingTransport {\n    client: Client,\n    base_url: Arc<RwLock<Url>>,\n    generator: StreamGenerator<Bytes>,\n}\n\nimpl PollingTransport {\n    pub fn new(\n        base_url: Url,\n        tls_config: Option<TlsConnector>,\n        opening_headers: Option<HeaderMap>,\n    ) -> Self {\n        let client = match (tls_config, opening_headers) {\n            (Some(config), Some(map)) => ClientBuilder::new()\n                .use_preconfigured_tls(config)\n                .default_headers(map)\n                .build()\n                .unwrap(),\n            (Some(config), None) => ClientBuilder::new()\n                .use_preconfigured_tls(config)\n                .build()\n                .unwrap(),\n            (None, Some(map)) => ClientBuilder::new().default_headers(map).build().unwrap(),\n            (None, None) => Client::new(),\n        };\n\n        let mut url = base_url;\n        url.query_pairs_mut().append_pair(\"transport\", \"polling\");\n\n        PollingTransport {\n            client: client.clone(),\n            base_url: Arc::new(RwLock::new(url.clone())),\n            generator: StreamGenerator::new(Self::stream(url, client)),\n        }\n    }\n\n    fn address(mut url: Url) -> Result<Url> {\n        let reader = format!(\"{:#?}\", SystemTime::now());\n        let hash = adler32(reader.as_bytes()).unwrap();\n        url.query_pairs_mut().append_pair(\"t\", &hash.to_string());\n        Ok(url)\n    }\n\n    fn send_request(url: Url, client: Client) -> impl Stream<Item = Result<Response>> {\n        try_stream! {\n            let address = Self::address(url);\n\n            yield client\n                .get(address?)\n                .send().await?\n        }\n    }\n\n    fn stream(\n        url: Url,\n        client: Client,\n    ) -> Pin<Box<dyn Stream<Item = Result<Bytes>> + 'static + Send>> {\n        Box::pin(try_stream! {\n            loop {\n                for await elem in Self::send_request(url.clone(), client.clone()) {\n                    for await bytes in elem?.bytes_stream() {\n                        yield bytes?;\n                    }\n                }\n            }\n        })\n    }\n}\n\nimpl Stream for PollingTransport {\n    type Item = Result<Bytes>;\n\n    fn poll_next(\n        mut self: Pin<&mut Self>,\n        cx: &mut std::task::Context<'_>,\n    ) -> std::task::Poll<Option<Self::Item>> {\n        self.generator.poll_next_unpin(cx)\n    }\n}\n\n#[async_trait]\nimpl AsyncTransport for PollingTransport {\n    async fn emit(&self, data: Bytes, is_binary_att: bool) -> Result<()> {\n        let data_to_send = if is_binary_att {\n            // the binary attachment gets `base64` encoded\n            let mut packet_bytes = BytesMut::with_capacity(data.len() + 1);\n            packet_bytes.put_u8(b'b');\n\n            let encoded_data = general_purpose::STANDARD.encode(data);\n            packet_bytes.put(encoded_data.as_bytes());\n\n            packet_bytes.freeze()\n        } else {\n            data\n        };\n\n        let status = self\n            .client\n            .post(self.address().await?)\n            .body(data_to_send)\n            .send()\n            .await?\n            .status()\n            .as_u16();\n\n        if status != 200 {\n            let error = Error::IncompleteHttp(status);\n            return Err(error);\n        }\n\n        Ok(())\n    }\n\n    async fn base_url(&self) -> Result<Url> {\n        Ok(self.base_url.read().await.clone())\n    }\n\n    async fn set_base_url(&self, base_url: Url) -> Result<()> {\n        let mut url = base_url;\n        if !url\n            .query_pairs()\n            .any(|(k, v)| k == \"transport\" && v == \"polling\")\n        {\n            url.query_pairs_mut().append_pair(\"transport\", \"polling\");\n        }\n        *self.base_url.write().await = url;\n        Ok(())\n    }\n}\n\nimpl Debug for PollingTransport {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"PollingTransport\")\n            .field(\"client\", &self.client)\n            .field(\"base_url\", &self.base_url)\n            .finish()\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use crate::asynchronous::transport::AsyncTransport;\n\n    use super::*;\n    use std::str::FromStr;\n\n    #[tokio::test]\n    async fn polling_transport_base_url() -> Result<()> {\n        let url = crate::test::engine_io_server()?.to_string();\n        let transport = PollingTransport::new(Url::from_str(&url[..]).unwrap(), None, None);\n        assert_eq!(\n            transport.base_url().await?.to_string(),\n            url.clone() + \"?transport=polling\"\n        );\n        transport\n            .set_base_url(Url::parse(\"https://127.0.0.1\")?)\n            .await?;\n        assert_eq!(\n            transport.base_url().await?.to_string(),\n            \"https://127.0.0.1/?transport=polling\"\n        );\n        assert_ne!(transport.base_url().await?.to_string(), url);\n\n        transport\n            .set_base_url(Url::parse(\"http://127.0.0.1/?transport=polling\")?)\n            .await?;\n        assert_eq!(\n            transport.base_url().await?.to_string(),\n            \"http://127.0.0.1/?transport=polling\"\n        );\n        assert_ne!(transport.base_url().await?.to_string(), url);\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "engineio/src/asynchronous/async_transports/websocket.rs",
    "content": "use std::fmt::Debug;\nuse std::pin::Pin;\nuse std::sync::Arc;\n\nuse crate::asynchronous::transport::AsyncTransport;\nuse crate::error::Result;\nuse async_trait::async_trait;\nuse bytes::Bytes;\nuse futures_util::stream::StreamExt;\nuse futures_util::Stream;\nuse http::HeaderMap;\nuse tokio::sync::RwLock;\nuse tokio_tungstenite::connect_async;\nuse tungstenite::client::IntoClientRequest;\nuse url::Url;\n\nuse super::websocket_general::AsyncWebsocketGeneralTransport;\n\n/// An asynchronous websocket transport type.\n/// This type only allows for plain websocket\n/// connections (\"ws://\").\n#[derive(Clone)]\npub struct WebsocketTransport {\n    inner: AsyncWebsocketGeneralTransport,\n    base_url: Arc<RwLock<Url>>,\n}\n\nimpl WebsocketTransport {\n    /// Creates a new instance over a request that might hold additional headers and an URL.\n    pub async fn new(base_url: Url, headers: Option<HeaderMap>) -> Result<Self> {\n        let mut url = base_url;\n        url.query_pairs_mut().append_pair(\"transport\", \"websocket\");\n        url.set_scheme(\"ws\").unwrap();\n\n        let mut req = url.clone().into_client_request()?;\n        if let Some(map) = headers {\n            // SAFETY: this unwrap never panics as the underlying request is just initialized and in proper state\n            req.headers_mut().extend(map);\n        }\n\n        let (ws_stream, _) = connect_async(req).await?;\n        let (sen, rec) = ws_stream.split();\n\n        let inner = AsyncWebsocketGeneralTransport::new(sen, rec).await;\n        Ok(WebsocketTransport {\n            inner,\n            base_url: Arc::new(RwLock::new(url)),\n        })\n    }\n\n    /// Sends probe packet to ensure connection is valid, then sends upgrade\n    /// request\n    pub(crate) async fn upgrade(&self) -> Result<()> {\n        self.inner.upgrade().await\n    }\n\n    pub(crate) async fn poll_next(&self) -> Result<Option<Bytes>> {\n        self.inner.poll_next().await\n    }\n}\n\n#[async_trait]\nimpl AsyncTransport for WebsocketTransport {\n    async fn emit(&self, data: Bytes, is_binary_att: bool) -> Result<()> {\n        self.inner.emit(data, is_binary_att).await\n    }\n\n    async fn base_url(&self) -> Result<Url> {\n        Ok(self.base_url.read().await.clone())\n    }\n\n    async fn set_base_url(&self, base_url: Url) -> Result<()> {\n        let mut url = base_url;\n        if !url\n            .query_pairs()\n            .any(|(k, v)| k == \"transport\" && v == \"websocket\")\n        {\n            url.query_pairs_mut().append_pair(\"transport\", \"websocket\");\n        }\n        url.set_scheme(\"ws\").unwrap();\n        *self.base_url.write().await = url;\n        Ok(())\n    }\n}\n\nimpl Stream for WebsocketTransport {\n    type Item = Result<Bytes>;\n\n    fn poll_next(\n        mut self: Pin<&mut Self>,\n        cx: &mut std::task::Context<'_>,\n    ) -> std::task::Poll<Option<Self::Item>> {\n        self.inner.poll_next_unpin(cx)\n    }\n}\n\nimpl Debug for WebsocketTransport {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"AsyncWebsocketTransport\")\n            .field(\n                \"base_url\",\n                &self\n                    .base_url\n                    .try_read()\n                    .map_or(\"Currently not available\".to_owned(), |url| url.to_string()),\n            )\n            .finish()\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::ENGINE_IO_VERSION;\n    use std::str::FromStr;\n\n    async fn new() -> Result<WebsocketTransport> {\n        let url = crate::test::engine_io_server()?.to_string()\n            + \"engine.io/?EIO=\"\n            + &ENGINE_IO_VERSION.to_string();\n        WebsocketTransport::new(Url::from_str(&url[..])?, None).await\n    }\n\n    #[tokio::test]\n    async fn websocket_transport_base_url() -> Result<()> {\n        let transport = new().await?;\n        let mut url = crate::test::engine_io_server()?;\n        url.set_path(\"/engine.io/\");\n        url.query_pairs_mut()\n            .append_pair(\"EIO\", &ENGINE_IO_VERSION.to_string())\n            .append_pair(\"transport\", \"websocket\");\n        url.set_scheme(\"ws\").unwrap();\n        assert_eq!(transport.base_url().await?.to_string(), url.to_string());\n        transport\n            .set_base_url(reqwest::Url::parse(\"https://127.0.0.1\")?)\n            .await?;\n        assert_eq!(\n            transport.base_url().await?.to_string(),\n            \"ws://127.0.0.1/?transport=websocket\"\n        );\n        assert_ne!(transport.base_url().await?.to_string(), url.to_string());\n\n        transport\n            .set_base_url(reqwest::Url::parse(\n                \"http://127.0.0.1/?transport=websocket\",\n            )?)\n            .await?;\n        assert_eq!(\n            transport.base_url().await?.to_string(),\n            \"ws://127.0.0.1/?transport=websocket\"\n        );\n        assert_ne!(transport.base_url().await?.to_string(), url.to_string());\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn websocket_secure_debug() -> Result<()> {\n        let mut transport = new().await?;\n        assert_eq!(\n            format!(\"{:?}\", transport),\n            format!(\n                \"AsyncWebsocketTransport {{ base_url: {:?} }}\",\n                transport.base_url().await?.to_string()\n            )\n        );\n        println!(\"{:?}\", transport.next().await.unwrap());\n        println!(\"{:?}\", transport.next().await.unwrap());\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "engineio/src/asynchronous/async_transports/websocket_general.rs",
    "content": "use std::{borrow::Cow, str::from_utf8, sync::Arc, task::Poll};\n\nuse crate::{error::Result, Error, Packet, PacketId};\nuse bytes::{BufMut, Bytes, BytesMut};\nuse futures_util::{\n    ready,\n    stream::{SplitSink, SplitStream},\n    FutureExt, SinkExt, Stream, StreamExt,\n};\nuse tokio::{net::TcpStream, sync::Mutex};\nuse tokio_tungstenite::{MaybeTlsStream, WebSocketStream};\nuse tungstenite::Message;\n\ntype AsyncWebsocketSender = SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>;\ntype AsyncWebsocketReceiver = SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>;\n\n/// A general purpose asynchronous websocket transport type. Holds\n/// the sender and receiver stream of a websocket connection\n/// and implements the common methods `update` and `emit`. This also\n/// implements `Stream`.\n#[derive(Clone)]\npub(crate) struct AsyncWebsocketGeneralTransport {\n    sender: Arc<Mutex<AsyncWebsocketSender>>,\n    receiver: Arc<Mutex<AsyncWebsocketReceiver>>,\n}\n\nimpl AsyncWebsocketGeneralTransport {\n    pub(crate) async fn new(\n        sender: SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>,\n        receiver: SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>,\n    ) -> Self {\n        AsyncWebsocketGeneralTransport {\n            sender: Arc::new(Mutex::new(sender)),\n            receiver: Arc::new(Mutex::new(receiver)),\n        }\n    }\n\n    /// Sends probe packet to ensure connection is valid, then sends upgrade\n    /// request\n    pub(crate) async fn upgrade(&self) -> Result<()> {\n        let mut receiver = self.receiver.lock().await;\n        let mut sender = self.sender.lock().await;\n\n        sender\n            .send(Message::text(Cow::Borrowed(from_utf8(&Bytes::from(\n                Packet::new(PacketId::Ping, Bytes::from(\"probe\")),\n            ))?)))\n            .await?;\n\n        let msg = receiver\n            .next()\n            .await\n            .ok_or(Error::IllegalWebsocketUpgrade())??;\n\n        if msg.into_data() != Bytes::from(Packet::new(PacketId::Pong, Bytes::from(\"probe\"))) {\n            return Err(Error::InvalidPacket());\n        }\n\n        sender\n            .send(Message::text(Cow::Borrowed(from_utf8(&Bytes::from(\n                Packet::new(PacketId::Upgrade, Bytes::from(\"\")),\n            ))?)))\n            .await?;\n\n        Ok(())\n    }\n\n    pub(crate) async fn emit(&self, data: Bytes, is_binary_att: bool) -> Result<()> {\n        let mut sender = self.sender.lock().await;\n\n        let message = if is_binary_att {\n            Message::binary(Cow::Borrowed(data.as_ref()))\n        } else {\n            Message::text(Cow::Borrowed(std::str::from_utf8(data.as_ref())?))\n        };\n\n        sender.send(message).await?;\n\n        Ok(())\n    }\n\n    pub(crate) async fn poll_next(&self) -> Result<Option<Bytes>> {\n        loop {\n            let mut receiver = self.receiver.lock().await;\n            let next = receiver.next().await;\n            match next {\n                Some(Ok(Message::Text(str))) => return Ok(Some(Bytes::from(str))),\n                Some(Ok(Message::Binary(data))) => {\n                    let mut msg = BytesMut::with_capacity(data.len() + 1);\n                    msg.put_u8(PacketId::Message as u8);\n                    msg.put(data.as_ref());\n\n                    return Ok(Some(msg.freeze()));\n                }\n                // ignore packets other than text and binary\n                Some(Ok(_)) => (),\n                Some(Err(err)) => return Err(err.into()),\n                None => return Ok(None),\n            }\n        }\n    }\n}\n\nimpl Stream for AsyncWebsocketGeneralTransport {\n    type Item = Result<Bytes>;\n\n    fn poll_next(\n        self: std::pin::Pin<&mut Self>,\n        cx: &mut std::task::Context<'_>,\n    ) -> std::task::Poll<Option<Self::Item>> {\n        loop {\n            let mut lock = ready!(Box::pin(self.receiver.lock()).poll_unpin(cx));\n            let next = ready!(lock.poll_next_unpin(cx));\n\n            match next {\n                Some(Ok(Message::Text(str))) => return Poll::Ready(Some(Ok(Bytes::from(str)))),\n                Some(Ok(Message::Binary(data))) => {\n                    let mut msg = BytesMut::with_capacity(data.len() + 1);\n                    msg.put_u8(PacketId::Message as u8);\n                    msg.put(data.as_ref());\n\n                    return Poll::Ready(Some(Ok(msg.freeze())));\n                }\n                // ignore packets other than text and binary\n                Some(Ok(_)) => (),\n                Some(Err(err)) => return Poll::Ready(Some(Err(err.into()))),\n                None => return Poll::Ready(None),\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "engineio/src/asynchronous/async_transports/websocket_secure.rs",
    "content": "use std::fmt::Debug;\nuse std::pin::Pin;\nuse std::sync::Arc;\n\nuse crate::asynchronous::transport::AsyncTransport;\nuse crate::error::Result;\nuse async_trait::async_trait;\nuse bytes::Bytes;\nuse futures_util::Stream;\nuse futures_util::StreamExt;\nuse http::HeaderMap;\nuse native_tls::TlsConnector;\nuse tokio::sync::RwLock;\nuse tokio_tungstenite::connect_async_tls_with_config;\nuse tokio_tungstenite::Connector;\nuse tungstenite::client::IntoClientRequest;\nuse url::Url;\n\nuse super::websocket_general::AsyncWebsocketGeneralTransport;\n\n/// An asynchronous websocket transport type.\n/// This type only allows for secure websocket\n/// connections (\"wss://\").\n#[derive(Clone)]\npub struct WebsocketSecureTransport {\n    inner: AsyncWebsocketGeneralTransport,\n    base_url: Arc<RwLock<Url>>,\n}\n\nimpl WebsocketSecureTransport {\n    /// Creates a new instance over a request that might hold additional headers, a possible\n    /// Tls connector and an URL.\n    pub(crate) async fn new(\n        base_url: Url,\n        tls_config: Option<TlsConnector>,\n        headers: Option<HeaderMap>,\n    ) -> Result<Self> {\n        let mut url = base_url;\n        url.query_pairs_mut().append_pair(\"transport\", \"websocket\");\n        url.set_scheme(\"wss\").unwrap();\n\n        let mut req = url.clone().into_client_request()?;\n        if let Some(map) = headers {\n            // SAFETY: this unwrap never panics as the underlying request is just initialized and in proper state\n            req.headers_mut().extend(map);\n        }\n\n        // `disable_nagle` Sets the value of the TCP_NODELAY option on this socket.\n        //\n        // If set to `true`, this option disables the Nagle algorithm.\n        // This means that segments are always sent as soon as possible, even if there is only a small amount of data.\n        // When `false`, data is buffered until there is a sufficient amount to send out, thereby avoiding the frequent sending of small packets.\n        //\n        // See the docs: https://docs.rs/tokio/latest/tokio/net/struct.TcpStream.html#method.set_nodelay\n        let (ws_stream, _) = connect_async_tls_with_config(\n            req,\n            None,\n            /*disable_nagle=*/ false,\n            tls_config.map(Connector::NativeTls),\n        )\n        .await?;\n\n        let (sen, rec) = ws_stream.split();\n        let inner = AsyncWebsocketGeneralTransport::new(sen, rec).await;\n\n        Ok(WebsocketSecureTransport {\n            inner,\n            base_url: Arc::new(RwLock::new(url)),\n        })\n    }\n\n    /// Sends probe packet to ensure connection is valid, then sends upgrade\n    /// request\n    pub(crate) async fn upgrade(&self) -> Result<()> {\n        self.inner.upgrade().await\n    }\n\n    pub(crate) async fn poll_next(&self) -> Result<Option<Bytes>> {\n        self.inner.poll_next().await\n    }\n}\n\nimpl Stream for WebsocketSecureTransport {\n    type Item = Result<Bytes>;\n\n    fn poll_next(\n        mut self: Pin<&mut Self>,\n        cx: &mut std::task::Context<'_>,\n    ) -> std::task::Poll<Option<Self::Item>> {\n        self.inner.poll_next_unpin(cx)\n    }\n}\n\n#[async_trait]\nimpl AsyncTransport for WebsocketSecureTransport {\n    async fn emit(&self, data: Bytes, is_binary_att: bool) -> Result<()> {\n        self.inner.emit(data, is_binary_att).await\n    }\n\n    async fn base_url(&self) -> Result<Url> {\n        Ok(self.base_url.read().await.clone())\n    }\n\n    async fn set_base_url(&self, base_url: Url) -> Result<()> {\n        let mut url = base_url;\n        if !url\n            .query_pairs()\n            .any(|(k, v)| k == \"transport\" && v == \"websocket\")\n        {\n            url.query_pairs_mut().append_pair(\"transport\", \"websocket\");\n        }\n        url.set_scheme(\"wss\").unwrap();\n        *self.base_url.write().await = url;\n        Ok(())\n    }\n}\n\nimpl Debug for WebsocketSecureTransport {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"AsyncWebsocketSecureTransport\")\n            .field(\n                \"base_url\",\n                &self\n                    .base_url\n                    .try_read()\n                    .map_or(\"Currently not available\".to_owned(), |url| url.to_string()),\n            )\n            .finish()\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::ENGINE_IO_VERSION;\n    use std::str::FromStr;\n\n    async fn new() -> Result<WebsocketSecureTransport> {\n        let url = crate::test::engine_io_server_secure()?.to_string()\n            + \"engine.io/?EIO=\"\n            + &ENGINE_IO_VERSION.to_string();\n        WebsocketSecureTransport::new(\n            Url::from_str(&url[..])?,\n            Some(crate::test::tls_connector()?),\n            None,\n        )\n        .await\n    }\n\n    #[tokio::test]\n    async fn websocket_secure_transport_base_url() -> Result<()> {\n        let transport = new().await?;\n        let mut url = crate::test::engine_io_server_secure()?;\n        url.set_path(\"/engine.io/\");\n        url.query_pairs_mut()\n            .append_pair(\"EIO\", &ENGINE_IO_VERSION.to_string())\n            .append_pair(\"transport\", \"websocket\");\n        url.set_scheme(\"wss\").unwrap();\n        assert_eq!(transport.base_url().await?.to_string(), url.to_string());\n        transport\n            .set_base_url(reqwest::Url::parse(\"https://127.0.0.1\")?)\n            .await?;\n        assert_eq!(\n            transport.base_url().await?.to_string(),\n            \"wss://127.0.0.1/?transport=websocket\"\n        );\n        assert_ne!(transport.base_url().await?.to_string(), url.to_string());\n\n        transport\n            .set_base_url(reqwest::Url::parse(\n                \"http://127.0.0.1/?transport=websocket\",\n            )?)\n            .await?;\n        assert_eq!(\n            transport.base_url().await?.to_string(),\n            \"wss://127.0.0.1/?transport=websocket\"\n        );\n        assert_ne!(transport.base_url().await?.to_string(), url.to_string());\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn websocket_secure_debug() -> Result<()> {\n        let transport = new().await?;\n        assert_eq!(\n            format!(\"{:?}\", transport),\n            format!(\n                \"AsyncWebsocketSecureTransport {{ base_url: {:?} }}\",\n                transport.base_url().await?.to_string()\n            )\n        );\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "engineio/src/asynchronous/callback.rs",
    "content": "use bytes::Bytes;\nuse futures_util::future::BoxFuture;\nuse std::{fmt::Debug, ops::Deref, sync::Arc};\n\nuse crate::Packet;\n\n/// Internal type, provides a way to store futures and return them in a boxed manner.\npub(crate) type DynAsyncCallback<I> = dyn 'static + Send + Sync + Fn(I) -> BoxFuture<'static, ()>;\n\n/// Internal type, might hold an async callback.\n#[derive(Clone)]\npub(crate) struct OptionalCallback<I> {\n    inner: Option<Arc<DynAsyncCallback<I>>>,\n}\n\nimpl<I> OptionalCallback<I> {\n    pub(crate) fn new<T>(callback: T) -> Self\n    where\n        T: 'static + Send + Sync + Fn(I) -> BoxFuture<'static, ()>,\n    {\n        OptionalCallback {\n            inner: Some(Arc::new(callback)),\n        }\n    }\n\n    pub(crate) fn default() -> Self {\n        OptionalCallback { inner: None }\n    }\n}\n\n#[cfg_attr(tarpaulin, ignore)]\nimpl Debug for OptionalCallback<String> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {\n        f.write_fmt(format_args!(\n            \"Callback({:?})\",\n            if self.inner.is_some() {\n                \"Fn(String)\"\n            } else {\n                \"None\"\n            }\n        ))\n    }\n}\n\n#[cfg_attr(tarpaulin, ignore)]\nimpl Debug for OptionalCallback<()> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {\n        f.write_fmt(format_args!(\n            \"Callback({:?})\",\n            if self.inner.is_some() {\n                \"Fn(())\"\n            } else {\n                \"None\"\n            }\n        ))\n    }\n}\n\n#[cfg_attr(tarpaulin, ignore)]\nimpl Debug for OptionalCallback<Packet> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {\n        f.write_fmt(format_args!(\n            \"Callback({:?})\",\n            if self.inner.is_some() {\n                \"Fn(Packet)\"\n            } else {\n                \"None\"\n            }\n        ))\n    }\n}\n\n#[cfg_attr(tarpaulin, ignore)]\nimpl Debug for OptionalCallback<Bytes> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {\n        f.write_fmt(format_args!(\n            \"Callback({:?})\",\n            if self.inner.is_some() {\n                \"Fn(Bytes)\"\n            } else {\n                \"None\"\n            }\n        ))\n    }\n}\n\nimpl<I> Deref for OptionalCallback<I> {\n    type Target = Option<Arc<DynAsyncCallback<I>>>;\n    fn deref(&self) -> &<Self as std::ops::Deref>::Target {\n        &self.inner\n    }\n}\n"
  },
  {
    "path": "engineio/src/asynchronous/client/async_client.rs",
    "content": "use std::{fmt::Debug, pin::Pin};\n\nuse crate::{\n    asynchronous::{async_socket::Socket as InnerSocket, generator::StreamGenerator},\n    error::Result,\n    Packet,\n};\nuse async_stream::try_stream;\nuse futures_util::{Stream, StreamExt};\n\n/// An engine.io client that allows interaction with the connected engine.io\n/// server. This client provides means for connecting, disconnecting and sending\n/// packets to the server.\n///\n/// ## Note:\n/// There is no need to put this Client behind an `Arc`, as the type uses `Arc`\n/// internally and provides a shared state beyond all cloned instances.\n#[derive(Clone)]\npub struct Client {\n    pub(super) socket: InnerSocket,\n    generator: StreamGenerator<Packet>,\n}\n\nimpl Client {\n    pub(super) fn new(socket: InnerSocket) -> Self {\n        Client {\n            socket: socket.clone(),\n            generator: StreamGenerator::new(Self::stream(socket)),\n        }\n    }\n\n    pub async fn close(&self) -> Result<()> {\n        self.socket.disconnect().await\n    }\n\n    /// Opens the connection to a specified server. The first Pong packet is sent\n    /// to the server to trigger the Ping-cycle.\n    pub async fn connect(&self) -> Result<()> {\n        self.socket.connect().await\n    }\n\n    /// Disconnects the connection.\n    pub async fn disconnect(&self) -> Result<()> {\n        self.socket.disconnect().await\n    }\n\n    /// Sends a packet to the server.\n    pub async fn emit(&self, packet: Packet) -> Result<()> {\n        self.socket.emit(packet).await\n    }\n\n    /// Static method that returns a generator for each element of the stream.\n    fn stream(\n        socket: InnerSocket,\n    ) -> Pin<Box<impl Stream<Item = Result<Packet>> + 'static + Send>> {\n        Box::pin(try_stream! {\n            let socket = socket.clone();\n            for await item in socket.as_stream() {\n                let packet = item?;\n                socket.handle_incoming_packet(packet.clone()).await?;\n                yield packet;\n            }\n        })\n    }\n\n    /// Check if the underlying transport client is connected.\n    pub fn is_connected(&self) -> bool {\n        self.socket.is_connected()\n    }\n}\n\nimpl Stream for Client {\n    type Item = Result<Packet>;\n\n    fn poll_next(\n        mut self: Pin<&mut Self>,\n        cx: &mut std::task::Context<'_>,\n    ) -> std::task::Poll<Option<Self::Item>> {\n        self.generator.poll_next_unpin(cx)\n    }\n}\n\nimpl Debug for Client {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Client\")\n            .field(\"socket\", &self.socket)\n            .finish()\n    }\n}\n\n#[cfg(all(test))]\nmod test {\n\n    use super::*;\n    use crate::{asynchronous::ClientBuilder, header::HeaderMap, packet::PacketId, Error};\n    use bytes::Bytes;\n    use futures_util::StreamExt;\n    use native_tls::TlsConnector;\n    use url::Url;\n\n    /// The purpose of this test is to check whether the Client is properly cloneable or not.\n    /// As the documentation of the engine.io client states, the object needs to maintain it's internal\n    /// state when cloned and the cloned object should reflect the same state throughout the lifetime\n    /// of both objects (initial and cloned).\n    #[tokio::test]\n    async fn test_client_cloneable() -> Result<()> {\n        let url = crate::test::engine_io_server()?;\n\n        let mut sut = builder(url).build().await?;\n        let mut cloned = sut.clone();\n\n        sut.connect().await?;\n\n        // when the underlying socket is connected, the\n        // state should also change on the cloned one\n        assert!(sut.is_connected());\n        assert!(cloned.is_connected());\n\n        // both clients should reflect the same messages.\n        assert_eq!(\n            sut.next().await.unwrap()?,\n            Packet::new(PacketId::Message, \"hello client\")\n        );\n\n        sut.emit(Packet::new(PacketId::Message, \"respond\")).await?;\n\n        assert_eq!(\n            cloned.next().await.unwrap()?,\n            Packet::new(PacketId::Message, \"Roger Roger\")\n        );\n\n        cloned.disconnect().await?;\n\n        // when the underlying socket is disconnected, the\n        // state should also change on the cloned one\n        assert!(!sut.is_connected());\n        assert!(!cloned.is_connected());\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn test_illegal_actions() -> Result<()> {\n        let url = crate::test::engine_io_server()?;\n        let mut sut = builder(url.clone()).build().await?;\n\n        assert!(sut\n            .emit(Packet::new(PacketId::Close, Bytes::new()))\n            .await\n            .is_err());\n\n        sut.connect().await?;\n\n        assert!(sut.next().await.unwrap().is_ok());\n\n        assert!(builder(Url::parse(\"fake://fake.fake\").unwrap())\n            .build_websocket()\n            .await\n            .is_err());\n\n        sut.disconnect().await?;\n\n        Ok(())\n    }\n\n    use reqwest::header::HOST;\n\n    use crate::packet::Packet;\n\n    fn builder(url: Url) -> ClientBuilder {\n        ClientBuilder::new(url)\n            .on_open(|_| {\n                Box::pin(async {\n                    println!(\"Open event!\");\n                })\n            })\n            .on_packet(|packet| {\n                Box::pin(async move {\n                    println!(\"Received packet: {:?}\", packet);\n                })\n            })\n            .on_data(|data| {\n                Box::pin(async move {\n                    println!(\"Received data: {:?}\", std::str::from_utf8(&data));\n                })\n            })\n            .on_close(|_| {\n                Box::pin(async {\n                    println!(\"Close event!\");\n                })\n            })\n            .on_error(|error| {\n                Box::pin(async move {\n                    println!(\"Error {}\", error);\n                })\n            })\n    }\n\n    async fn test_connection(socket: Client) -> Result<()> {\n        let mut socket = socket;\n\n        socket.connect().await.unwrap();\n\n        assert_eq!(\n            socket.next().await.unwrap()?,\n            Packet::new(PacketId::Message, \"hello client\")\n        );\n        println!(\"received msg, about to send\");\n\n        socket\n            .emit(Packet::new(PacketId::Message, \"respond\"))\n            .await?;\n\n        println!(\"send msg\");\n\n        assert_eq!(\n            socket.next().await.unwrap()?,\n            Packet::new(PacketId::Message, \"Roger Roger\")\n        );\n        println!(\"received 2\");\n\n        socket.close().await\n    }\n\n    #[tokio::test]\n    async fn test_connection_long() -> Result<()> {\n        // Long lived socket to receive pings\n        let url = crate::test::engine_io_server()?;\n        let mut socket = builder(url).build().await?;\n\n        socket.connect().await?;\n\n        // hello client\n        assert!(matches!(\n            socket.next().await.unwrap()?,\n            Packet {\n                packet_id: PacketId::Message,\n                ..\n            }\n        ));\n        // Ping\n        assert!(matches!(\n            socket.next().await.unwrap()?,\n            Packet {\n                packet_id: PacketId::Ping,\n                ..\n            }\n        ));\n\n        socket.disconnect().await?;\n\n        assert!(!socket.is_connected());\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn test_connection_dynamic() -> Result<()> {\n        let url = crate::test::engine_io_server()?;\n        let socket = builder(url).build().await?;\n        test_connection(socket).await?;\n\n        let url = crate::test::engine_io_polling_server()?;\n        let socket = builder(url).build().await?;\n        test_connection(socket).await\n    }\n\n    #[tokio::test]\n    async fn test_connection_fallback() -> Result<()> {\n        let url = crate::test::engine_io_server()?;\n        let socket = builder(url).build_with_fallback().await?;\n        test_connection(socket).await?;\n\n        let url = crate::test::engine_io_polling_server()?;\n        let socket = builder(url).build_with_fallback().await?;\n        test_connection(socket).await\n    }\n\n    #[tokio::test]\n    async fn test_connection_dynamic_secure() -> Result<()> {\n        let url = crate::test::engine_io_server_secure()?;\n        let mut socket_builder = builder(url);\n        socket_builder = socket_builder.tls_config(crate::test::tls_connector()?);\n        let socket = socket_builder.build().await?;\n        test_connection(socket).await\n    }\n\n    #[tokio::test]\n    async fn test_connection_polling() -> Result<()> {\n        let url = crate::test::engine_io_server()?;\n        let socket = builder(url).build_polling().await?;\n        test_connection(socket).await\n    }\n\n    #[tokio::test]\n    async fn test_connection_wss() -> Result<()> {\n        let url = crate::test::engine_io_polling_server()?;\n        assert!(builder(url).build_websocket_with_upgrade().await.is_err());\n\n        let host =\n            std::env::var(\"ENGINE_IO_SECURE_HOST\").unwrap_or_else(|_| \"localhost\".to_owned());\n        let mut url = crate::test::engine_io_server_secure()?;\n\n        let mut headers = HeaderMap::default();\n        headers.insert(HOST, host);\n        let mut builder = builder(url.clone());\n\n        builder = builder.tls_config(crate::test::tls_connector()?);\n        builder = builder.headers(headers.clone());\n        let socket = builder.clone().build_websocket_with_upgrade().await?;\n\n        test_connection(socket).await?;\n\n        let socket = builder.build_websocket().await?;\n\n        test_connection(socket).await?;\n\n        url.set_scheme(\"wss\").unwrap();\n\n        let builder = self::builder(url)\n            .tls_config(crate::test::tls_connector()?)\n            .headers(headers);\n        let socket = builder.clone().build_websocket().await?;\n\n        test_connection(socket).await?;\n\n        assert!(builder.build_websocket_with_upgrade().await.is_err());\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn test_connection_ws() -> Result<()> {\n        let url = crate::test::engine_io_polling_server()?;\n        assert!(builder(url.clone()).build_websocket().await.is_err());\n        assert!(builder(url).build_websocket_with_upgrade().await.is_err());\n\n        let mut url = crate::test::engine_io_server()?;\n\n        let builder = builder(url.clone());\n        let socket = builder.clone().build_websocket().await?;\n        test_connection(socket).await?;\n\n        let socket = builder.build_websocket_with_upgrade().await?;\n        test_connection(socket).await?;\n\n        url.set_scheme(\"ws\").unwrap();\n\n        let builder = self::builder(url);\n        let socket = builder.clone().build_websocket().await?;\n\n        test_connection(socket).await?;\n\n        assert!(builder.build_websocket_with_upgrade().await.is_err());\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn test_open_invariants() -> Result<()> {\n        let url = crate::test::engine_io_server()?;\n        let illegal_url = \"this is illegal\";\n\n        assert!(Url::parse(illegal_url).is_err());\n\n        let invalid_protocol = \"file:///tmp/foo\";\n        assert!(builder(Url::parse(invalid_protocol).unwrap())\n            .build()\n            .await\n            .is_err());\n\n        let sut = builder(url.clone()).build().await?;\n        let _error = sut\n            .emit(Packet::new(PacketId::Close, Bytes::new()))\n            .await\n            .expect_err(\"error\");\n        assert!(matches!(Error::IllegalActionBeforeOpen(), _error));\n\n        // test missing match arm in socket constructor\n        let mut headers = HeaderMap::default();\n        let host =\n            std::env::var(\"ENGINE_IO_SECURE_HOST\").unwrap_or_else(|_| \"localhost\".to_owned());\n        headers.insert(HOST, host);\n\n        let _ = builder(url.clone())\n            .tls_config(\n                TlsConnector::builder()\n                    .danger_accept_invalid_certs(true)\n                    .build()\n                    .unwrap(),\n            )\n            .build()\n            .await?;\n        let _ = builder(url).headers(headers).build().await?;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "engineio/src/asynchronous/client/builder.rs",
    "content": "use crate::{\n    asynchronous::{\n        async_socket::Socket as InnerSocket,\n        async_transports::{PollingTransport, WebsocketSecureTransport, WebsocketTransport},\n        callback::OptionalCallback,\n        transport::AsyncTransport,\n    },\n    error::Result,\n    header::HeaderMap,\n    packet::HandshakePacket,\n    Error, Packet, ENGINE_IO_VERSION,\n};\nuse bytes::Bytes;\nuse futures_util::{future::BoxFuture, StreamExt};\nuse native_tls::TlsConnector;\nuse url::Url;\n\nuse super::Client;\n\n#[derive(Clone, Debug)]\npub struct ClientBuilder {\n    url: Url,\n    tls_config: Option<TlsConnector>,\n    headers: Option<HeaderMap>,\n    handshake: Option<HandshakePacket>,\n    on_error: OptionalCallback<String>,\n    on_open: OptionalCallback<()>,\n    on_close: OptionalCallback<()>,\n    on_data: OptionalCallback<Bytes>,\n    on_packet: OptionalCallback<Packet>,\n}\n\nimpl ClientBuilder {\n    pub fn new(url: Url) -> Self {\n        let mut url = url;\n        url.query_pairs_mut()\n            .append_pair(\"EIO\", &ENGINE_IO_VERSION.to_string());\n\n        // No path add engine.io\n        if url.path() == \"/\" {\n            url.set_path(\"/engine.io/\");\n        }\n        ClientBuilder {\n            url,\n            headers: None,\n            tls_config: None,\n            handshake: None,\n            on_close: OptionalCallback::default(),\n            on_data: OptionalCallback::default(),\n            on_error: OptionalCallback::default(),\n            on_open: OptionalCallback::default(),\n            on_packet: OptionalCallback::default(),\n        }\n    }\n\n    /// Specify transport's tls config\n    pub fn tls_config(mut self, tls_config: TlsConnector) -> Self {\n        self.tls_config = Some(tls_config);\n        self\n    }\n\n    /// Specify transport's HTTP headers\n    pub fn headers(mut self, headers: HeaderMap) -> Self {\n        self.headers = Some(headers);\n        self\n    }\n\n    /// Registers the `on_close` callback.\n    #[cfg(feature = \"async-callbacks\")]\n    pub fn on_close<T>(mut self, callback: T) -> Self\n    where\n        T: 'static + Send + Sync + Fn(()) -> BoxFuture<'static, ()>,\n    {\n        self.on_close = OptionalCallback::new(callback);\n        self\n    }\n\n    /// Registers the `on_data` callback.\n    #[cfg(feature = \"async-callbacks\")]\n    pub fn on_data<T>(mut self, callback: T) -> Self\n    where\n        T: 'static + Send + Sync + Fn(Bytes) -> BoxFuture<'static, ()>,\n    {\n        self.on_data = OptionalCallback::new(callback);\n        self\n    }\n\n    /// Registers the `on_error` callback.\n    #[cfg(feature = \"async-callbacks\")]\n    pub fn on_error<T>(mut self, callback: T) -> Self\n    where\n        T: 'static + Send + Sync + Fn(String) -> BoxFuture<'static, ()>,\n    {\n        self.on_error = OptionalCallback::new(callback);\n        self\n    }\n\n    /// Registers the `on_open` callback.\n    #[cfg(feature = \"async-callbacks\")]\n    pub fn on_open<T>(mut self, callback: T) -> Self\n    where\n        T: 'static + Send + Sync + Fn(()) -> BoxFuture<'static, ()>,\n    {\n        self.on_open = OptionalCallback::new(callback);\n        self\n    }\n\n    /// Registers the `on_packet` callback.\n    #[cfg(feature = \"async-callbacks\")]\n    pub fn on_packet<T>(mut self, callback: T) -> Self\n    where\n        T: 'static + Send + Sync + Fn(Packet) -> BoxFuture<'static, ()>,\n    {\n        self.on_packet = OptionalCallback::new(callback);\n        self\n    }\n\n    /// Performs the handshake\n    async fn handshake_with_transport<T: AsyncTransport + Unpin>(\n        &mut self,\n        transport: &mut T,\n    ) -> Result<()> {\n        // No need to handshake twice\n        if self.handshake.is_some() {\n            return Ok(());\n        }\n\n        let mut url = self.url.clone();\n\n        let handshake: HandshakePacket =\n            Packet::try_from(transport.next().await.ok_or(Error::IncompletePacket())??)?\n                .try_into()?;\n\n        // update the base_url with the new sid\n        url.query_pairs_mut().append_pair(\"sid\", &handshake.sid[..]);\n\n        self.handshake = Some(handshake);\n\n        self.url = url;\n\n        Ok(())\n    }\n\n    async fn handshake(&mut self) -> Result<()> {\n        if self.handshake.is_some() {\n            return Ok(());\n        }\n\n        let headers = if let Some(map) = self.headers.clone() {\n            Some(map.try_into()?)\n        } else {\n            None\n        };\n\n        // Start with polling transport\n        let mut transport =\n            PollingTransport::new(self.url.clone(), self.tls_config.clone(), headers);\n\n        self.handshake_with_transport(&mut transport).await\n    }\n\n    /// Build websocket if allowed, if not fall back to polling\n    pub async fn build(mut self) -> Result<Client> {\n        self.handshake().await?;\n\n        if self.websocket_upgrade()? {\n            self.build_websocket_with_upgrade().await\n        } else {\n            self.build_polling().await\n        }\n    }\n\n    /// Build socket with polling transport\n    pub async fn build_polling(mut self) -> Result<Client> {\n        self.handshake().await?;\n\n        // Make a polling transport with new sid\n        let transport = PollingTransport::new(\n            self.url,\n            self.tls_config,\n            self.headers.map(|v| v.try_into().unwrap()),\n        );\n\n        // SAFETY: handshake function called previously.\n        Ok(Client::new(InnerSocket::new(\n            transport.into(),\n            self.handshake.unwrap(),\n            self.on_close,\n            self.on_data,\n            self.on_error,\n            self.on_open,\n            self.on_packet,\n        )))\n    }\n\n    /// Build socket with a polling transport then upgrade to websocket transport\n    pub async fn build_websocket_with_upgrade(mut self) -> Result<Client> {\n        self.handshake().await?;\n\n        if self.websocket_upgrade()? {\n            self.build_websocket().await\n        } else {\n            Err(Error::IllegalWebsocketUpgrade())\n        }\n    }\n\n    /// Build socket with only a websocket transport\n    pub async fn build_websocket(mut self) -> Result<Client> {\n        let headers = if let Some(map) = self.headers.clone() {\n            Some(map.try_into()?)\n        } else {\n            None\n        };\n\n        match self.url.scheme() {\n            \"http\" | \"ws\" => {\n                let mut transport = WebsocketTransport::new(self.url.clone(), headers).await?;\n\n                if self.handshake.is_some() {\n                    transport.upgrade().await?;\n                } else {\n                    self.handshake_with_transport(&mut transport).await?;\n                }\n                // NOTE: Although self.url contains the sid, it does not propagate to the transport\n                // SAFETY: handshake function called previously.\n                Ok(Client::new(InnerSocket::new(\n                    transport.into(),\n                    self.handshake.unwrap(),\n                    self.on_close,\n                    self.on_data,\n                    self.on_error,\n                    self.on_open,\n                    self.on_packet,\n                )))\n            }\n            \"https\" | \"wss\" => {\n                let mut transport = WebsocketSecureTransport::new(\n                    self.url.clone(),\n                    self.tls_config.clone(),\n                    headers,\n                )\n                .await?;\n\n                if self.handshake.is_some() {\n                    transport.upgrade().await?;\n                } else {\n                    self.handshake_with_transport(&mut transport).await?;\n                }\n                // NOTE: Although self.url contains the sid, it does not propagate to the transport\n                // SAFETY: handshake function called previously.\n                Ok(Client::new(InnerSocket::new(\n                    transport.into(),\n                    self.handshake.unwrap(),\n                    self.on_close,\n                    self.on_data,\n                    self.on_error,\n                    self.on_open,\n                    self.on_packet,\n                )))\n            }\n            _ => Err(Error::InvalidUrlScheme(self.url.scheme().to_string())),\n        }\n    }\n\n    /// Build websocket if allowed, if not allowed or errored fall back to polling.\n    /// WARNING: websocket errors suppressed, no indication of websocket success or failure.\n    pub async fn build_with_fallback(self) -> Result<Client> {\n        let result = self.clone().build().await;\n        if result.is_err() {\n            self.build_polling().await\n        } else {\n            result\n        }\n    }\n\n    /// Checks the handshake to see if websocket upgrades are allowed\n    fn websocket_upgrade(&mut self) -> Result<bool> {\n        if self.handshake.is_none() {\n            return Ok(false);\n        }\n\n        Ok(self\n            .handshake\n            .as_ref()\n            .unwrap()\n            .upgrades\n            .iter()\n            .any(|upgrade| upgrade.to_lowercase() == *\"websocket\"))\n    }\n}\n"
  },
  {
    "path": "engineio/src/asynchronous/client/mod.rs",
    "content": "mod async_client;\nmod builder;\npub use async_client::Client;\npub use builder::ClientBuilder;\n"
  },
  {
    "path": "engineio/src/asynchronous/generator.rs",
    "content": "use std::{pin::Pin, sync::Arc};\n\nuse crate::error::Result;\nuse futures_util::{ready, FutureExt, Stream, StreamExt};\nuse tokio::sync::Mutex;\n\n/// A generator is an internal type that represents a [`Send`] [`futures_util::Stream`]\n/// that yields a certain type `T` whenever it's polled.\npub(crate) type Generator<T> = Pin<Box<dyn Stream<Item = T> + 'static + Send>>;\n\n/// An internal type that implements stream by repeatedly calling [`Stream::poll_next`] on an\n/// underlying stream. Note that the generic parameter will be wrapped in a [`Result`].\n#[derive(Clone)]\npub(crate) struct StreamGenerator<T> {\n    inner: Arc<Mutex<Generator<Result<T>>>>,\n}\n\nimpl<T> Stream for StreamGenerator<T> {\n    type Item = Result<T>;\n\n    fn poll_next(\n        self: Pin<&mut Self>,\n        cx: &mut std::task::Context<'_>,\n    ) -> std::task::Poll<Option<Self::Item>> {\n        let mut lock = ready!(Box::pin(self.inner.lock()).poll_unpin(cx));\n        lock.poll_next_unpin(cx)\n    }\n}\n\nimpl<T> StreamGenerator<T> {\n    pub(crate) fn new(generator_stream: Generator<Result<T>>) -> Self {\n        StreamGenerator {\n            inner: Arc::new(Mutex::new(generator_stream)),\n        }\n    }\n}\n"
  },
  {
    "path": "engineio/src/asynchronous/mod.rs",
    "content": "pub mod async_transports;\npub mod transport;\n\npub(self) mod async_socket;\n#[cfg(feature = \"async-callbacks\")]\nmod callback;\n#[cfg(feature = \"async\")]\npub mod client;\nmod generator;\n\n#[cfg(feature = \"async\")]\npub use client::Client;\n\n#[cfg(feature = \"async\")]\npub use client::ClientBuilder;\n"
  },
  {
    "path": "engineio/src/asynchronous/transport.rs",
    "content": "use crate::error::Result;\nuse adler32::adler32;\nuse async_trait::async_trait;\nuse bytes::Bytes;\nuse futures_util::Stream;\nuse std::{pin::Pin, time::SystemTime};\nuse url::Url;\n\nuse super::async_transports::{PollingTransport, WebsocketSecureTransport, WebsocketTransport};\n\n#[async_trait]\npub trait AsyncTransport: Stream<Item = Result<Bytes>> + Unpin {\n    /// Sends a packet to the server. This optionally handles sending of a\n    /// socketio binary attachment via the boolean attribute `is_binary_att`.\n    async fn emit(&self, data: Bytes, is_binary_att: bool) -> Result<()>;\n\n    /// Returns start of the url. ex. http://localhost:2998/engine.io/?EIO=4&transport=polling\n    /// Must have EIO and transport already set.\n    async fn base_url(&self) -> Result<Url>;\n\n    /// Used to update the base path, like when adding the sid.\n    async fn set_base_url(&self, base_url: Url) -> Result<()>;\n\n    /// Full query address\n    async fn address(&self) -> Result<Url>\n    where\n        Self: Sized,\n    {\n        let reader = format!(\"{:#?}\", SystemTime::now());\n        let hash = adler32(reader.as_bytes()).unwrap();\n        let mut url = self.base_url().await?;\n        url.query_pairs_mut().append_pair(\"t\", &hash.to_string());\n        Ok(url)\n    }\n}\n\n#[derive(Debug, Clone)]\npub enum AsyncTransportType {\n    Polling(PollingTransport),\n    Websocket(WebsocketTransport),\n    WebsocketSecure(WebsocketSecureTransport),\n}\n\nimpl From<PollingTransport> for AsyncTransportType {\n    fn from(transport: PollingTransport) -> Self {\n        AsyncTransportType::Polling(transport)\n    }\n}\n\nimpl From<WebsocketTransport> for AsyncTransportType {\n    fn from(transport: WebsocketTransport) -> Self {\n        AsyncTransportType::Websocket(transport)\n    }\n}\n\nimpl From<WebsocketSecureTransport> for AsyncTransportType {\n    fn from(transport: WebsocketSecureTransport) -> Self {\n        AsyncTransportType::WebsocketSecure(transport)\n    }\n}\n\n#[cfg(feature = \"async\")]\nimpl AsyncTransportType {\n    pub fn as_transport(&self) -> &(dyn AsyncTransport + Send) {\n        match self {\n            AsyncTransportType::Polling(transport) => transport,\n            AsyncTransportType::Websocket(transport) => transport,\n            AsyncTransportType::WebsocketSecure(transport) => transport,\n        }\n    }\n\n    pub fn as_pin_box(&mut self) -> Pin<Box<&mut (dyn AsyncTransport + Send)>> {\n        match self {\n            AsyncTransportType::Polling(transport) => Box::pin(transport),\n            AsyncTransportType::Websocket(transport) => Box::pin(transport),\n            AsyncTransportType::WebsocketSecure(transport) => Box::pin(transport),\n        }\n    }\n}\n"
  },
  {
    "path": "engineio/src/callback.rs",
    "content": "use crate::Packet;\nuse bytes::Bytes;\nuse std::fmt::Debug;\nuse std::ops::Deref;\nuse std::sync::Arc;\n\npub(crate) type DynCallback<I> = dyn Fn(I) + 'static + Sync + Send;\n\n#[derive(Clone)]\n/// Internal type, only implements debug on fixed set of generics\npub(crate) struct OptionalCallback<I> {\n    inner: Arc<Option<Box<DynCallback<I>>>>,\n}\n\nimpl<I> OptionalCallback<I> {\n    pub(crate) fn new<T>(callback: T) -> Self\n    where\n        T: Fn(I) + 'static + Sync + Send,\n    {\n        OptionalCallback {\n            inner: Arc::new(Some(Box::new(callback))),\n        }\n    }\n\n    pub(crate) fn default() -> Self {\n        OptionalCallback {\n            inner: Arc::new(None),\n        }\n    }\n}\n\n#[cfg_attr(tarpaulin, ignore)]\nimpl Debug for OptionalCallback<String> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {\n        f.write_fmt(format_args!(\n            \"Callback({:?})\",\n            if self.inner.is_some() {\n                \"Fn(String)\"\n            } else {\n                \"None\"\n            }\n        ))\n    }\n}\n\n#[cfg_attr(tarpaulin, ignore)]\nimpl Debug for OptionalCallback<()> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {\n        f.write_fmt(format_args!(\n            \"Callback({:?})\",\n            if self.inner.is_some() {\n                \"Fn(())\"\n            } else {\n                \"None\"\n            }\n        ))\n    }\n}\n\n#[cfg_attr(tarpaulin, ignore)]\nimpl Debug for OptionalCallback<Packet> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {\n        f.write_fmt(format_args!(\n            \"Callback({:?})\",\n            if self.inner.is_some() {\n                \"Fn(Packet)\"\n            } else {\n                \"None\"\n            }\n        ))\n    }\n}\n\n#[cfg_attr(tarpaulin, ignore)]\nimpl Debug for OptionalCallback<Bytes> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {\n        f.write_fmt(format_args!(\n            \"Callback({:?})\",\n            if self.inner.is_some() {\n                \"Fn(Bytes)\"\n            } else {\n                \"None\"\n            }\n        ))\n    }\n}\n\nimpl<I> Deref for OptionalCallback<I> {\n    type Target = Option<Box<DynCallback<I>>>;\n    fn deref(&self) -> &<Self as std::ops::Deref>::Target {\n        self.inner.as_ref()\n    }\n}\n"
  },
  {
    "path": "engineio/src/client/client.rs",
    "content": "use super::super::socket::Socket as InnerSocket;\nuse crate::callback::OptionalCallback;\nuse crate::socket::DEFAULT_MAX_POLL_TIMEOUT;\nuse crate::transport::Transport;\n\nuse crate::error::{Error, Result};\nuse crate::header::HeaderMap;\nuse crate::packet::{HandshakePacket, Packet, PacketId};\nuse crate::transports::{PollingTransport, WebsocketSecureTransport, WebsocketTransport};\nuse crate::ENGINE_IO_VERSION;\nuse bytes::Bytes;\nuse native_tls::TlsConnector;\nuse std::convert::TryFrom;\nuse std::convert::TryInto;\nuse std::fmt::Debug;\nuse url::Url;\n\n/// An engine.io client that allows interaction with the connected engine.io\n/// server. This client provides means for connecting, disconnecting and sending\n/// packets to the server.\n///\n/// ## Note:\n/// There is no need to put this Client behind an `Arc`, as the type uses `Arc`\n/// internally and provides a shared state beyond all cloned instances.\n#[derive(Clone, Debug)]\npub struct Client {\n    socket: InnerSocket,\n}\n\n#[derive(Clone, Debug)]\npub struct ClientBuilder {\n    url: Url,\n    tls_config: Option<TlsConnector>,\n    headers: Option<HeaderMap>,\n    handshake: Option<HandshakePacket>,\n    on_error: OptionalCallback<String>,\n    on_open: OptionalCallback<()>,\n    on_close: OptionalCallback<()>,\n    on_data: OptionalCallback<Bytes>,\n    on_packet: OptionalCallback<Packet>,\n}\n\nimpl ClientBuilder {\n    pub fn new(url: Url) -> Self {\n        let mut url = url;\n        url.query_pairs_mut()\n            .append_pair(\"EIO\", &ENGINE_IO_VERSION.to_string());\n\n        // No path add engine.io\n        if url.path() == \"/\" {\n            url.set_path(\"/engine.io/\");\n        }\n        ClientBuilder {\n            url,\n            headers: None,\n            tls_config: None,\n            handshake: None,\n            on_close: OptionalCallback::default(),\n            on_data: OptionalCallback::default(),\n            on_error: OptionalCallback::default(),\n            on_open: OptionalCallback::default(),\n            on_packet: OptionalCallback::default(),\n        }\n    }\n\n    /// Specify transport's tls config\n    pub fn tls_config(mut self, tls_config: TlsConnector) -> Self {\n        self.tls_config = Some(tls_config);\n        self\n    }\n\n    /// Specify transport's HTTP headers\n    pub fn headers(mut self, headers: HeaderMap) -> Self {\n        self.headers = Some(headers);\n        self\n    }\n\n    /// Registers the `on_close` callback.\n    pub fn on_close<T>(mut self, callback: T) -> Self\n    where\n        T: Fn(()) + 'static + Sync + Send,\n    {\n        self.on_close = OptionalCallback::new(callback);\n        self\n    }\n\n    /// Registers the `on_data` callback.\n    pub fn on_data<T>(mut self, callback: T) -> Self\n    where\n        T: Fn(Bytes) + 'static + Sync + Send,\n    {\n        self.on_data = OptionalCallback::new(callback);\n        self\n    }\n\n    /// Registers the `on_error` callback.\n    pub fn on_error<T>(mut self, callback: T) -> Self\n    where\n        T: Fn(String) + 'static + Sync + Send,\n    {\n        self.on_error = OptionalCallback::new(callback);\n        self\n    }\n\n    /// Registers the `on_open` callback.\n    pub fn on_open<T>(mut self, callback: T) -> Self\n    where\n        T: Fn(()) + 'static + Sync + Send,\n    {\n        self.on_open = OptionalCallback::new(callback);\n        self\n    }\n\n    /// Registers the `on_packet` callback.\n    pub fn on_packet<T>(mut self, callback: T) -> Self\n    where\n        T: Fn(Packet) + 'static + Sync + Send,\n    {\n        self.on_packet = OptionalCallback::new(callback);\n        self\n    }\n\n    /// Performs the handshake\n    fn handshake_with_transport<T: Transport>(&mut self, transport: &T) -> Result<()> {\n        // No need to handshake twice\n        if self.handshake.is_some() {\n            return Ok(());\n        }\n\n        let mut url = self.url.clone();\n\n        let handshake: HandshakePacket =\n            Packet::try_from(transport.poll(DEFAULT_MAX_POLL_TIMEOUT)?)?.try_into()?;\n\n        // update the base_url with the new sid\n        url.query_pairs_mut().append_pair(\"sid\", &handshake.sid[..]);\n\n        self.handshake = Some(handshake);\n\n        self.url = url;\n\n        Ok(())\n    }\n\n    fn handshake(&mut self) -> Result<()> {\n        if self.handshake.is_some() {\n            return Ok(());\n        }\n\n        // Start with polling transport\n        let transport = PollingTransport::new(\n            self.url.clone(),\n            self.tls_config.clone(),\n            self.headers.clone().map(|v| v.try_into().unwrap()),\n        );\n\n        self.handshake_with_transport(&transport)\n    }\n\n    /// Build websocket if allowed, if not fall back to polling\n    pub fn build(mut self) -> Result<Client> {\n        self.handshake()?;\n\n        if self.websocket_upgrade()? {\n            self.build_websocket_with_upgrade()\n        } else {\n            self.build_polling()\n        }\n    }\n\n    /// Build socket with polling transport\n    pub fn build_polling(mut self) -> Result<Client> {\n        self.handshake()?;\n\n        // Make a polling transport with new sid\n        let transport = PollingTransport::new(\n            self.url,\n            self.tls_config,\n            self.headers.map(|v| v.try_into().unwrap()),\n        );\n\n        // SAFETY: handshake function called previously.\n        Ok(Client {\n            socket: InnerSocket::new(\n                transport.into(),\n                self.handshake.unwrap(),\n                self.on_close,\n                self.on_data,\n                self.on_error,\n                self.on_open,\n                self.on_packet,\n            ),\n        })\n    }\n\n    /// Build socket with a polling transport then upgrade to websocket transport\n    pub fn build_websocket_with_upgrade(mut self) -> Result<Client> {\n        self.handshake()?;\n\n        if self.websocket_upgrade()? {\n            self.build_websocket()\n        } else {\n            Err(Error::IllegalWebsocketUpgrade())\n        }\n    }\n\n    /// Build socket with only a websocket transport\n    pub fn build_websocket(mut self) -> Result<Client> {\n        // SAFETY: Already a Url\n        let url = url::Url::parse(self.url.as_ref())?;\n\n        let headers: Option<http::HeaderMap> = if let Some(map) = self.headers.clone() {\n            Some(map.try_into()?)\n        } else {\n            None\n        };\n\n        match url.scheme() {\n            \"http\" | \"ws\" => {\n                let transport = WebsocketTransport::new(url, headers)?;\n                if self.handshake.is_some() {\n                    transport.upgrade()?;\n                } else {\n                    self.handshake_with_transport(&transport)?;\n                }\n                // NOTE: Although self.url contains the sid, it does not propagate to the transport\n                // SAFETY: handshake function called previously.\n                Ok(Client {\n                    socket: InnerSocket::new(\n                        transport.into(),\n                        self.handshake.unwrap(),\n                        self.on_close,\n                        self.on_data,\n                        self.on_error,\n                        self.on_open,\n                        self.on_packet,\n                    ),\n                })\n            }\n            \"https\" | \"wss\" => {\n                let transport =\n                    WebsocketSecureTransport::new(url, self.tls_config.clone(), headers)?;\n                if self.handshake.is_some() {\n                    transport.upgrade()?;\n                } else {\n                    self.handshake_with_transport(&transport)?;\n                }\n                // NOTE: Although self.url contains the sid, it does not propagate to the transport\n                // SAFETY: handshake function called previously.\n                Ok(Client {\n                    socket: InnerSocket::new(\n                        transport.into(),\n                        self.handshake.unwrap(),\n                        self.on_close,\n                        self.on_data,\n                        self.on_error,\n                        self.on_open,\n                        self.on_packet,\n                    ),\n                })\n            }\n            _ => Err(Error::InvalidUrlScheme(url.scheme().to_string())),\n        }\n    }\n\n    /// Build websocket if allowed, if not allowed or errored fall back to polling.\n    /// WARNING: websocket errors suppressed, no indication of websocket success or failure.\n    pub fn build_with_fallback(self) -> Result<Client> {\n        let result = self.clone().build();\n        if result.is_err() {\n            self.build_polling()\n        } else {\n            result\n        }\n    }\n\n    /// Checks the handshake to see if websocket upgrades are allowed\n    fn websocket_upgrade(&mut self) -> Result<bool> {\n        // SAFETY: handshake set by above function.\n        Ok(self\n            .handshake\n            .as_ref()\n            .unwrap()\n            .upgrades\n            .iter()\n            .any(|upgrade| upgrade.to_lowercase() == *\"websocket\"))\n    }\n}\n\nimpl Client {\n    pub fn close(&self) -> Result<()> {\n        self.socket.disconnect()\n    }\n\n    /// Opens the connection to a specified server. The first Pong packet is sent\n    /// to the server to trigger the Ping-cycle.\n    pub fn connect(&self) -> Result<()> {\n        self.socket.connect()\n    }\n\n    /// Disconnects the connection.\n    pub fn disconnect(&self) -> Result<()> {\n        self.socket.disconnect()\n    }\n\n    /// Sends a packet to the server.\n    pub fn emit(&self, packet: Packet) -> Result<()> {\n        self.socket.emit(packet)\n    }\n\n    /// Polls for next payload\n    #[doc(hidden)]\n    pub fn poll(&self) -> Result<Option<Packet>> {\n        let packet = self.socket.poll()?;\n        if let Some(packet) = packet {\n            // check for the appropriate action or callback\n            self.socket.handle_packet(packet.clone());\n            match packet.packet_id {\n                PacketId::MessageBinary => {\n                    self.socket.handle_data(packet.data.clone());\n                }\n                PacketId::Message => {\n                    self.socket.handle_data(packet.data.clone());\n                }\n                PacketId::Close => {\n                    self.socket.handle_close();\n                }\n                PacketId::Open => {\n                    unreachable!(\"Won't happen as we open the connection beforehand\");\n                }\n                PacketId::Upgrade => {\n                    // this is already checked during the handshake, so just do nothing here\n                }\n                PacketId::Ping => {\n                    self.socket.pinged()?;\n                    self.emit(Packet::new(PacketId::Pong, Bytes::new()))?;\n                }\n                PacketId::Pong => {\n                    // this will never happen as the pong packet is\n                    // only sent by the client\n                    unreachable!();\n                }\n                PacketId::Noop => (),\n            }\n            Ok(Some(packet))\n        } else {\n            Ok(None)\n        }\n    }\n\n    /// Check if the underlying transport client is connected.\n    pub fn is_connected(&self) -> Result<bool> {\n        self.socket.is_connected()\n    }\n\n    pub fn iter(&self) -> Iter {\n        Iter { socket: self }\n    }\n}\n\n#[derive(Clone)]\npub struct Iter<'a> {\n    socket: &'a Client,\n}\n\nimpl<'a> Iterator for Iter<'a> {\n    type Item = Result<Packet>;\n    fn next(&mut self) -> std::option::Option<<Self as std::iter::Iterator>::Item> {\n        match self.socket.poll() {\n            Ok(Some(packet)) => Some(Ok(packet)),\n            Ok(None) => None,\n            Err(err) => Some(Err(err)),\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n\n    use crate::packet::PacketId;\n\n    use super::*;\n\n    /// The purpose of this test is to check whether the Client is properly cloneable or not.\n    /// As the documentation of the engine.io client states, the object needs to maintain it's internal\n    /// state when cloned and the cloned object should reflect the same state throughout the lifetime\n    /// of both objects (initial and cloned).\n    #[test]\n    fn test_client_cloneable() -> Result<()> {\n        let url = crate::test::engine_io_server()?;\n        let sut = builder(url).build()?;\n\n        let cloned = sut.clone();\n\n        sut.connect()?;\n\n        // when the underlying socket is connected, the\n        // state should also change on the cloned one\n        assert!(sut.is_connected()?);\n        assert!(cloned.is_connected()?);\n\n        // both clients should reflect the same messages.\n        let mut iter = sut\n            .iter()\n            .map(|packet| packet.unwrap())\n            .filter(|packet| packet.packet_id != PacketId::Ping);\n\n        let mut iter_cloned = cloned\n            .iter()\n            .map(|packet| packet.unwrap())\n            .filter(|packet| packet.packet_id != PacketId::Ping);\n\n        assert_eq!(\n            iter.next(),\n            Some(Packet::new(PacketId::Message, \"hello client\"))\n        );\n\n        sut.emit(Packet::new(PacketId::Message, \"respond\"))?;\n\n        assert_eq!(\n            iter_cloned.next(),\n            Some(Packet::new(PacketId::Message, \"Roger Roger\"))\n        );\n\n        cloned.disconnect()?;\n\n        // when the underlying socket is disconnected, the\n        // state should also change on the cloned one\n        assert!(!sut.is_connected()?);\n        assert!(!cloned.is_connected()?);\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_illegal_actions() -> Result<()> {\n        let url = crate::test::engine_io_server()?;\n        let sut = builder(url.clone()).build()?;\n\n        assert!(sut\n            .emit(Packet::new(PacketId::Close, Bytes::new()))\n            .is_err());\n\n        sut.connect()?;\n\n        assert!(sut.poll().is_ok());\n\n        assert!(builder(Url::parse(\"fake://fake.fake\").unwrap())\n            .build_websocket()\n            .is_err());\n\n        Ok(())\n    }\n    use reqwest::header::HOST;\n\n    use crate::packet::Packet;\n\n    fn builder(url: Url) -> ClientBuilder {\n        ClientBuilder::new(url)\n            .on_open(|_| {\n                println!(\"Open event!\");\n            })\n            .on_packet(|packet| {\n                println!(\"Received packet: {:?}\", packet);\n            })\n            .on_data(|data| {\n                println!(\"Received data: {:?}\", std::str::from_utf8(&data));\n            })\n            .on_close(|_| {\n                println!(\"Close event!\");\n            })\n            .on_error(|error| {\n                println!(\"Error {}\", error);\n            })\n    }\n\n    fn test_connection(socket: Client) -> Result<()> {\n        let socket = socket;\n\n        socket.connect().unwrap();\n\n        // TODO: 0.3.X better tests\n\n        let mut iter = socket\n            .iter()\n            .map(|packet| packet.unwrap())\n            .filter(|packet| packet.packet_id != PacketId::Ping);\n\n        assert_eq!(\n            iter.next(),\n            Some(Packet::new(PacketId::Message, \"hello client\"))\n        );\n\n        socket.emit(Packet::new(PacketId::Message, \"respond\"))?;\n\n        assert_eq!(\n            iter.next(),\n            Some(Packet::new(PacketId::Message, \"Roger Roger\"))\n        );\n\n        socket.close()\n    }\n\n    #[test]\n    fn test_connection_long() -> Result<()> {\n        // Long lived socket to receive pings\n        let url = crate::test::engine_io_server()?;\n        let socket = builder(url).build()?;\n\n        socket.connect()?;\n\n        let mut iter = socket.iter();\n        // hello client\n        iter.next();\n        // Ping\n        iter.next();\n\n        socket.disconnect()?;\n\n        assert!(!socket.is_connected()?);\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_connection_dynamic() -> Result<()> {\n        let url = crate::test::engine_io_server()?;\n        let socket = builder(url).build()?;\n        test_connection(socket)?;\n\n        let url = crate::test::engine_io_polling_server()?;\n        let socket = builder(url).build()?;\n        test_connection(socket)\n    }\n\n    #[test]\n    fn test_connection_fallback() -> Result<()> {\n        let url = crate::test::engine_io_server()?;\n        let socket = builder(url).build_with_fallback()?;\n        test_connection(socket)?;\n\n        let url = crate::test::engine_io_polling_server()?;\n        let socket = builder(url).build_with_fallback()?;\n        test_connection(socket)\n    }\n\n    #[test]\n    fn test_connection_dynamic_secure() -> Result<()> {\n        let url = crate::test::engine_io_server_secure()?;\n        let mut builder = builder(url);\n        builder = builder.tls_config(crate::test::tls_connector()?);\n        let socket = builder.build()?;\n        test_connection(socket)\n    }\n\n    #[test]\n    fn test_connection_polling() -> Result<()> {\n        let url = crate::test::engine_io_server()?;\n        let socket = builder(url).build_polling()?;\n        test_connection(socket)\n    }\n\n    #[test]\n    fn test_connection_wss() -> Result<()> {\n        let url = crate::test::engine_io_polling_server()?;\n        assert!(builder(url).build_websocket_with_upgrade().is_err());\n\n        let host =\n            std::env::var(\"ENGINE_IO_SECURE_HOST\").unwrap_or_else(|_| \"localhost\".to_owned());\n        let mut url = crate::test::engine_io_server_secure()?;\n\n        let mut headers = HeaderMap::default();\n        headers.insert(HOST, host);\n        let mut builder = builder(url.clone());\n\n        builder = builder.tls_config(crate::test::tls_connector()?);\n        builder = builder.headers(headers.clone());\n        let socket = builder.clone().build_websocket_with_upgrade()?;\n\n        test_connection(socket)?;\n\n        let socket = builder.build_websocket()?;\n\n        test_connection(socket)?;\n\n        url.set_scheme(\"wss\").unwrap();\n\n        let builder = self::builder(url)\n            .tls_config(crate::test::tls_connector()?)\n            .headers(headers);\n        let socket = builder.clone().build_websocket()?;\n\n        test_connection(socket)?;\n\n        assert!(builder.build_websocket_with_upgrade().is_err());\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_connection_ws() -> Result<()> {\n        let url = crate::test::engine_io_polling_server()?;\n        assert!(builder(url.clone()).build_websocket().is_err());\n        assert!(builder(url).build_websocket_with_upgrade().is_err());\n\n        let mut url = crate::test::engine_io_server()?;\n\n        let builder = builder(url.clone());\n        let socket = builder.clone().build_websocket()?;\n        test_connection(socket)?;\n\n        let socket = builder.build_websocket_with_upgrade()?;\n        test_connection(socket)?;\n\n        url.set_scheme(\"ws\").unwrap();\n\n        let builder = self::builder(url);\n        let socket = builder.clone().build_websocket()?;\n\n        test_connection(socket)?;\n\n        assert!(builder.build_websocket_with_upgrade().is_err());\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_open_invariants() -> Result<()> {\n        let url = crate::test::engine_io_server()?;\n        let illegal_url = \"this is illegal\";\n\n        assert!(Url::parse(illegal_url).is_err());\n\n        let invalid_protocol = \"file:///tmp/foo\";\n        assert!(builder(Url::parse(invalid_protocol).unwrap())\n            .build()\n            .is_err());\n\n        let sut = builder(url.clone()).build()?;\n        let _error = sut\n            .emit(Packet::new(PacketId::Close, Bytes::new()))\n            .expect_err(\"error\");\n        assert!(matches!(Error::IllegalActionBeforeOpen(), _error));\n\n        // test missing match arm in socket constructor\n        let mut headers = HeaderMap::default();\n        let host =\n            std::env::var(\"ENGINE_IO_SECURE_HOST\").unwrap_or_else(|_| \"localhost\".to_owned());\n        headers.insert(HOST, host);\n\n        let _ = builder(url.clone())\n            .tls_config(\n                TlsConnector::builder()\n                    .danger_accept_invalid_certs(true)\n                    .build()\n                    .unwrap(),\n            )\n            .build()?;\n        let _ = builder(url).headers(headers).build()?;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "engineio/src/client/mod.rs",
    "content": "mod client;\npub use client::Iter;\npub use {client::Client, client::ClientBuilder, client::Iter as SocketIter};\n"
  },
  {
    "path": "engineio/src/error.rs",
    "content": "use base64::DecodeError;\nuse reqwest::Error as ReqwestError;\nuse serde_json::Error as JsonError;\nuse std::io::Error as IoError;\nuse std::str::Utf8Error;\nuse thiserror::Error;\nuse tungstenite::Error as TungsteniteError;\nuse url::ParseError as UrlParseError;\n\n/// Enumeration of all possible errors in the `socket.io` context.\n#[derive(Error, Debug)]\n#[non_exhaustive]\n#[cfg_attr(tarpaulin, ignore)]\npub enum Error {\n    // Conform to https://rust-lang.github.io/api-guidelines/naming.html#names-use-a-consistent-word-order-c-word-order\n    // Negative verb-object\n    #[error(\"Invalid packet id: {0}\")]\n    InvalidPacketId(u8),\n    #[error(\"Error while parsing an incomplete packet\")]\n    IncompletePacket(),\n    #[error(\"Got an invalid packet which did not follow the protocol format\")]\n    InvalidPacket(),\n    #[error(\"An error occurred while decoding the utf-8 text: {0}\")]\n    InvalidUtf8(#[from] Utf8Error),\n    #[error(\"An error occurred while encoding/decoding base64: {0}\")]\n    InvalidBase64(#[from] DecodeError),\n    #[error(\"Invalid Url during parsing\")]\n    InvalidUrl(#[from] UrlParseError),\n    #[error(\"Invalid Url Scheme: {0}\")]\n    InvalidUrlScheme(String),\n    #[error(\"Error during connection via http: {0}\")]\n    IncompleteResponseFromReqwest(#[from] ReqwestError),\n    #[error(\"Error with websocket connection: {0}\")]\n    WebsocketError(#[from] TungsteniteError),\n    #[error(\"Network request returned with status code: {0}\")]\n    IncompleteHttp(u16),\n    #[error(\"Got illegal handshake response: {0}\")]\n    InvalidHandshake(String),\n    #[error(\"Called an action before the connection was established\")]\n    IllegalActionBeforeOpen(),\n    #[error(\"Error setting up the http request: {0}\")]\n    InvalidHttpConfiguration(#[from] http::Error),\n    #[error(\"string is not json serializable: {0}\")]\n    InvalidJson(#[from] JsonError),\n    #[error(\"A lock was poisoned\")]\n    InvalidPoisonedLock(),\n    #[error(\"Got an IO-Error: {0}\")]\n    IncompleteIo(#[from] IoError),\n    #[error(\"Server did not allow upgrading to websockets\")]\n    IllegalWebsocketUpgrade(),\n    #[error(\"Invalid header name\")]\n    InvalidHeaderNameFromReqwest(#[from] reqwest::header::InvalidHeaderName),\n    #[error(\"Invalid header value\")]\n    InvalidHeaderValueFromReqwest(#[from] reqwest::header::InvalidHeaderValue),\n    #[error(\"The server did not send a PING packet in time\")]\n    PingTimeout(),\n}\n\npub(crate) type Result<T> = std::result::Result<T, Error>;\n\nimpl<T> From<std::sync::PoisonError<T>> for Error {\n    fn from(_: std::sync::PoisonError<T>) -> Self {\n        Self::InvalidPoisonedLock()\n    }\n}\n\nimpl From<Error> for std::io::Error {\n    fn from(err: Error) -> std::io::Error {\n        std::io::Error::new(std::io::ErrorKind::Other, err)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::sync::{Mutex, PoisonError};\n\n    use super::*;\n\n    /// This just tests the own implementations and relies on `thiserror` for the others.\n    #[test]\n    fn test_error_conversion() {\n        let mutex = Mutex::new(0);\n        let _error = Error::from(PoisonError::new(mutex.lock()));\n        assert!(matches!(Error::InvalidPoisonedLock(), _error));\n\n        let _io_error = std::io::Error::from(Error::IllegalWebsocketUpgrade());\n        let _error =\n            std::io::Error::new(std::io::ErrorKind::Other, Error::IllegalWebsocketUpgrade());\n        assert!(matches!(_io_error, _error));\n    }\n}\n"
  },
  {
    "path": "engineio/src/header.rs",
    "content": "use crate::Error;\nuse bytes::Bytes;\nuse http::{\n    header::HeaderName as HttpHeaderName, HeaderMap as HttpHeaderMap,\n    HeaderValue as HttpHeaderValue,\n};\nuse std::collections::HashMap;\nuse std::convert::TryFrom;\nuse std::fmt::{Display, Formatter, Result as FmtResult};\nuse std::str::FromStr;\n\n#[derive(Eq, PartialEq, Hash, Debug, Clone)]\npub struct HeaderName {\n    inner: Box<str>,\n}\n\n#[derive(Eq, PartialEq, Hash, Debug, Clone)]\npub struct HeaderValue {\n    inner: Bytes,\n}\n\n#[derive(Eq, PartialEq, Debug, Clone, Default)]\npub struct HeaderMap {\n    map: HashMap<HeaderName, HeaderValue>,\n}\n\npub struct IntoIter {\n    inner: std::collections::hash_map::IntoIter<HeaderName, HeaderValue>,\n}\n\nimpl Display for HeaderName {\n    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {\n        f.write_str(self.inner.as_ref())\n    }\n}\n\nimpl From<String> for HeaderName {\n    fn from(string: String) -> Self {\n        HeaderName {\n            inner: string.into_boxed_str(),\n        }\n    }\n}\n\nimpl TryFrom<HeaderName> for HttpHeaderName {\n    type Error = Error;\n    fn try_from(\n        header: HeaderName,\n    ) -> std::result::Result<Self, <Self as std::convert::TryFrom<HeaderName>>::Error> {\n        Ok(HttpHeaderName::from_str(header.inner.as_ref())?)\n    }\n}\n\nimpl From<HttpHeaderName> for HeaderName {\n    fn from(header: HttpHeaderName) -> Self {\n        HeaderName::from(header.to_string())\n    }\n}\n\nimpl From<String> for HeaderValue {\n    fn from(string: String) -> Self {\n        HeaderValue {\n            inner: Bytes::from(string),\n        }\n    }\n}\n\nimpl TryFrom<HeaderValue> for HttpHeaderValue {\n    type Error = Error;\n    fn try_from(\n        value: HeaderValue,\n    ) -> std::result::Result<Self, <Self as std::convert::TryFrom<HeaderValue>>::Error> {\n        Ok(HttpHeaderValue::from_bytes(&value.inner[..])?)\n    }\n}\n\nimpl From<HttpHeaderValue> for HeaderValue {\n    fn from(value: HttpHeaderValue) -> Self {\n        HeaderValue {\n            inner: Bytes::copy_from_slice(value.as_bytes()),\n        }\n    }\n}\n\nimpl From<&str> for HeaderValue {\n    fn from(string: &str) -> Self {\n        Self::from(string.to_owned())\n    }\n}\n\nimpl TryFrom<HeaderMap> for HttpHeaderMap {\n    type Error = Error;\n    fn try_from(\n        headers: HeaderMap,\n    ) -> std::result::Result<Self, <Self as std::convert::TryFrom<HeaderMap>>::Error> {\n        headers\n            .into_iter()\n            .map(|(key, value)| {\n                Ok((\n                    HttpHeaderName::try_from(key)?,\n                    HttpHeaderValue::try_from(value)?,\n                ))\n            })\n            .collect()\n    }\n}\n\nimpl IntoIterator for HeaderMap {\n    type Item = (HeaderName, HeaderValue);\n    type IntoIter = IntoIter;\n    fn into_iter(self) -> <Self as std::iter::IntoIterator>::IntoIter {\n        IntoIter {\n            inner: self.map.into_iter(),\n        }\n    }\n}\n\nimpl HeaderMap {\n    pub fn new() -> Self {\n        HeaderMap {\n            map: HashMap::new(),\n        }\n    }\n\n    pub fn insert<T: Into<HeaderName>, U: Into<HeaderValue>>(\n        &mut self,\n        key: T,\n        value: U,\n    ) -> Option<HeaderValue> {\n        self.map.insert(key.into(), value.into())\n    }\n}\n\nimpl Iterator for IntoIter {\n    type Item = (HeaderName, HeaderValue);\n    fn next(&mut self) -> std::option::Option<<Self as std::iter::Iterator>::Item> {\n        self.inner.next()\n    }\n}\n"
  },
  {
    "path": "engineio/src/lib.rs",
    "content": "//! # Rust-engineio-client\n//!\n//! An implementation of a engine.io client written in the rust programming language. This implementation currently\n//! supports revision 4 of the engine.io protocol. If you have any connection issues with this client,\n//! make sure the server uses at least revision 4 of the engine.io protocol.\n//!\n//! ## Example usage\n//!\n//! ``` rust\n//! use rust_engineio::{ClientBuilder, Client, packet::{Packet, PacketId}};\n//! use url::Url;\n//! use bytes::Bytes;\n//!\n//! // get a client with an `on_open` callback\n//! let client: Client = ClientBuilder::new(Url::parse(\"http://localhost:4201\").unwrap())\n//!      .on_open(|_| println!(\"Connection opened!\"))\n//!      .build()\n//!      .expect(\"Creating client failed\");\n//!\n//! // connect to the server\n//! client.connect().expect(\"Connection failed\");\n//!\n//! // create a packet, in this case a message packet and emit it\n//! let packet = Packet::new(PacketId::Message, Bytes::from_static(b\"Hello World\"));\n//! client.emit(packet).expect(\"Server unreachable\");\n//!\n//! // disconnect from the server\n//! client.disconnect().expect(\"Disconnect failed\")\n//! ```\n//!\n//! The main entry point for using this crate is the [`ClientBuilder`] (or [`asynchronous::ClientBuilder`] respectively)\n//! which provides the opportunity to define how you want to connect to a certain endpoint.\n//! The following connection methods are available:\n//! * `build`: Build websocket if allowed, if not fall back to polling. Standard configuration.\n//! * `build_polling`: enforces a `polling` transport.\n//! * `build_websocket_with_upgrade`: Build socket with a polling transport then upgrade to websocket transport (if possible).\n//! * `build_websocket`: Build socket with only a websocket transport, crashes when websockets are not allowed.\n//!\n//!\n//! ## Current features\n//!\n//! This implementation now supports all of the features of the engine.io protocol mentioned [here](https://github.com/socketio/engine.io-protocol).\n//! This includes various transport options, the possibility of sending engine.io packets and registering the\n//! common engine.io event callbacks:\n//! * on_open\n//! * on_close\n//! * on_data\n//! * on_error\n//! * on_packet\n//!\n//! It is also possible to pass in custom tls configurations via the `TlsConnector` as well\n//! as custom headers for the opening request.\n//!\n//! ## Async version\n//!\n//! The crate also ships with an asynchronous version that can be enabled with a feature flag.\n//! The async version implements the same features mentioned above.\n//! The asynchronous version has a similar API, just with async functions. Currently the futures\n//! can only be executed with [`tokio`](https://tokio.rs). In the first benchmarks the async version\n//! showed improvements of up to 93% in speed.\n//! To make use of the async version, import the crate as follows:\n//! ```toml\n//! [depencencies]\n//! rust-engineio = { version = \"0.3.1\", features = [\"async\"] }\n//! ```\n//!\n#![allow(clippy::rc_buffer)]\n#![warn(clippy::complexity)]\n#![warn(clippy::style)]\n#![warn(clippy::perf)]\n#![warn(clippy::correctness)]\n/// A small macro that spawns a scoped thread. Used for calling the callback\n/// functions.\nmacro_rules! spawn_scoped {\n    ($e:expr) => {\n        std::thread::scope(|s| {\n            s.spawn(|| $e);\n        });\n    };\n}\n\npub mod asynchronous;\nmod callback;\npub mod client;\n/// Generic header map\npub mod header;\npub mod packet;\npub(self) mod socket;\npub mod transport;\npub mod transports;\n\npub const ENGINE_IO_VERSION: i32 = 4;\n\n/// Contains the error type which will be returned with every result in this\n/// crate. Handles all kinds of errors.\npub mod error;\n\npub use client::{Client, ClientBuilder};\npub use error::Error;\npub use packet::{Packet, PacketId};\n\n#[cfg(test)]\npub(crate) mod test {\n    use super::*;\n    use native_tls::TlsConnector;\n    const CERT_PATH: &str = \"../ci/cert/ca.crt\";\n    use native_tls::Certificate;\n    use std::fs::File;\n    use std::io::Read;\n\n    pub(crate) fn tls_connector() -> error::Result<TlsConnector> {\n        let cert_path = std::env::var(\"CA_CERT_PATH\").unwrap_or_else(|_| CERT_PATH.to_owned());\n        let mut cert_file = File::open(cert_path)?;\n        let mut buf = vec![];\n        cert_file.read_to_end(&mut buf)?;\n        let cert: Certificate = Certificate::from_pem(&buf[..]).unwrap();\n        Ok(TlsConnector::builder()\n            // ONLY USE FOR TESTING!\n            .danger_accept_invalid_hostnames(true)\n            .add_root_certificate(cert)\n            .build()\n            .unwrap())\n    }\n    /// The `engine.io` server for testing runs on port 4201\n    const SERVER_URL: &str = \"http://localhost:4201\";\n    /// The `engine.io` server that refuses upgrades runs on port 4203\n    const SERVER_POLLING_URL: &str = \"http://localhost:4203\";\n    const SERVER_URL_SECURE: &str = \"https://localhost:4202\";\n    use url::Url;\n\n    pub(crate) fn engine_io_server() -> crate::error::Result<Url> {\n        let url = std::env::var(\"ENGINE_IO_SERVER\").unwrap_or_else(|_| SERVER_URL.to_owned());\n        Ok(Url::parse(&url)?)\n    }\n\n    pub(crate) fn engine_io_polling_server() -> crate::error::Result<Url> {\n        let url = std::env::var(\"ENGINE_IO_POLLING_SERVER\")\n            .unwrap_or_else(|_| SERVER_POLLING_URL.to_owned());\n        Ok(Url::parse(&url)?)\n    }\n\n    pub(crate) fn engine_io_server_secure() -> crate::error::Result<Url> {\n        let url = std::env::var(\"ENGINE_IO_SECURE_SERVER\")\n            .unwrap_or_else(|_| SERVER_URL_SECURE.to_owned());\n        Ok(Url::parse(&url)?)\n    }\n}\n"
  },
  {
    "path": "engineio/src/packet.rs",
    "content": "use base64::{engine::general_purpose, Engine as _};\nuse bytes::{BufMut, Bytes, BytesMut};\nuse serde::{Deserialize, Serialize};\nuse std::char;\nuse std::convert::TryFrom;\nuse std::convert::TryInto;\nuse std::fmt::{Display, Formatter, Result as FmtResult, Write};\nuse std::ops::Index;\n\nuse crate::error::{Error, Result};\n/// Enumeration of the `engine.io` `Packet` types.\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\npub enum PacketId {\n    Open,\n    Close,\n    Ping,\n    Pong,\n    Message,\n    // A type of message that is base64 encoded\n    MessageBinary,\n    Upgrade,\n    Noop,\n}\n\nimpl PacketId {\n    /// Returns the byte that represents the [`PacketId`] as a [`char`].\n    fn to_string_byte(self) -> u8 {\n        match self {\n            Self::MessageBinary => b'b',\n            _ => u8::from(self) + b'0',\n        }\n    }\n}\n\nimpl Display for PacketId {\n    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {\n        f.write_char(self.to_string_byte() as char)\n    }\n}\n\nimpl From<PacketId> for u8 {\n    fn from(packet_id: PacketId) -> Self {\n        match packet_id {\n            PacketId::Open => 0,\n            PacketId::Close => 1,\n            PacketId::Ping => 2,\n            PacketId::Pong => 3,\n            PacketId::Message => 4,\n            PacketId::MessageBinary => 4,\n            PacketId::Upgrade => 5,\n            PacketId::Noop => 6,\n        }\n    }\n}\n\nimpl TryFrom<u8> for PacketId {\n    type Error = Error;\n    /// Converts a byte into the corresponding `PacketId`.\n    fn try_from(b: u8) -> Result<PacketId> {\n        match b {\n            0 | b'0' => Ok(PacketId::Open),\n            1 | b'1' => Ok(PacketId::Close),\n            2 | b'2' => Ok(PacketId::Ping),\n            3 | b'3' => Ok(PacketId::Pong),\n            4 | b'4' => Ok(PacketId::Message),\n            5 | b'5' => Ok(PacketId::Upgrade),\n            6 | b'6' => Ok(PacketId::Noop),\n            _ => Err(Error::InvalidPacketId(b)),\n        }\n    }\n}\n\n/// A `Packet` sent via the `engine.io` protocol.\n#[derive(Debug, Clone, Eq, PartialEq)]\npub struct Packet {\n    pub packet_id: PacketId,\n    pub data: Bytes,\n}\n\n/// Data which gets exchanged in a handshake as defined by the server.\n#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]\npub struct HandshakePacket {\n    pub sid: String,\n    pub upgrades: Vec<String>,\n    #[serde(rename = \"pingInterval\")]\n    pub ping_interval: u64,\n    #[serde(rename = \"pingTimeout\")]\n    pub ping_timeout: u64,\n}\n\nimpl TryFrom<Packet> for HandshakePacket {\n    type Error = Error;\n    fn try_from(packet: Packet) -> Result<HandshakePacket> {\n        Ok(serde_json::from_slice(packet.data[..].as_ref())?)\n    }\n}\n\nimpl Packet {\n    /// Creates a new `Packet`.\n    pub fn new<T: Into<Bytes>>(packet_id: PacketId, data: T) -> Self {\n        Packet {\n            packet_id,\n            data: data.into(),\n        }\n    }\n}\n\nimpl TryFrom<Bytes> for Packet {\n    type Error = Error;\n    /// Decodes a single `Packet` from an `u8` byte stream.\n    fn try_from(\n        bytes: Bytes,\n    ) -> std::result::Result<Self, <Self as std::convert::TryFrom<Bytes>>::Error> {\n        if bytes.is_empty() {\n            return Err(Error::IncompletePacket());\n        }\n\n        let is_base64 = *bytes.first().ok_or(Error::IncompletePacket())? == b'b';\n\n        // only 'messages' packets could be encoded\n        let packet_id = if is_base64 {\n            PacketId::MessageBinary\n        } else {\n            (*bytes.first().ok_or(Error::IncompletePacket())?).try_into()?\n        };\n\n        if bytes.len() == 1 && packet_id == PacketId::Message {\n            return Err(Error::IncompletePacket());\n        }\n\n        let data: Bytes = bytes.slice(1..);\n\n        Ok(Packet {\n            packet_id,\n            data: if is_base64 {\n                Bytes::from(general_purpose::STANDARD.decode(data.as_ref())?)\n            } else {\n                data\n            },\n        })\n    }\n}\n\nimpl From<Packet> for Bytes {\n    /// Encodes a `Packet` into an `u8` byte stream.\n    fn from(packet: Packet) -> Self {\n        let mut result = BytesMut::with_capacity(packet.data.len() + 1);\n        result.put_u8(packet.packet_id.to_string_byte());\n        if packet.packet_id == PacketId::MessageBinary {\n            result.extend(general_purpose::STANDARD.encode(packet.data).into_bytes());\n        } else {\n            result.put(packet.data);\n        }\n        result.freeze()\n    }\n}\n\n#[derive(Debug, Clone)]\npub(crate) struct Payload(Vec<Packet>);\n\nimpl Payload {\n    // see https://en.wikipedia.org/wiki/Delimiter#ASCII_delimited_text\n    const SEPARATOR: char = '\\x1e';\n\n    #[cfg(test)]\n    pub fn len(&self) -> usize {\n        self.0.len()\n    }\n}\n\nimpl TryFrom<Bytes> for Payload {\n    type Error = Error;\n    /// Decodes a `payload` which in the `engine.io` context means a chain of normal\n    /// packets separated by a certain SEPARATOR, in this case the delimiter `\\x30`.\n    fn try_from(payload: Bytes) -> Result<Self> {\n        payload\n            .split(|&c| c as char == Self::SEPARATOR)\n            .map(|slice| Packet::try_from(payload.slice_ref(slice)))\n            .collect::<Result<Vec<_>>>()\n            .map(Self)\n    }\n}\n\nimpl TryFrom<Payload> for Bytes {\n    type Error = Error;\n    /// Encodes a payload. Payload in the `engine.io` context means a chain of\n    /// normal `packets` separated by a SEPARATOR, in this case the delimiter\n    /// `\\x30`.\n    fn try_from(packets: Payload) -> Result<Self> {\n        let mut buf = BytesMut::new();\n        for packet in packets {\n            // at the moment no base64 encoding is used\n            buf.extend(Bytes::from(packet.clone()));\n            buf.put_u8(Payload::SEPARATOR as u8);\n        }\n\n        // remove the last separator\n        let _ = buf.split_off(buf.len() - 1);\n        Ok(buf.freeze())\n    }\n}\n\n#[derive(Clone, Debug)]\npub struct IntoIter {\n    iter: std::vec::IntoIter<Packet>,\n}\n\nimpl Iterator for IntoIter {\n    type Item = Packet;\n    fn next(&mut self) -> std::option::Option<<Self as std::iter::Iterator>::Item> {\n        self.iter.next()\n    }\n}\n\nimpl IntoIterator for Payload {\n    type Item = Packet;\n    type IntoIter = IntoIter;\n    fn into_iter(self) -> <Self as std::iter::IntoIterator>::IntoIter {\n        IntoIter {\n            iter: self.0.into_iter(),\n        }\n    }\n}\n\nimpl Index<usize> for Payload {\n    type Output = Packet;\n    fn index(&self, index: usize) -> &Packet {\n        &self.0[index]\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_packet_error() {\n        let err = Packet::try_from(BytesMut::with_capacity(10).freeze());\n        assert!(err.is_err())\n    }\n\n    #[test]\n    fn test_is_reflexive() {\n        let data = Bytes::from_static(b\"1Hello World\");\n        let packet = Packet::try_from(data).unwrap();\n\n        assert_eq!(packet.packet_id, PacketId::Close);\n        assert_eq!(packet.data, Bytes::from_static(b\"Hello World\"));\n\n        let data = Bytes::from_static(b\"1Hello World\");\n        assert_eq!(Bytes::from(packet), data);\n    }\n\n    #[test]\n    fn test_binary_packet() {\n        // SGVsbG8= is the encoded string for 'Hello'\n        let data = Bytes::from_static(b\"bSGVsbG8=\");\n        let packet = Packet::try_from(data.clone()).unwrap();\n\n        assert_eq!(packet.packet_id, PacketId::MessageBinary);\n        assert_eq!(packet.data, Bytes::from_static(b\"Hello\"));\n\n        assert_eq!(Bytes::from(packet), data);\n    }\n\n    #[test]\n    fn test_decode_payload() -> Result<()> {\n        let data = Bytes::from_static(b\"1Hello\\x1e1HelloWorld\");\n        let packets = Payload::try_from(data)?;\n\n        assert_eq!(packets[0].packet_id, PacketId::Close);\n        assert_eq!(packets[0].data, Bytes::from_static(b\"Hello\"));\n        assert_eq!(packets[1].packet_id, PacketId::Close);\n        assert_eq!(packets[1].data, Bytes::from_static(b\"HelloWorld\"));\n\n        let data = \"1Hello\\x1e1HelloWorld\".to_owned().into_bytes();\n        assert_eq!(Bytes::try_from(packets).unwrap(), data);\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_binary_payload() {\n        let data = Bytes::from_static(b\"bSGVsbG8=\\x1ebSGVsbG9Xb3JsZA==\\x1ebSGVsbG8=\");\n        let packets = Payload::try_from(data.clone()).unwrap();\n\n        assert!(packets.len() == 3);\n        assert_eq!(packets[0].packet_id, PacketId::MessageBinary);\n        assert_eq!(packets[0].data, Bytes::from_static(b\"Hello\"));\n        assert_eq!(packets[1].packet_id, PacketId::MessageBinary);\n        assert_eq!(packets[1].data, Bytes::from_static(b\"HelloWorld\"));\n        assert_eq!(packets[2].packet_id, PacketId::MessageBinary);\n        assert_eq!(packets[2].data, Bytes::from_static(b\"Hello\"));\n\n        assert_eq!(Bytes::try_from(packets).unwrap(), data);\n    }\n\n    #[test]\n    fn test_packet_id_conversion_and_incompl_packet() -> Result<()> {\n        let sut = Packet::try_from(Bytes::from_static(b\"4\"));\n        assert!(sut.is_err());\n        let _sut = sut.unwrap_err();\n        assert!(matches!(Error::IncompletePacket, _sut));\n\n        assert_eq!(PacketId::MessageBinary.to_string(), \"b\");\n\n        let sut = PacketId::try_from(b'0')?;\n        assert_eq!(sut, PacketId::Open);\n        assert_eq!(sut.to_string(), \"0\");\n\n        let sut = PacketId::try_from(b'1')?;\n        assert_eq!(sut, PacketId::Close);\n        assert_eq!(sut.to_string(), \"1\");\n\n        let sut = PacketId::try_from(b'2')?;\n        assert_eq!(sut, PacketId::Ping);\n        assert_eq!(sut.to_string(), \"2\");\n\n        let sut = PacketId::try_from(b'3')?;\n        assert_eq!(sut, PacketId::Pong);\n        assert_eq!(sut.to_string(), \"3\");\n\n        let sut = PacketId::try_from(b'4')?;\n        assert_eq!(sut, PacketId::Message);\n        assert_eq!(sut.to_string(), \"4\");\n\n        let sut = PacketId::try_from(b'5')?;\n        assert_eq!(sut, PacketId::Upgrade);\n        assert_eq!(sut.to_string(), \"5\");\n\n        let sut = PacketId::try_from(b'6')?;\n        assert_eq!(sut, PacketId::Noop);\n        assert_eq!(sut.to_string(), \"6\");\n\n        let sut = PacketId::try_from(42);\n        assert!(sut.is_err());\n        assert!(matches!(sut.unwrap_err(), Error::InvalidPacketId(42)));\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_handshake_packet() {\n        assert!(\n            HandshakePacket::try_from(Packet::new(PacketId::Message, Bytes::from(\"test\"))).is_err()\n        );\n        let packet = HandshakePacket {\n            ping_interval: 10000,\n            ping_timeout: 1000,\n            sid: \"Test\".to_owned(),\n            upgrades: vec![\"websocket\".to_owned(), \"test\".to_owned()],\n        };\n        let encoded: String = serde_json::to_string(&packet).unwrap();\n\n        assert_eq!(\n            packet,\n            HandshakePacket::try_from(Packet::new(PacketId::Message, Bytes::from(encoded)))\n                .unwrap()\n        );\n    }\n}\n"
  },
  {
    "path": "engineio/src/socket.rs",
    "content": "use crate::callback::OptionalCallback;\nuse crate::transport::TransportType;\n\nuse crate::error::{Error, Result};\nuse crate::packet::{HandshakePacket, Packet, PacketId, Payload};\nuse bytes::Bytes;\nuse std::convert::TryFrom;\nuse std::sync::RwLock;\nuse std::time::Duration;\nuse std::{fmt::Debug, sync::atomic::Ordering};\nuse std::{\n    sync::{atomic::AtomicBool, Arc, Mutex},\n    time::Instant,\n};\n\n/// The default maximum ping timeout as calculated from the pingInterval and pingTimeout.\n/// See https://socket.io/docs/v4/server-options/#pinginterval and\n/// https://socket.io/docs/v4/server-options/#pingtimeout\npub const DEFAULT_MAX_POLL_TIMEOUT: Duration = Duration::from_secs(45);\n\n/// An `engine.io` socket which manages a connection with the server and allows\n/// it to register common callbacks.\n#[derive(Clone)]\npub struct Socket {\n    transport: Arc<TransportType>,\n    on_close: OptionalCallback<()>,\n    on_data: OptionalCallback<Bytes>,\n    on_error: OptionalCallback<String>,\n    on_open: OptionalCallback<()>,\n    on_packet: OptionalCallback<Packet>,\n    connected: Arc<AtomicBool>,\n    last_ping: Arc<Mutex<Instant>>,\n    last_pong: Arc<Mutex<Instant>>,\n    connection_data: Arc<HandshakePacket>,\n    /// Since we get packets in payloads it's possible to have a state where only some of the packets have been consumed.\n    remaining_packets: Arc<RwLock<Option<crate::packet::IntoIter>>>,\n    max_ping_timeout: u64,\n}\n\nimpl Socket {\n    pub(crate) fn new(\n        transport: TransportType,\n        handshake: HandshakePacket,\n        on_close: OptionalCallback<()>,\n        on_data: OptionalCallback<Bytes>,\n        on_error: OptionalCallback<String>,\n        on_open: OptionalCallback<()>,\n        on_packet: OptionalCallback<Packet>,\n    ) -> Self {\n        let max_ping_timeout = handshake.ping_interval + handshake.ping_timeout;\n\n        Socket {\n            on_close,\n            on_data,\n            on_error,\n            on_open,\n            on_packet,\n            transport: Arc::new(transport),\n            connected: Arc::new(AtomicBool::default()),\n            last_ping: Arc::new(Mutex::new(Instant::now())),\n            last_pong: Arc::new(Mutex::new(Instant::now())),\n            connection_data: Arc::new(handshake),\n            remaining_packets: Arc::new(RwLock::new(None)),\n            max_ping_timeout,\n        }\n    }\n\n    /// Opens the connection to a specified server. The first Pong packet is sent\n    /// to the server to trigger the Ping-cycle.\n    pub fn connect(&self) -> Result<()> {\n        // SAFETY: Has valid handshake due to type\n        self.connected.store(true, Ordering::Release);\n\n        if let Some(on_open) = self.on_open.as_ref() {\n            spawn_scoped!(on_open(()));\n        }\n\n        // set the last ping to now and set the connected state\n        *self.last_ping.lock()? = Instant::now();\n\n        // emit a pong packet to keep trigger the ping cycle on the server\n        self.emit(Packet::new(PacketId::Pong, Bytes::new()))?;\n\n        Ok(())\n    }\n\n    pub fn disconnect(&self) -> Result<()> {\n        if let Some(on_close) = self.on_close.as_ref() {\n            spawn_scoped!(on_close(()));\n        }\n\n        // will not succeed when connection to the server is interrupted\n        let _ = self.emit(Packet::new(PacketId::Close, Bytes::new()));\n\n        self.connected.store(false, Ordering::Release);\n\n        Ok(())\n    }\n\n    /// Sends a packet to the server.\n    pub fn emit(&self, packet: Packet) -> Result<()> {\n        if !self.connected.load(Ordering::Acquire) {\n            let error = Error::IllegalActionBeforeOpen();\n            self.call_error_callback(format!(\"{}\", error));\n            return Err(error);\n        }\n\n        let is_binary = packet.packet_id == PacketId::MessageBinary;\n\n        // send a post request with the encoded payload as body\n        // if this is a binary attachment, then send the raw bytes\n        let data: Bytes = if is_binary {\n            packet.data\n        } else {\n            packet.into()\n        };\n\n        if let Err(error) = self.transport.as_transport().emit(data, is_binary) {\n            self.call_error_callback(error.to_string());\n            return Err(error);\n        }\n\n        Ok(())\n    }\n\n    /// Polls for next payload\n    pub(crate) fn poll(&self) -> Result<Option<Packet>> {\n        loop {\n            if self.connected.load(Ordering::Acquire) {\n                if self.remaining_packets.read()?.is_some() {\n                    // SAFETY: checked is some above\n                    let mut iter = self.remaining_packets.write()?;\n                    let iter = iter.as_mut().unwrap();\n                    if let Some(packet) = iter.next() {\n                        return Ok(Some(packet));\n                    }\n                }\n\n                // Iterator has run out of packets, get a new payload.\n                // Make sure that payload is received within time_to_next_ping, as otherwise the heart\n                // stopped beating and we disconnect.\n                let data = self\n                    .transport\n                    .as_transport()\n                    .poll(Duration::from_millis(self.time_to_next_ping()?))?;\n\n                if data.is_empty() {\n                    continue;\n                }\n\n                let payload = Payload::try_from(data)?;\n                let mut iter = payload.into_iter();\n\n                if let Some(packet) = iter.next() {\n                    *self.remaining_packets.write()? = Some(iter);\n                    return Ok(Some(packet));\n                }\n            } else {\n                return Ok(None);\n            }\n        }\n    }\n\n    /// Calls the error callback with a given message.\n    #[inline]\n    fn call_error_callback(&self, text: String) {\n        if let Some(function) = self.on_error.as_ref() {\n            spawn_scoped!(function(text));\n        }\n    }\n\n    // Check if the underlying transport client is connected.\n    pub(crate) fn is_connected(&self) -> Result<bool> {\n        Ok(self.connected.load(Ordering::Acquire))\n    }\n\n    pub(crate) fn pinged(&self) -> Result<()> {\n        *self.last_ping.lock()? = Instant::now();\n        Ok(())\n    }\n\n    /// Returns the time in milliseconds that is left until a new ping must be received.\n    /// This is used to detect whether we have been disconnected from the server.\n    /// See https://socket.io/docs/v4/how-it-works/#disconnection-detection\n    fn time_to_next_ping(&self) -> Result<u64> {\n        match Instant::now().checked_duration_since(*self.last_ping.lock()?) {\n            Some(since_last_ping) => {\n                let since_last_ping = since_last_ping.as_millis() as u64;\n                if since_last_ping > self.max_ping_timeout {\n                    Ok(0)\n                } else {\n                    Ok(self.max_ping_timeout - since_last_ping)\n                }\n            }\n            None => Ok(0),\n        }\n    }\n\n    pub(crate) fn handle_packet(&self, packet: Packet) {\n        if let Some(on_packet) = self.on_packet.as_ref() {\n            spawn_scoped!(on_packet(packet));\n        }\n    }\n\n    pub(crate) fn handle_data(&self, data: Bytes) {\n        if let Some(on_data) = self.on_data.as_ref() {\n            spawn_scoped!(on_data(data));\n        }\n    }\n\n    pub(crate) fn handle_close(&self) {\n        if let Some(on_close) = self.on_close.as_ref() {\n            spawn_scoped!(on_close(()));\n        }\n\n        self.connected.store(false, Ordering::Release);\n    }\n}\n\n#[cfg_attr(tarpaulin, ignore)]\nimpl Debug for Socket {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        f.write_fmt(format_args!(\n            \"EngineSocket(transport: {:?}, on_error: {:?}, on_open: {:?}, on_close: {:?}, on_packet: {:?}, on_data: {:?}, connected: {:?}, last_ping: {:?}, last_pong: {:?}, connection_data: {:?})\",\n            self.transport,\n            self.on_error,\n            self.on_open,\n            self.on_close,\n            self.on_packet,\n            self.on_data,\n            self.connected,\n            self.last_ping,\n            self.last_pong,\n            self.connection_data,\n        ))\n    }\n}\n"
  },
  {
    "path": "engineio/src/transport.rs",
    "content": "use super::transports::{PollingTransport, WebsocketSecureTransport, WebsocketTransport};\nuse crate::error::Result;\nuse adler32::adler32;\nuse bytes::Bytes;\nuse std::time::{Duration, SystemTime};\nuse url::Url;\n\npub trait Transport {\n    /// Sends a packet to the server. This optionally handles sending of a\n    /// socketio binary attachment via the boolean attribute `is_binary_att`.\n    fn emit(&self, data: Bytes, is_binary_att: bool) -> Result<()>;\n\n    /// Performs the server long polling procedure as long as the client is\n    /// connected. This should run separately at all time to ensure proper\n    /// response handling from the server.\n    fn poll(&self, timeout: Duration) -> Result<Bytes>;\n\n    /// Returns start of the url. ex. http://localhost:2998/engine.io/?EIO=4&transport=polling\n    /// Must have EIO and transport already set.\n    fn base_url(&self) -> Result<Url>;\n\n    /// Used to update the base path, like when adding the sid.\n    fn set_base_url(&self, base_url: Url) -> Result<()>;\n\n    /// Full query address\n    fn address(&self) -> Result<Url> {\n        let reader = format!(\"{:#?}\", SystemTime::now());\n        let hash = adler32(reader.as_bytes()).unwrap();\n        let mut url = self.base_url()?;\n        url.query_pairs_mut().append_pair(\"t\", &hash.to_string());\n        Ok(url)\n    }\n}\n\n#[derive(Debug)]\npub enum TransportType {\n    Polling(PollingTransport),\n    WebsocketSecure(WebsocketSecureTransport),\n    Websocket(WebsocketTransport),\n}\n\nimpl From<PollingTransport> for TransportType {\n    fn from(transport: PollingTransport) -> Self {\n        TransportType::Polling(transport)\n    }\n}\n\nimpl From<WebsocketSecureTransport> for TransportType {\n    fn from(transport: WebsocketSecureTransport) -> Self {\n        TransportType::WebsocketSecure(transport)\n    }\n}\n\nimpl From<WebsocketTransport> for TransportType {\n    fn from(transport: WebsocketTransport) -> Self {\n        TransportType::Websocket(transport)\n    }\n}\n\nimpl TransportType {\n    pub fn as_transport(&self) -> &dyn Transport {\n        match self {\n            TransportType::Polling(transport) => transport,\n            TransportType::Websocket(transport) => transport,\n            TransportType::WebsocketSecure(transport) => transport,\n        }\n    }\n}\n\nimpl std::fmt::Debug for dyn Transport {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        f.write_fmt(format_args!(\"Transport(base_url: {:?})\", self.base_url(),))\n    }\n}\n"
  },
  {
    "path": "engineio/src/transports/mod.rs",
    "content": "mod polling;\nmod websocket;\nmod websocket_secure;\n\npub use self::polling::PollingTransport;\npub use self::websocket::WebsocketTransport;\npub use self::websocket_secure::WebsocketSecureTransport;\n"
  },
  {
    "path": "engineio/src/transports/polling.rs",
    "content": "use crate::error::{Error, Result};\nuse crate::transport::Transport;\nuse base64::{engine::general_purpose, Engine as _};\nuse bytes::{BufMut, Bytes, BytesMut};\nuse native_tls::TlsConnector;\nuse reqwest::{\n    blocking::{Client, ClientBuilder},\n    header::HeaderMap,\n};\nuse std::sync::{Arc, RwLock};\nuse std::time::Duration;\nuse url::Url;\n\n#[derive(Debug, Clone)]\npub struct PollingTransport {\n    client: Arc<Client>,\n    base_url: Arc<RwLock<Url>>,\n}\n\nimpl PollingTransport {\n    /// Creates an instance of `PollingTransport`.\n    pub fn new(\n        base_url: Url,\n        tls_config: Option<TlsConnector>,\n        opening_headers: Option<HeaderMap>,\n    ) -> Self {\n        let client = match (tls_config, opening_headers) {\n            (Some(config), Some(map)) => ClientBuilder::new()\n                .use_preconfigured_tls(config)\n                .default_headers(map)\n                .build()\n                .unwrap(),\n            (Some(config), None) => ClientBuilder::new()\n                .use_preconfigured_tls(config)\n                .build()\n                .unwrap(),\n            (None, Some(map)) => ClientBuilder::new().default_headers(map).build().unwrap(),\n            (None, None) => Client::new(),\n        };\n\n        let mut url = base_url;\n        url.query_pairs_mut().append_pair(\"transport\", \"polling\");\n\n        PollingTransport {\n            client: Arc::new(client),\n            base_url: Arc::new(RwLock::new(url)),\n        }\n    }\n}\n\nimpl Transport for PollingTransport {\n    fn emit(&self, data: Bytes, is_binary_att: bool) -> Result<()> {\n        let data_to_send = if is_binary_att {\n            // the binary attachment gets `base64` encoded\n            let mut packet_bytes = BytesMut::with_capacity(data.len() + 1);\n            packet_bytes.put_u8(b'b');\n\n            let encoded_data = general_purpose::STANDARD.encode(data);\n            packet_bytes.put(encoded_data.as_bytes());\n\n            packet_bytes.freeze()\n        } else {\n            data\n        };\n        let status = self\n            .client\n            .post(self.address()?)\n            .body(data_to_send)\n            .send()?\n            .status()\n            .as_u16();\n\n        if status != 200 {\n            let error = Error::IncompleteHttp(status);\n            return Err(error);\n        }\n\n        Ok(())\n    }\n\n    fn poll(&self, timeout: Duration) -> Result<Bytes> {\n        Ok(self\n            .client\n            .get(self.address()?)\n            .timeout(timeout)\n            .send()?\n            .bytes()?)\n    }\n\n    fn base_url(&self) -> Result<Url> {\n        Ok(self.base_url.read()?.clone())\n    }\n\n    fn set_base_url(&self, base_url: Url) -> Result<()> {\n        let mut url = base_url;\n        if !url\n            .query_pairs()\n            .any(|(k, v)| k == \"transport\" && v == \"polling\")\n        {\n            url.query_pairs_mut().append_pair(\"transport\", \"polling\");\n        }\n        *self.base_url.write()? = url;\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use std::str::FromStr;\n    #[test]\n    fn polling_transport_base_url() -> Result<()> {\n        let url = crate::test::engine_io_server()?.to_string();\n        let transport = PollingTransport::new(Url::from_str(&url[..]).unwrap(), None, None);\n        assert_eq!(\n            transport.base_url()?.to_string(),\n            url.clone() + \"?transport=polling\"\n        );\n        transport.set_base_url(Url::parse(\"https://127.0.0.1\")?)?;\n        assert_eq!(\n            transport.base_url()?.to_string(),\n            \"https://127.0.0.1/?transport=polling\"\n        );\n        assert_ne!(transport.base_url()?.to_string(), url);\n\n        transport.set_base_url(Url::parse(\"http://127.0.0.1/?transport=polling\")?)?;\n        assert_eq!(\n            transport.base_url()?.to_string(),\n            \"http://127.0.0.1/?transport=polling\"\n        );\n        assert_ne!(transport.base_url()?.to_string(), url);\n        Ok(())\n    }\n\n    #[test]\n    fn transport_debug() -> Result<()> {\n        let mut url = crate::test::engine_io_server()?;\n        let transport =\n            PollingTransport::new(Url::from_str(&url.to_string()[..]).unwrap(), None, None);\n        url.query_pairs_mut().append_pair(\"transport\", \"polling\");\n        assert_eq!(format!(\"PollingTransport {{ client: {:?}, base_url: RwLock {{ data: {:?}, poisoned: false, .. }} }}\", transport.client, url), format!(\"{:?}\", transport));\n        let test: Box<dyn Transport> = Box::new(transport);\n        assert_eq!(\n            format!(\"Transport(base_url: Ok({:?}))\", url),\n            format!(\"{:?}\", test)\n        );\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "engineio/src/transports/websocket.rs",
    "content": "use crate::{\n    asynchronous::{\n        async_transports::WebsocketTransport as AsyncWebsocketTransport, transport::AsyncTransport,\n    },\n    error::Result,\n    transport::Transport,\n    Error,\n};\nuse bytes::Bytes;\nuse http::HeaderMap;\nuse std::{sync::Arc, time::Duration};\nuse tokio::runtime::Runtime;\nuse url::Url;\n\n#[derive(Clone)]\npub struct WebsocketTransport {\n    runtime: Arc<Runtime>,\n    inner: Arc<AsyncWebsocketTransport>,\n}\n\nimpl WebsocketTransport {\n    /// Creates an instance of `WebsocketTransport`.\n    pub fn new(base_url: Url, headers: Option<HeaderMap>) -> Result<Self> {\n        let runtime = tokio::runtime::Builder::new_current_thread()\n            .enable_all()\n            .build()?;\n\n        let inner = runtime.block_on(AsyncWebsocketTransport::new(base_url, headers))?;\n\n        Ok(WebsocketTransport {\n            runtime: Arc::new(runtime),\n            inner: Arc::new(inner),\n        })\n    }\n\n    /// Sends probe packet to ensure connection is valid, then sends upgrade\n    /// request\n    pub(crate) fn upgrade(&self) -> Result<()> {\n        self.runtime.block_on(async { self.inner.upgrade().await })\n    }\n}\n\nimpl Transport for WebsocketTransport {\n    fn emit(&self, data: Bytes, is_binary_att: bool) -> Result<()> {\n        self.runtime\n            .block_on(async { self.inner.emit(data, is_binary_att).await })\n    }\n\n    fn poll(&self, timeout: Duration) -> Result<Bytes> {\n        self.runtime.block_on(async {\n            let r = match tokio::time::timeout(timeout, self.inner.poll_next()).await {\n                Ok(r) => r,\n                Err(_) => return Err(Error::PingTimeout()),\n            };\n            match r {\n                Ok(b) => b.ok_or(Error::IncompletePacket()),\n                Err(_) => Err(Error::IncompletePacket()),\n            }\n        })\n    }\n\n    fn base_url(&self) -> Result<url::Url> {\n        self.runtime.block_on(async { self.inner.base_url().await })\n    }\n\n    fn set_base_url(&self, url: url::Url) -> Result<()> {\n        self.runtime\n            .block_on(async { self.inner.set_base_url(url).await })\n    }\n}\n\nimpl std::fmt::Debug for WebsocketTransport {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        f.write_fmt(format_args!(\n            \"WebsocketTransport(base_url: {:?})\",\n            self.base_url(),\n        ))\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::ENGINE_IO_VERSION;\n    use std::str::FromStr;\n\n    const TIMEOUT_DURATION: Duration = Duration::from_secs(45);\n\n    fn new() -> Result<WebsocketTransport> {\n        let url = crate::test::engine_io_server()?.to_string()\n            + \"engine.io/?EIO=\"\n            + &ENGINE_IO_VERSION.to_string();\n        WebsocketTransport::new(Url::from_str(&url[..])?, None)\n    }\n\n    #[test]\n    fn websocket_transport_base_url() -> Result<()> {\n        let transport = new()?;\n        let mut url = crate::test::engine_io_server()?;\n        url.set_path(\"/engine.io/\");\n        url.query_pairs_mut()\n            .append_pair(\"EIO\", &ENGINE_IO_VERSION.to_string())\n            .append_pair(\"transport\", \"websocket\");\n        url.set_scheme(\"ws\").unwrap();\n        assert_eq!(transport.base_url()?.to_string(), url.to_string());\n        transport.set_base_url(reqwest::Url::parse(\"https://127.0.0.1\")?)?;\n        assert_eq!(\n            transport.base_url()?.to_string(),\n            \"ws://127.0.0.1/?transport=websocket\"\n        );\n        assert_ne!(transport.base_url()?.to_string(), url.to_string());\n\n        transport.set_base_url(reqwest::Url::parse(\n            \"http://127.0.0.1/?transport=websocket\",\n        )?)?;\n        assert_eq!(\n            transport.base_url()?.to_string(),\n            \"ws://127.0.0.1/?transport=websocket\"\n        );\n        assert_ne!(transport.base_url()?.to_string(), url.to_string());\n        Ok(())\n    }\n\n    #[test]\n    fn websocket_secure_debug() -> Result<()> {\n        let transport = new()?;\n        assert_eq!(\n            format!(\"{:?}\", transport),\n            format!(\"WebsocketTransport(base_url: {:?})\", transport.base_url())\n        );\n        println!(\"{:?}\", transport.poll(TIMEOUT_DURATION).unwrap());\n        println!(\"{:?}\", transport.poll(TIMEOUT_DURATION).unwrap());\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "engineio/src/transports/websocket_secure.rs",
    "content": "use crate::{\n    asynchronous::{\n        async_transports::WebsocketSecureTransport as AsyncWebsocketSecureTransport,\n        transport::AsyncTransport,\n    },\n    error::Result,\n    transport::Transport,\n    Error,\n};\nuse bytes::Bytes;\nuse http::HeaderMap;\nuse native_tls::TlsConnector;\nuse std::{sync::Arc, time::Duration};\nuse tokio::runtime::Runtime;\nuse url::Url;\n\n#[derive(Clone)]\npub struct WebsocketSecureTransport {\n    runtime: Arc<Runtime>,\n    inner: Arc<AsyncWebsocketSecureTransport>,\n}\n\nimpl WebsocketSecureTransport {\n    /// Creates an instance of `WebsocketSecureTransport`.\n    pub fn new(\n        base_url: Url,\n        tls_config: Option<TlsConnector>,\n        headers: Option<HeaderMap>,\n    ) -> Result<Self> {\n        let runtime = tokio::runtime::Builder::new_current_thread()\n            .enable_all()\n            .build()?;\n\n        let inner = runtime.block_on(AsyncWebsocketSecureTransport::new(\n            base_url, tls_config, headers,\n        ))?;\n\n        Ok(WebsocketSecureTransport {\n            runtime: Arc::new(runtime),\n            inner: Arc::new(inner),\n        })\n    }\n\n    /// Sends probe packet to ensure connection is valid, then sends upgrade\n    /// request\n    pub(crate) fn upgrade(&self) -> Result<()> {\n        self.runtime.block_on(async { self.inner.upgrade().await })\n    }\n}\n\nimpl Transport for WebsocketSecureTransport {\n    fn emit(&self, data: Bytes, is_binary_att: bool) -> Result<()> {\n        self.runtime\n            .block_on(async { self.inner.emit(data, is_binary_att).await })\n    }\n\n    fn poll(&self, timeout: Duration) -> Result<Bytes> {\n        self.runtime.block_on(async {\n            let r = match tokio::time::timeout(timeout, self.inner.poll_next()).await {\n                Ok(r) => r,\n                Err(_) => return Err(Error::PingTimeout()),\n            };\n            match r {\n                Ok(b) => b.ok_or(Error::IncompletePacket()),\n                Err(_) => Err(Error::IncompletePacket()),\n            }\n        })\n    }\n\n    fn base_url(&self) -> Result<url::Url> {\n        self.runtime.block_on(async { self.inner.base_url().await })\n    }\n\n    fn set_base_url(&self, url: url::Url) -> Result<()> {\n        self.runtime\n            .block_on(async { self.inner.set_base_url(url).await })\n    }\n}\n\nimpl std::fmt::Debug for WebsocketSecureTransport {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        f.write_fmt(format_args!(\n            \"WebsocketSecureTransport(base_url: {:?})\",\n            self.base_url(),\n        ))\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::ENGINE_IO_VERSION;\n    use std::str::FromStr;\n    fn new() -> Result<WebsocketSecureTransport> {\n        let url = crate::test::engine_io_server_secure()?.to_string()\n            + \"engine.io/?EIO=\"\n            + &ENGINE_IO_VERSION.to_string();\n        WebsocketSecureTransport::new(\n            Url::from_str(&url[..])?,\n            Some(crate::test::tls_connector()?),\n            None,\n        )\n    }\n\n    #[test]\n    fn websocket_secure_transport_base_url() -> Result<()> {\n        let transport = new()?;\n        let mut url = crate::test::engine_io_server_secure()?;\n        url.set_path(\"/engine.io/\");\n        url.query_pairs_mut()\n            .append_pair(\"EIO\", &ENGINE_IO_VERSION.to_string())\n            .append_pair(\"transport\", \"websocket\");\n        url.set_scheme(\"wss\").unwrap();\n        assert_eq!(transport.base_url()?.to_string(), url.to_string());\n        transport.set_base_url(reqwest::Url::parse(\"https://127.0.0.1\")?)?;\n        assert_eq!(\n            transport.base_url()?.to_string(),\n            \"wss://127.0.0.1/?transport=websocket\"\n        );\n        assert_ne!(transport.base_url()?.to_string(), url.to_string());\n\n        transport.set_base_url(reqwest::Url::parse(\n            \"http://127.0.0.1/?transport=websocket\",\n        )?)?;\n        assert_eq!(\n            transport.base_url()?.to_string(),\n            \"wss://127.0.0.1/?transport=websocket\"\n        );\n        assert_ne!(transport.base_url()?.to_string(), url.to_string());\n        Ok(())\n    }\n\n    #[test]\n    fn websocket_secure_debug() -> Result<()> {\n        let transport = new()?;\n        assert_eq!(\n            format!(\"{:?}\", transport),\n            format!(\n                \"WebsocketSecureTransport(base_url: {:?})\",\n                transport.base_url()\n            )\n        );\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "socketio/Cargo.toml",
    "content": "[package]\nname = \"rust_socketio\"\nversion = \"0.6.0\"\nauthors = [\"Bastian Kersting <bastian@cmbt.de>\"]\nedition = \"2021\"\ndescription = \"An implementation of a socketio client written in rust.\"\nreadme = \"../README.md\"\nrepository = \"https://github.com/1c3t3a/rust-socketio\"\nkeywords = [\"socketio\", \"engineio\", \"network\", \"protocol\", \"client\"]\ncategories = [\"network-programming\", \"web-programming\", \"web-programming::websocket\"]\nlicense = \"MIT\"\n\n[package.metadata.docs.rs]\nall-features = true\n\n[dependencies]\nrust_engineio = { version = \"0.6.0\", path = \"../engineio\" }\nbase64 = \"0.22.1\"\nbytes = \"1\"\nbackoff = \"0.4\"\nrand = \"0.8.5\"\nadler32 = \"1.2.0\"\nserde_json = \"1.0\"\nthiserror = \"1.0\"\nnative-tls = \"0.2.12\"\nurl = \"2.5.4\"\ntokio = { version = \"1.40.0\", optional = true }\nfutures-util = { version = \"0.3\", default-features = false, features = [\"sink\"], optional = true }\nasync-stream = { version = \"0.3.6\", optional = true }\nlog = \"0.4.22\"\nserde = \"1.0.215\"\n\n[dev-dependencies]\ncargo-tarpaulin = \"0.18.5\"\nserial_test = \"3.0.0\"\n\n[dev-dependencies.tokio]\nversion = \"1.40.0\"\n# we need the `#[tokio::test]` macro\nfeatures = [\"macros\", \"rt-multi-thread\"]\n\n[features]\ndefault = []\nasync-callbacks = [\"rust_engineio/async-callbacks\"]\nasync = [\"async-callbacks\", \"rust_engineio/async\", \"tokio\", \"futures-util\", \"async-stream\"]\n\n[[example]]\nname = \"async\"\npath = \"examples/async.rs\"\nrequired-features = [\"async\"]\n"
  },
  {
    "path": "socketio/examples/async.rs",
    "content": "use futures_util::FutureExt;\nuse rust_socketio::{\n    asynchronous::{Client, ClientBuilder},\n    Payload,\n};\nuse serde_json::json;\nuse std::time::Duration;\n\n#[tokio::main]\nasync fn main() {\n    // define a callback which is called when a payload is received\n    // this callback gets the payload as well as an instance of the\n    // socket to communicate with the server\n    let callback = |payload: Payload, socket: Client| {\n        async move {\n            match payload {\n                Payload::Text(values) => println!(\"Received: {:#?}\", values),\n                Payload::Binary(bin_data) => println!(\"Received bytes: {:#?}\", bin_data),\n                // Use Payload::Text instead\n                #[allow(deprecated)]\n                Payload::String(str) => println!(\"Received: {}\", str),\n            }\n            socket\n                .emit(\"test\", json!({\"got ack\": true}))\n                .await\n                .expect(\"Server unreachable\");\n        }\n        .boxed()\n    };\n\n    // get a socket that is connected to the admin namespace\n    let socket = ClientBuilder::new(\"http://localhost:4200/\")\n        .namespace(\"/admin\")\n        .on(\"test\", callback)\n        .on(\"error\", |err, _| {\n            async move { eprintln!(\"Error: {:#?}\", err) }.boxed()\n        })\n        .connect()\n        .await\n        .expect(\"Connection failed\");\n\n    // emit to the \"foo\" event\n    let json_payload = json!({\"token\": 123});\n    socket\n        .emit(\"foo\", json_payload)\n        .await\n        .expect(\"Server unreachable\");\n\n    // define a callback, that's executed when the ack got acked\n    let ack_callback = |message: Payload, _: Client| {\n        async move {\n            println!(\"Yehaa! My ack got acked?\");\n            println!(\"Ack data: {:#?}\", message);\n        }\n        .boxed()\n    };\n\n    let json_payload = json!({\"myAckData\": 123});\n    // emit with an ack\n    socket\n        .emit_with_ack(\"test\", json_payload, Duration::from_secs(2), ack_callback)\n        .await\n        .expect(\"Server unreachable\");\n\n    socket.disconnect().await.expect(\"Disconnect failed\");\n}\n"
  },
  {
    "path": "socketio/examples/callback.rs",
    "content": "use rust_socketio::{ClientBuilder, Event, Payload, RawClient};\nuse serde_json::json;\n\nfn handle_foo(payload: Payload, socket: RawClient) -> () {\n    socket.emit(\"bar\", payload).expect(\"Server unreachable\")\n}\n\nfn main() {\n    // define a callback which is called when a payload is received\n    // this callback gets the payload as well as an instance of the\n    // socket to communicate with the server\n    let handle_test = |payload: Payload, socket: RawClient| {\n        match payload {\n            Payload::Text(text) => println!(\"Received json: {:#?}\", text),\n            Payload::Binary(bin_data) => println!(\"Received bytes: {:#?}\", bin_data),\n            #[allow(deprecated)]\n            // Use Payload::Text instead\n            Payload::String(str) => println!(\"Received string: {}\", str),\n        }\n        socket\n            .emit(\"test\", json!({\"got ack\": true}))\n            .expect(\"Server unreachable\")\n    };\n\n    // get a socket that is connected to the admin namespace\n    let socket = ClientBuilder::new(\"http://localhost:4200\")\n        .namespace(\"/admin\")\n        // Saved closure\n        .on(\"test\", handle_test)\n        // Inline closure\n        .on(\"error\", |err, _| eprintln!(\"Error: {:#?}\", err))\n        // Function call with signature (payload: Payload, socket: RawClient) -> ()\n        .on(\"foo\", handle_foo)\n        // Reserved event names are case insensitive\n        .on(\"oPeN\", |_, _| println!(\"Connected\"))\n        // Custom names are case sensitive\n        .on(\"Test\", |_, _| println!(\"TesT received\"))\n        // Event specified by enum\n        .on(Event::Close, |_, socket| {\n            println!(\"Socket Closed\");\n            socket\n                .emit(\"message\", json!({\"foo\": \"Hello server\"}))\n                .expect(\"Error emitting\");\n        })\n        .connect()\n        .expect(\"Connection failed\");\n\n    // use the socket\n\n    socket.disconnect().expect(\"Disconnect failed\")\n}\n"
  },
  {
    "path": "socketio/examples/readme.rs",
    "content": "use rust_socketio::{ClientBuilder, Payload, RawClient};\nuse serde_json::json;\nuse std::time::Duration;\n\nfn main() {\n    // define a callback which is called when a payload is received\n    // this callback gets the payload as well as an instance of the\n    // socket to communicate with the server\n    let callback = |payload: Payload, socket: RawClient| {\n        match payload {\n            #[allow(deprecated)]\n            Payload::String(str) => println!(\"Received: {}\", str),\n            Payload::Text(text) => println!(\"Received json: {:#?}\", text),\n            Payload::Binary(bin_data) => println!(\"Received bytes: {:#?}\", bin_data),\n        }\n        socket\n            .emit(\"test\", json!({\"got ack\": true}))\n            .expect(\"Server unreachable\")\n    };\n\n    // get a socket that is connected to the admin namespace\n    let socket = ClientBuilder::new(\"http://localhost:4200\")\n        .namespace(\"/admin\")\n        .on(\"test\", callback)\n        .on(\"error\", |err, _| eprintln!(\"Error: {:#?}\", err))\n        .connect()\n        .expect(\"Connection failed\");\n\n    // emit to the \"foo\" event\n    let json_payload = json!({\"token\": 123});\n    socket\n        .emit(\"foo\", json_payload)\n        .expect(\"Server unreachable\");\n\n    // define a callback, that's executed when the ack got acked\n    let ack_callback = |message: Payload, _| {\n        println!(\"Yehaa! My ack got acked?\");\n        println!(\"Ack data: {:#?}\", message);\n    };\n\n    let json_payload = json!({\"myAckData\": 123});\n    // emit with an ack\n\n    socket\n        .emit_with_ack(\"test\", json_payload, Duration::from_secs(2), ack_callback)\n        .expect(\"Server unreachable\");\n\n    socket.disconnect().expect(\"Disconnect failed\")\n}\n"
  },
  {
    "path": "socketio/examples/secure.rs",
    "content": "use native_tls::Certificate;\nuse native_tls::TlsConnector;\nuse rust_socketio::ClientBuilder;\nuse std::fs::File;\nuse std::io::Read;\n\nfn main() {\n    // In case a trusted CA is needed that isn't in the trust chain.\n    let cert_path = \"ca.crt\";\n    let mut cert_file = File::open(cert_path).expect(\"Failed to open cert\");\n    let mut buf = vec![];\n    cert_file\n        .read_to_end(&mut buf)\n        .expect(\"Failed to read cert\");\n    let cert: Certificate = Certificate::from_pem(&buf[..]).unwrap();\n\n    let tls_connector = TlsConnector::builder()\n        .add_root_certificate(cert)\n        .build()\n        .expect(\"Failed to build TLS Connector\");\n\n    let socket = ClientBuilder::new(\"https://localhost:4200\")\n        .tls_config(tls_connector)\n        // Not strictly required for HTTPS\n        .opening_header(\"HOST\", \"localhost\")\n        .on(\"error\", |err, _| eprintln!(\"Error: {:#?}\", err))\n        .connect()\n        .expect(\"Connection failed\");\n\n    // use the socket\n\n    socket.disconnect().expect(\"Disconnect failed\")\n}\n"
  },
  {
    "path": "socketio/src/asynchronous/client/ack.rs",
    "content": "use std::time::Duration;\n\nuse crate::asynchronous::client::callback::Callback;\nuse tokio::time::Instant;\n\nuse super::callback::DynAsyncCallback;\n\n/// Represents an `Ack` as given back to the caller. Holds the internal `id` as\n/// well as the current ack'ed state. Holds data which will be accessible as\n/// soon as the ack'ed state is set to true. An `Ack` that didn't get ack'ed\n/// won't contain data.\n#[derive(Debug)]\npub(crate) struct Ack {\n    pub id: i32,\n    pub timeout: Duration,\n    pub time_started: Instant,\n    pub callback: Callback<DynAsyncCallback>,\n}\n"
  },
  {
    "path": "socketio/src/asynchronous/client/builder.rs",
    "content": "use futures_util::future::BoxFuture;\nuse log::trace;\nuse native_tls::TlsConnector;\nuse rust_engineio::{\n    asynchronous::ClientBuilder as EngineIoClientBuilder,\n    header::{HeaderMap, HeaderValue},\n};\nuse std::collections::HashMap;\nuse url::Url;\n\nuse crate::{error::Result, Event, Payload, TransportType};\n\nuse super::{\n    callback::{\n        Callback, DynAsyncAnyCallback, DynAsyncCallback, DynAsyncReconnectSettingsCallback,\n    },\n    client::{Client, ReconnectSettings},\n};\nuse crate::asynchronous::socket::Socket as InnerSocket;\n\n/// A builder class for a `socket.io` socket. This handles setting up the client and\n/// configuring the callback, the namespace and metadata of the socket. If no\n/// namespace is specified, the default namespace `/` is taken. The `connect` method\n/// acts the `build` method and returns a connected [`Client`].\npub struct ClientBuilder {\n    pub(crate) address: String,\n    pub(crate) on: HashMap<Event, Callback<DynAsyncCallback>>,\n    pub(crate) on_any: Option<Callback<DynAsyncAnyCallback>>,\n    pub(crate) on_reconnect: Option<Callback<DynAsyncReconnectSettingsCallback>>,\n    pub(crate) namespace: String,\n    tls_config: Option<TlsConnector>,\n    pub(crate) opening_headers: Option<HeaderMap>,\n    transport_type: TransportType,\n    pub(crate) auth: Option<serde_json::Value>,\n    pub(crate) reconnect: bool,\n    pub(crate) reconnect_on_disconnect: bool,\n    // None implies infinite attempts\n    pub(crate) max_reconnect_attempts: Option<u8>,\n    pub(crate) reconnect_delay_min: u64,\n    pub(crate) reconnect_delay_max: u64,\n}\n\nimpl ClientBuilder {\n    /// Create as client builder from a URL. URLs must be in the form\n    /// `[ws or wss or http or https]://[domain]:[port]/[path]`. The\n    /// path of the URL is optional and if no port is given, port 80\n    /// will be used.\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{Payload, asynchronous::{ClientBuilder, Client}};\n    /// use serde_json::json;\n    /// use futures_util::future::FutureExt;\n    ///\n    ///\n    /// #[tokio::main]\n    /// async fn main() {\n    ///     let callback = |payload: Payload, socket: Client| {\n    ///         async move {\n    ///             match payload {\n    ///                 Payload::Text(values) => println!(\"Received: {:#?}\", values),\n    ///                 Payload::Binary(bin_data) => println!(\"Received bytes: {:#?}\", bin_data),\n    ///                 // This is deprecated, use Payload::Text instead\n    ///                 Payload::String(str) => println!(\"Received: {}\", str),\n    ///             }\n    ///         }.boxed()\n    ///     };\n    ///\n    ///     let mut socket = ClientBuilder::new(\"http://localhost:4200\")\n    ///         .namespace(\"/admin\")\n    ///         .on(\"test\", callback)\n    ///         .connect()\n    ///         .await\n    ///         .expect(\"error while connecting\");\n    ///\n    ///     // use the socket\n    ///     let json_payload = json!({\"token\": 123});\n    ///\n    ///     let result = socket.emit(\"foo\", json_payload).await;\n    ///\n    ///     assert!(result.is_ok());\n    /// }\n    /// ```\n    pub fn new<T: Into<String>>(address: T) -> Self {\n        Self {\n            address: address.into(),\n            on: HashMap::new(),\n            on_any: None,\n            on_reconnect: None,\n            namespace: \"/\".to_owned(),\n            tls_config: None,\n            opening_headers: None,\n            transport_type: TransportType::Any,\n            auth: None,\n            reconnect: true,\n            reconnect_on_disconnect: false,\n            // None implies infinite attempts\n            max_reconnect_attempts: None,\n            reconnect_delay_min: 1000,\n            reconnect_delay_max: 5000,\n        }\n    }\n\n    /// Sets the target namespace of the client. The namespace should start\n    /// with a leading `/`. Valid examples are e.g. `/admin`, `/foo`.\n    /// If the String provided doesn't start with a leading `/`, it is\n    /// added manually.\n    pub fn namespace<T: Into<String>>(mut self, namespace: T) -> Self {\n        let mut nsp = namespace.into();\n        if !nsp.starts_with('/') {\n            nsp = \"/\".to_owned() + &nsp;\n            trace!(\"Added `/` to the given namespace: {}\", nsp);\n        }\n        self.namespace = nsp;\n        self\n    }\n\n    /// Registers a new callback for a certain [`crate::event::Event`]. The event could either be\n    /// one of the common events like `message`, `error`, `open`, `close` or a custom\n    /// event defined by a string, e.g. `onPayment` or `foo`.\n    ///\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{asynchronous::ClientBuilder, Payload};\n    /// use futures_util::FutureExt;\n    ///\n    ///  #[tokio::main]\n    /// async fn main() {\n    ///     let socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///         .namespace(\"/admin\")\n    ///         .on(\"test\", |payload: Payload, _| {\n    ///             async move {\n    ///                 match payload {\n    ///                     Payload::Text(values) => println!(\"Received: {:#?}\", values),\n    ///                     Payload::Binary(bin_data) => println!(\"Received bytes: {:#?}\", bin_data),\n    ///                     // This is deprecated, use Payload::Text instead\n    ///                     Payload::String(str) => println!(\"Received: {}\", str),\n    ///                 }\n    ///             }\n    ///             .boxed()\n    ///         })\n    ///         .on(\"error\", |err, _| async move { eprintln!(\"Error: {:#?}\", err) }.boxed())\n    ///         .connect()\n    ///         .await;\n    /// }\n    /// ```\n    ///\n    /// # Issues with type inference for the callback method\n    ///\n    /// Currently stable Rust does not contain types like `AsyncFnMut`.\n    /// That is why this library uses the type `FnMut(..) -> BoxFuture<_>`,\n    /// which basically represents a closure or function that returns a\n    /// boxed future that can be executed in an async executor.\n    /// The complicated constraints for the callback function\n    /// bring the Rust compiler to it's limits, resulting in confusing error\n    /// messages when passing in a variable that holds a closure (to the `on` method).\n    /// In order to make sure type inference goes well, the [`futures_util::FutureExt::boxed`]\n    /// method can be used on an async block (the future) to make sure the return type\n    /// is conform with the generic requirements. An example can be found here:\n    ///\n    /// ```rust\n    /// use rust_socketio::{asynchronous::ClientBuilder, Payload};\n    /// use futures_util::FutureExt;\n    ///\n    /// #[tokio::main]\n    /// async fn main() {\n    ///     let callback = |payload: Payload, _| {\n    ///             async move {\n    ///                 match payload {\n    ///                     Payload::Text(values) => println!(\"Received: {:#?}\", values),\n    ///                     Payload::Binary(bin_data) => println!(\"Received bytes: {:#?}\", bin_data),\n    ///                     // This is deprecated use Payload::Text instead\n    ///                     Payload::String(str) => println!(\"Received: {}\", str),\n    ///                 }\n    ///             }\n    ///             .boxed() // <-- this makes sure we end up with a `BoxFuture<_>`\n    ///         };\n    ///\n    ///     let socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///         .namespace(\"/admin\")\n    ///         .on(\"test\", callback)\n    ///         .connect()\n    ///         .await;\n    /// }\n    /// ```\n    ///\n    #[cfg(feature = \"async-callbacks\")]\n    pub fn on<T: Into<Event>, F>(mut self, event: T, callback: F) -> Self\n    where\n        F: for<'a> std::ops::FnMut(Payload, Client) -> BoxFuture<'static, ()>\n            + 'static\n            + Send\n            + Sync,\n    {\n        self.on\n            .insert(event.into(), Callback::<DynAsyncCallback>::new(callback));\n        self\n    }\n\n    /// Registers a callback for reconnect events. The event handler must return\n    /// a [ReconnectSettings] struct with the settings that should be updated.\n    ///\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{asynchronous::{ClientBuilder, ReconnectSettings}};\n    /// use futures_util::future::FutureExt;\n    /// use serde_json::json;\n    ///\n    /// #[tokio::main]\n    /// async fn main() {\n    ///     let client = ClientBuilder::new(\"http://localhost:4200/\")\n    ///         .namespace(\"/admin\")\n    ///         .on_reconnect(|| {\n    ///             async {\n    ///                 let mut settings = ReconnectSettings::new();\n    ///                 settings.address(\"http://server?test=123\");\n    ///                 settings.auth(json!({ \"token\": \"abc\" }));\n    ///                 settings.opening_header(\"TRAIL\", \"abc-123\");\n    ///                 settings\n    ///             }.boxed()\n    ///         })\n    ///         .connect()\n    ///         .await;\n    /// }\n    /// ```\n    pub fn on_reconnect<F>(mut self, callback: F) -> Self\n    where\n        F: for<'a> std::ops::FnMut() -> BoxFuture<'static, ReconnectSettings>\n            + 'static\n            + Send\n            + Sync,\n    {\n        self.on_reconnect = Some(Callback::<DynAsyncReconnectSettingsCallback>::new(callback));\n        self\n    }\n\n    /// Registers a Callback for all [`crate::event::Event::Custom`] and [`crate::event::Event::Message`].\n    ///\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{asynchronous::ClientBuilder, Payload};\n    /// use futures_util::future::FutureExt;\n    ///\n    /// #[tokio::main]\n    /// async fn main() {\n    ///     let client = ClientBuilder::new(\"http://localhost:4200/\")\n    ///         .namespace(\"/admin\")\n    ///         .on_any(|event, payload, _client| {\n    ///             async {\n    ///                 if let Payload::String(str) = payload {\n    ///                     println!(\"{}: {}\", String::from(event), str);\n    ///                 }\n    ///             }.boxed()\n    ///         })\n    ///         .connect()\n    ///         .await;\n    /// }\n    /// ```\n    pub fn on_any<F>(mut self, callback: F) -> Self\n    where\n        F: for<'a> FnMut(Event, Payload, Client) -> BoxFuture<'static, ()> + 'static + Send + Sync,\n    {\n        self.on_any = Some(Callback::<DynAsyncAnyCallback>::new(callback));\n        self\n    }\n\n    /// Uses a preconfigured TLS connector for secure communication. This configures\n    /// both the `polling` as well as the `websocket` transport type.\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{asynchronous::ClientBuilder, Payload};\n    /// use native_tls::TlsConnector;\n    /// use futures_util::future::FutureExt;\n    ///\n    /// #[tokio::main]\n    /// async fn main() {\n    ///     let tls_connector =  TlsConnector::builder()\n    ///                .use_sni(true)\n    ///                .build()\n    ///             .expect(\"Found illegal configuration\");\n    ///\n    ///     let socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///         .namespace(\"/admin\")\n    ///         .on(\"error\", |err, _| async move { eprintln!(\"Error: {:#?}\", err) }.boxed())\n    ///         .tls_config(tls_connector)\n    ///         .connect()\n    ///         .await;\n    /// }\n    /// ```\n    pub fn tls_config(mut self, tls_config: TlsConnector) -> Self {\n        self.tls_config = Some(tls_config);\n        self\n    }\n\n    /// Sets custom http headers for the opening request. The headers will be passed to the underlying\n    /// transport type (either websockets or polling) and then get passed with every request thats made.\n    /// via the transport layer.\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{asynchronous::ClientBuilder, Payload};\n    /// use futures_util::future::FutureExt;\n    ///\n    /// #[tokio::main]\n    /// async fn main() {\n    ///     let socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///         .namespace(\"/admin\")\n    ///         .on(\"error\", |err, _| async move { eprintln!(\"Error: {:#?}\", err) }.boxed())\n    ///         .opening_header(\"accept-encoding\", \"application/json\")\n    ///         .connect()\n    ///         .await;\n    /// }\n    /// ```\n    pub fn opening_header<T: Into<HeaderValue>, K: Into<String>>(mut self, key: K, val: T) -> Self {\n        match self.opening_headers {\n            Some(ref mut map) => {\n                map.insert(key.into(), val.into());\n            }\n            None => {\n                let mut map = HeaderMap::default();\n                map.insert(key.into(), val.into());\n                self.opening_headers = Some(map);\n            }\n        }\n        self\n    }\n\n    /// Sets authentification data sent in the opening request.\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{asynchronous::ClientBuilder};\n    /// use serde_json::json;\n    /// use futures_util::future::FutureExt;\n    ///\n    /// #[tokio::main]\n    /// async fn main() {\n    ///     let socket = ClientBuilder::new(\"http://localhost:4204/\")\n    ///         .namespace(\"/admin\")\n    ///         .auth(json!({ \"password\": \"1337\" }))\n    ///         .on(\"error\", |err, _| async move { eprintln!(\"Error: {:#?}\", err) }.boxed())\n    ///         .connect()\n    ///         .await;\n    /// }\n    /// ```\n    pub fn auth<T: Into<serde_json::Value>>(mut self, auth: T) -> Self {\n        self.auth = Some(auth.into());\n\n        self\n    }\n\n    /// Specifies which EngineIO [`TransportType`] to use.\n    ///\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{asynchronous::ClientBuilder, TransportType};\n    ///\n    /// #[tokio::main]\n    /// async fn main() {\n    ///     let socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///         // Use websockets to handshake and connect.\n    ///         .transport_type(TransportType::Websocket)\n    ///         .connect()\n    ///         .await\n    ///         .expect(\"connection failed\");\n    /// }\n    /// ```\n    pub fn transport_type(mut self, transport_type: TransportType) -> Self {\n        self.transport_type = transport_type;\n\n        self\n    }\n\n    /// If set to `false` do not try to reconnect on network errors. Defaults to\n    /// `true`\n    pub fn reconnect(mut self, reconnect: bool) -> Self {\n        self.reconnect = reconnect;\n        self\n    }\n\n    /// If set to `true` try to reconnect when the server disconnects the\n    /// client. Defaults to `false`\n    pub fn reconnect_on_disconnect(mut self, reconnect_on_disconnect: bool) -> Self {\n        self.reconnect_on_disconnect = reconnect_on_disconnect;\n        self\n    }\n\n    /// Sets the minimum and maximum delay between reconnection attempts\n    pub fn reconnect_delay(mut self, min: u64, max: u64) -> Self {\n        self.reconnect_delay_min = min;\n        self.reconnect_delay_max = max;\n        self\n    }\n\n    /// Sets the maximum number of times to attempt reconnections. Defaults to\n    /// an infinite number of attempts\n    pub fn max_reconnect_attempts(mut self, reconnect_attempts: u8) -> Self {\n        self.max_reconnect_attempts = Some(reconnect_attempts);\n        self\n    }\n\n    /// Connects the socket to a certain endpoint. This returns a connected\n    /// [`Client`] instance. This method returns an [`std::result::Result::Err`]\n    /// value if something goes wrong during connection. Also starts a separate\n    /// thread to start polling for packets. Used with callbacks.\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{asynchronous::ClientBuilder, Payload};\n    /// use serde_json::json;\n    /// use futures_util::future::FutureExt;\n    ///\n    /// #[tokio::main]\n    /// async fn main() {\n    ///     let mut socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///         .namespace(\"/admin\")\n    ///         .on(\"error\", |err, _| async move { eprintln!(\"Error: {:#?}\", err) }.boxed())\n    ///         .connect()\n    ///         .await\n    ///         .expect(\"connection failed\");\n    ///\n    ///     // use the socket\n    ///     let json_payload = json!({\"token\": 123});\n    ///\n    ///     let result = socket.emit(\"foo\", json_payload).await;\n    ///\n    ///     assert!(result.is_ok());\n    /// }\n    /// ```\n    pub async fn connect(self) -> Result<Client> {\n        let mut socket = self.connect_manual().await?;\n        socket.poll_stream().await?;\n\n        Ok(socket)\n    }\n\n    /// Creates a new Socket that can be used for reconnections\n    pub(crate) async fn inner_create(&self) -> Result<InnerSocket> {\n        let mut url = Url::parse(&self.address)?;\n\n        if url.path() == \"/\" {\n            url.set_path(\"/socket.io/\");\n        }\n\n        let mut builder = EngineIoClientBuilder::new(url);\n\n        if let Some(tls_config) = &self.tls_config {\n            builder = builder.tls_config(tls_config.to_owned());\n        }\n        if let Some(headers) = &self.opening_headers {\n            builder = builder.headers(headers.to_owned());\n        }\n\n        let engine_client = match self.transport_type {\n            TransportType::Any => builder.build_with_fallback().await?,\n            TransportType::Polling => builder.build_polling().await?,\n            TransportType::Websocket => builder.build_websocket().await?,\n            TransportType::WebsocketUpgrade => builder.build_websocket_with_upgrade().await?,\n        };\n\n        let inner_socket = InnerSocket::new(engine_client)?;\n        Ok(inner_socket)\n    }\n\n    //TODO: 0.3.X stabilize\n    pub(crate) async fn connect_manual(self) -> Result<Client> {\n        let inner_socket = self.inner_create().await?;\n\n        let socket = Client::new(inner_socket, self)?;\n        socket.connect().await?;\n\n        Ok(socket)\n    }\n}\n"
  },
  {
    "path": "socketio/src/asynchronous/client/callback.rs",
    "content": "use futures_util::future::BoxFuture;\nuse std::{\n    fmt::Debug,\n    ops::{Deref, DerefMut},\n};\n\nuse crate::{Event, Payload};\n\nuse super::client::{Client, ReconnectSettings};\n\n/// Internal type, provides a way to store futures and return them in a boxed manner.\npub(crate) type DynAsyncCallback =\n    Box<dyn for<'a> FnMut(Payload, Client) -> BoxFuture<'static, ()> + 'static + Send + Sync>;\n\npub(crate) type DynAsyncAnyCallback = Box<\n    dyn for<'a> FnMut(Event, Payload, Client) -> BoxFuture<'static, ()> + 'static + Send + Sync,\n>;\n\npub(crate) type DynAsyncReconnectSettingsCallback =\n    Box<dyn for<'a> FnMut() -> BoxFuture<'static, ReconnectSettings> + 'static + Send + Sync>;\n\npub(crate) struct Callback<T> {\n    inner: T,\n}\n\nimpl<T> Debug for Callback<T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(\"Callback\")\n    }\n}\n\nimpl Deref for Callback<DynAsyncCallback> {\n    type Target =\n        dyn for<'a> FnMut(Payload, Client) -> BoxFuture<'static, ()> + 'static + Sync + Send;\n\n    fn deref(&self) -> &Self::Target {\n        self.inner.as_ref()\n    }\n}\n\nimpl DerefMut for Callback<DynAsyncCallback> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.inner.as_mut()\n    }\n}\n\nimpl Callback<DynAsyncCallback> {\n    pub(crate) fn new<T>(callback: T) -> Self\n    where\n        T: for<'a> FnMut(Payload, Client) -> BoxFuture<'static, ()> + 'static + Sync + Send,\n    {\n        Callback {\n            inner: Box::new(callback),\n        }\n    }\n}\n\nimpl Deref for Callback<DynAsyncAnyCallback> {\n    type Target =\n        dyn for<'a> FnMut(Event, Payload, Client) -> BoxFuture<'static, ()> + 'static + Sync + Send;\n\n    fn deref(&self) -> &Self::Target {\n        self.inner.as_ref()\n    }\n}\n\nimpl DerefMut for Callback<DynAsyncAnyCallback> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.inner.as_mut()\n    }\n}\n\nimpl Callback<DynAsyncAnyCallback> {\n    pub(crate) fn new<T>(callback: T) -> Self\n    where\n        T: for<'a> FnMut(Event, Payload, Client) -> BoxFuture<'static, ()> + 'static + Sync + Send,\n    {\n        Callback {\n            inner: Box::new(callback),\n        }\n    }\n}\n\nimpl Deref for Callback<DynAsyncReconnectSettingsCallback> {\n    type Target =\n        dyn for<'a> FnMut() -> BoxFuture<'static, ReconnectSettings> + 'static + Sync + Send;\n\n    fn deref(&self) -> &Self::Target {\n        self.inner.as_ref()\n    }\n}\n\nimpl DerefMut for Callback<DynAsyncReconnectSettingsCallback> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.inner.as_mut()\n    }\n}\n\nimpl Callback<DynAsyncReconnectSettingsCallback> {\n    pub(crate) fn new<T>(callback: T) -> Self\n    where\n        T: for<'a> FnMut() -> BoxFuture<'static, ReconnectSettings> + 'static + Sync + Send,\n    {\n        Callback {\n            inner: Box::new(callback),\n        }\n    }\n}\n"
  },
  {
    "path": "socketio/src/asynchronous/client/client.rs",
    "content": "use std::{ops::DerefMut, pin::Pin, sync::Arc};\n\nuse backoff::{backoff::Backoff, ExponentialBackoffBuilder};\nuse futures_util::{future::BoxFuture, stream, Stream, StreamExt};\nuse log::{error, trace};\nuse rand::{thread_rng, Rng};\nuse rust_engineio::header::{HeaderMap, HeaderValue};\nuse serde_json::Value;\nuse tokio::{\n    sync::RwLock,\n    time::{sleep, Duration, Instant},\n};\n\nuse super::{\n    ack::Ack,\n    builder::ClientBuilder,\n    callback::{Callback, DynAsyncCallback},\n};\nuse crate::{\n    asynchronous::socket::Socket as InnerSocket,\n    error::{Error, Result},\n    packet::{Packet, PacketId},\n    CloseReason, Event, Payload,\n};\n\n#[derive(Default)]\nenum DisconnectReason {\n    /// There is no known reason for the disconnect; likely a network error\n    #[default]\n    Unknown,\n    /// The user disconnected manually\n    Manual,\n    /// The server disconnected\n    Server,\n}\n\n/// Settings that can be updated before reconnecting to a server\n#[derive(Default)]\npub struct ReconnectSettings {\n    address: Option<String>,\n    auth: Option<serde_json::Value>,\n    headers: Option<HeaderMap>,\n}\n\nimpl ReconnectSettings {\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Sets the URL that will be used when reconnecting to the server\n    pub fn address<T>(&mut self, address: T) -> &mut Self\n    where\n        T: Into<String>,\n    {\n        self.address = Some(address.into());\n        self\n    }\n\n    /// Sets the authentication data that will be send in the opening request\n    pub fn auth(&mut self, auth: serde_json::Value) {\n        self.auth = Some(auth);\n    }\n\n    /// Adds an http header to a container that is going to completely replace opening headers on reconnect.\n    /// If there are no headers set in `ReconnectSettings`, client will use headers initially set via the builder.\n    pub fn opening_header<T: Into<HeaderValue>, K: Into<String>>(\n        &mut self,\n        key: K,\n        val: T,\n    ) -> &mut Self {\n        self.headers\n            .get_or_insert_with(|| HeaderMap::default())\n            .insert(key.into(), val.into());\n        self\n    }\n}\n\n/// A socket which handles communication with the server. It's initialized with\n/// a specific address as well as an optional namespace to connect to. If `None`\n/// is given the client will connect to the default namespace `\"/\"`.\n#[derive(Clone)]\npub struct Client {\n    /// The inner socket client to delegate the methods to.\n    socket: Arc<RwLock<InnerSocket>>,\n    outstanding_acks: Arc<RwLock<Vec<Ack>>>,\n    // namespace, for multiplexing messages\n    nsp: String,\n    // Data send in the opening packet (commonly used as for auth)\n    auth: Option<serde_json::Value>,\n    builder: Arc<RwLock<ClientBuilder>>,\n    disconnect_reason: Arc<RwLock<DisconnectReason>>,\n}\n\nimpl Client {\n    /// Creates a socket with a certain address to connect to as well as a\n    /// namespace. If `None` is passed in as namespace, the default namespace\n    /// `\"/\"` is taken.\n    /// ```\n    pub(crate) fn new(socket: InnerSocket, builder: ClientBuilder) -> Result<Self> {\n        Ok(Client {\n            socket: Arc::new(RwLock::new(socket)),\n            nsp: builder.namespace.to_owned(),\n            outstanding_acks: Arc::new(RwLock::new(Vec::new())),\n            auth: builder.auth.clone(),\n            builder: Arc::new(RwLock::new(builder)),\n            disconnect_reason: Arc::new(RwLock::new(DisconnectReason::default())),\n        })\n    }\n\n    /// Connects the client to a server. Afterwards the `emit_*` methods can be\n    /// called to interact with the server.\n    pub(crate) async fn connect(&self) -> Result<()> {\n        // Connect the underlying socket\n        self.socket.read().await.connect().await?;\n\n        // construct the opening packet\n        let auth = self.auth.as_ref().map(|data| data.to_string());\n        let open_packet = Packet::new(PacketId::Connect, self.nsp.clone(), auth, None, 0, None);\n\n        self.socket.read().await.send(open_packet).await?;\n\n        Ok(())\n    }\n\n    pub(crate) async fn reconnect(&mut self) -> Result<()> {\n        let mut builder = self.builder.write().await;\n\n        if let Some(config) = builder.on_reconnect.as_mut() {\n            let reconnect_settings = config().await;\n\n            if let Some(address) = reconnect_settings.address {\n                builder.address = address;\n            }\n\n            if let Some(auth) = reconnect_settings.auth {\n                self.auth = Some(auth);\n            }\n\n            if reconnect_settings.headers.is_some() {\n                builder.opening_headers = reconnect_settings.headers;\n            }\n        }\n\n        let socket = builder.inner_create().await?;\n\n        // New inner socket that can be connected\n        let mut client_socket = self.socket.write().await;\n        *client_socket = socket;\n\n        // Now that we have replaced `self.socket`, we drop the write lock\n        // because the `connect` method we call below will need to use it\n        drop(client_socket);\n\n        self.connect().await?;\n\n        Ok(())\n    }\n\n    /// Drives the stream using a thread so messages are processed\n    pub(crate) async fn poll_stream(&mut self) -> Result<()> {\n        let builder = self.builder.read().await;\n        let reconnect_delay_min = builder.reconnect_delay_min;\n        let reconnect_delay_max = builder.reconnect_delay_max;\n        let max_reconnect_attempts = builder.max_reconnect_attempts;\n        let reconnect = builder.reconnect;\n        let reconnect_on_disconnect = builder.reconnect_on_disconnect;\n        drop(builder);\n\n        let mut client_clone = self.clone();\n\n        tokio::runtime::Handle::current().spawn(async move {\n            loop {\n                let mut stream = client_clone.as_stream().await;\n                // Consume the stream until it returns None and the stream is closed.\n                while let Some(item) = stream.next().await {\n                    if let Err(e) = item {\n                        trace!(\"Network error occurred: {}\", e);\n                    }\n                }\n\n                // Drop the stream so we can once again use `socket_clone` as mutable\n                drop(stream);\n\n                let should_reconnect = match *(client_clone.disconnect_reason.read().await) {\n                    DisconnectReason::Unknown => {\n                        // If we disconnected for an unknown reason, the client might not have noticed\n                        // the closure yet. Hence, fire a transport close event to notify it.\n                        // We don't need to do that in the other cases, since proper server close\n                        // and manual client close are handled explicitly.\n                        if let Some(err) = client_clone\n                            .callback(&Event::Close, CloseReason::TransportClose.as_str())\n                            .await\n                            .err()\n                        {\n                            error!(\"Error while notifying client of transport close: {err}\")\n                        }\n\n                        reconnect\n                    }\n                    DisconnectReason::Manual => false,\n                    DisconnectReason::Server => reconnect_on_disconnect,\n                };\n\n                if should_reconnect {\n                    let mut reconnect_attempts = 0;\n                    let mut backoff = ExponentialBackoffBuilder::new()\n                        .with_initial_interval(Duration::from_millis(reconnect_delay_min))\n                        .with_max_interval(Duration::from_millis(reconnect_delay_max))\n                        .build();\n\n                    loop {\n                        if let Some(max_reconnect_attempts) = max_reconnect_attempts {\n                            reconnect_attempts += 1;\n                            if reconnect_attempts > max_reconnect_attempts {\n                                trace!(\"Max reconnect attempts reached without success\");\n                                break;\n                            }\n                        }\n                        match client_clone.reconnect().await {\n                            Ok(_) => {\n                                trace!(\"Reconnected after {reconnect_attempts} attempts\");\n                                break;\n                            }\n                            Err(e) => {\n                                trace!(\"Failed to reconnect: {e:?}\");\n                                if let Some(delay) = backoff.next_backoff() {\n                                    let delay_ms = delay.as_millis();\n                                    trace!(\"Waiting for {delay_ms}ms before reconnecting\");\n                                    sleep(delay).await;\n                                }\n                            }\n                        }\n                    }\n                } else {\n                    break;\n                }\n            }\n        });\n\n        Ok(())\n    }\n\n    /// Sends a message to the server using the underlying `engine.io` protocol.\n    /// This message takes an event, which could either be one of the common\n    /// events like \"message\" or \"error\" or a custom event like \"foo\". But be\n    /// careful, the data string needs to be valid JSON. It's recommended to use\n    /// a library like `serde_json` to serialize the data properly.\n    ///\n    /// # Example\n    /// ```\n    /// use rust_socketio::{asynchronous::{ClientBuilder, Client}, Payload};\n    /// use serde_json::json;\n    /// use futures_util::FutureExt;\n    ///\n    /// #[tokio::main]\n    /// async fn main() {\n    ///     let mut socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///         .on(\"test\", |payload: Payload, socket: Client| {\n    ///             async move {\n    ///                 println!(\"Received: {:#?}\", payload);\n    ///                 socket.emit(\"test\", json!({\"hello\": true})).await.expect(\"Server unreachable\");\n    ///             }.boxed()\n    ///         })\n    ///         .connect()\n    ///         .await\n    ///         .expect(\"connection failed\");\n    ///\n    ///     let json_payload = json!({\"token\": 123});\n    ///\n    ///     let result = socket.emit(\"foo\", json_payload).await;\n    ///\n    ///     assert!(result.is_ok());\n    /// }\n    /// ```\n    #[inline]\n    pub async fn emit<E, D>(&self, event: E, data: D) -> Result<()>\n    where\n        E: Into<Event>,\n        D: Into<Payload>,\n    {\n        self.socket\n            .read()\n            .await\n            .emit(&self.nsp, event.into(), data.into())\n            .await\n    }\n\n    /// Disconnects this client from the server by sending a `socket.io` closing\n    /// packet.\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{asynchronous::{ClientBuilder, Client}, Payload};\n    /// use serde_json::json;\n    /// use futures_util::{FutureExt, future::BoxFuture};\n    ///\n    /// #[tokio::main]\n    /// async fn main() {\n    ///     // apparently the syntax for functions is a bit verbose as rust currently doesn't\n    ///     // support an `AsyncFnMut` type that conform with async functions\n    ///     fn handle_test(payload: Payload, socket: Client) -> BoxFuture<'static, ()> {\n    ///         async move {\n    ///             println!(\"Received: {:#?}\", payload);\n    ///             socket.emit(\"test\", json!({\"hello\": true})).await.expect(\"Server unreachable\");\n    ///         }.boxed()\n    ///     }\n    ///\n    ///     let mut socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///         .on(\"test\", handle_test)\n    ///         .connect()\n    ///         .await\n    ///         .expect(\"connection failed\");\n    ///\n    ///     let json_payload = json!({\"token\": 123});\n    ///\n    ///     socket.emit(\"foo\", json_payload).await;\n    ///\n    ///     // disconnect from the server\n    ///     socket.disconnect().await;\n    /// }\n    /// ```\n    pub async fn disconnect(&self) -> Result<()> {\n        *(self.disconnect_reason.write().await) = DisconnectReason::Manual;\n\n        let disconnect_packet =\n            Packet::new(PacketId::Disconnect, self.nsp.clone(), None, None, 0, None);\n\n        self.socket.read().await.send(disconnect_packet).await?;\n        self.socket.read().await.disconnect().await?;\n\n        Ok(())\n    }\n\n    /// Sends a message to the server but `alloc`s an `ack` to check whether the\n    /// server responded in a given time span. This message takes an event, which\n    /// could either be one of the common events like \"message\" or \"error\" or a\n    /// custom event like \"foo\", as well as a data parameter. But be careful,\n    /// in case you send a [`Payload::String`], the string needs to be valid JSON.\n    /// It's even recommended to use a library like serde_json to serialize the data properly.\n    /// It also requires a timeout `Duration` in which the client needs to answer.\n    /// If the ack is acked in the correct time span, the specified callback is\n    /// called. The callback consumes a [`Payload`] which represents the data send\n    /// by the server.\n    ///\n    /// Please note that the requirements on the provided callbacks are similar to the ones\n    /// for [`crate::asynchronous::ClientBuilder::on`].\n    /// # Example\n    /// ```\n    /// use rust_socketio::{asynchronous::{ClientBuilder, Client}, Payload};\n    /// use serde_json::json;\n    /// use std::time::Duration;\n    /// use std::thread::sleep;\n    /// use futures_util::FutureExt;\n    ///\n    /// #[tokio::main]\n    /// async fn main() {\n    ///     let mut socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///         .on(\"foo\", |payload: Payload, _| async move { println!(\"Received: {:#?}\", payload) }.boxed())\n    ///         .connect()\n    ///         .await\n    ///         .expect(\"connection failed\");\n    ///\n    ///     let ack_callback = |message: Payload, socket: Client| {\n    ///         async move {\n    ///             match message {\n    ///                 Payload::Text(values) => println!(\"{:#?}\", values),\n    ///                 Payload::Binary(bytes) => println!(\"Received bytes: {:#?}\", bytes),\n    ///                 // This is deprecated use Payload::Text instead\n    ///                 Payload::String(str) => println!(\"{}\", str),\n    ///             }\n    ///         }.boxed()\n    ///     };\n    ///\n    ///\n    ///     let payload = json!({\"token\": 123});\n    ///     socket.emit_with_ack(\"foo\", payload, Duration::from_secs(2), ack_callback).await.unwrap();\n    ///\n    ///     sleep(Duration::from_secs(2));\n    /// }\n    /// ```\n    #[inline]\n    pub async fn emit_with_ack<F, E, D>(\n        &self,\n        event: E,\n        data: D,\n        timeout: Duration,\n        callback: F,\n    ) -> Result<()>\n    where\n        F: for<'a> std::ops::FnMut(Payload, Client) -> BoxFuture<'static, ()>\n            + 'static\n            + Send\n            + Sync,\n        E: Into<Event>,\n        D: Into<Payload>,\n    {\n        let id = thread_rng().gen_range(0..999);\n        let socket_packet =\n            Packet::new_from_payload(data.into(), event.into(), &self.nsp, Some(id))?;\n\n        let ack = Ack {\n            id,\n            time_started: Instant::now(),\n            timeout,\n            callback: Callback::<DynAsyncCallback>::new(callback),\n        };\n\n        // add the ack to the tuple of outstanding acks\n        self.outstanding_acks.write().await.push(ack);\n\n        self.socket.read().await.send(socket_packet).await\n    }\n\n    async fn callback<P: Into<Payload>>(&self, event: &Event, payload: P) -> Result<()> {\n        let mut builder = self.builder.write().await;\n        let payload = payload.into();\n\n        if let Some(callback) = builder.on.get_mut(event) {\n            callback(payload.clone(), self.clone()).await;\n        }\n\n        // Call on_any for all common and custom events.\n        match event {\n            Event::Message | Event::Custom(_) => {\n                if let Some(callback) = builder.on_any.as_mut() {\n                    callback(event.clone(), payload, self.clone()).await;\n                }\n            }\n            _ => (),\n        }\n\n        Ok(())\n    }\n\n    /// Handles the incoming acks and classifies what callbacks to call and how.\n    #[inline]\n    async fn handle_ack(&self, socket_packet: &Packet) -> Result<()> {\n        let mut to_be_removed = Vec::new();\n        if let Some(id) = socket_packet.id {\n            for (index, ack) in self.outstanding_acks.write().await.iter_mut().enumerate() {\n                if ack.id == id {\n                    to_be_removed.push(index);\n\n                    if ack.time_started.elapsed() < ack.timeout {\n                        if let Some(ref payload) = socket_packet.data {\n                            ack.callback.deref_mut()(\n                                Payload::from(payload.to_owned()),\n                                self.clone(),\n                            )\n                            .await;\n                        }\n                        if let Some(ref attachments) = socket_packet.attachments {\n                            if let Some(payload) = attachments.get(0) {\n                                ack.callback.deref_mut()(\n                                    Payload::Binary(payload.to_owned()),\n                                    self.clone(),\n                                )\n                                .await;\n                            }\n                        }\n                    } else {\n                        trace!(\"Received an Ack that is now timed out (elapsed time was longer than specified duration)\");\n                    }\n                }\n            }\n            for index in to_be_removed {\n                self.outstanding_acks.write().await.remove(index);\n            }\n        }\n        Ok(())\n    }\n\n    /// Handles a binary event.\n    #[inline]\n    async fn handle_binary_event(&self, packet: &Packet) -> Result<()> {\n        let event = if let Some(string_data) = &packet.data {\n            string_data.replace('\\\"', \"\").into()\n        } else {\n            Event::Message\n        };\n\n        if let Some(attachments) = &packet.attachments {\n            if let Some(binary_payload) = attachments.get(0) {\n                self.callback(&event, Payload::Binary(binary_payload.to_owned()))\n                    .await?;\n            }\n        }\n        Ok(())\n    }\n\n    /// A method that parses a packet and eventually calls the corresponding\n    /// callback with the supplied data.\n    async fn handle_event(&self, packet: &Packet) -> Result<()> {\n        let Some(ref data) = packet.data else {\n            return Ok(());\n        };\n\n        // a socketio message always comes in one of the following two flavors (both JSON):\n        // 1: `[\"event\", \"msg\", ...]`\n        // 2: `[\"msg\"]`\n        // in case 2, the message is ment for the default message event, in case 1 the event\n        // is specified\n        if let Ok(Value::Array(contents)) = serde_json::from_str::<Value>(data) {\n            let (event, payloads) = match contents.len() {\n                0 => return Err(Error::IncompletePacket()),\n                // Incorrect packet, ignore it\n                1 => (Event::Message, contents.as_slice()),\n                // it's a message event\n                _ => match contents.first() {\n                    Some(Value::String(ev)) => (Event::from(ev.as_str()), &contents[1..]),\n                    // get rest(1..) of them as data, not just take the 2nd element\n                    _ => (Event::Message, contents.as_slice()),\n                    // take them all as data\n                },\n            };\n\n            // call the correct callback\n            self.callback(&event, payloads.to_vec()).await?;\n        }\n\n        Ok(())\n    }\n\n    /// Handles the incoming messages and classifies what callbacks to call and how.\n    /// This method is later registered as the callback for the `on_data` event of the\n    /// engineio client.\n    #[inline]\n    async fn handle_socketio_packet(&self, packet: &Packet) -> Result<()> {\n        if packet.nsp == self.nsp {\n            match packet.packet_type {\n                PacketId::Ack | PacketId::BinaryAck => {\n                    if let Err(err) = self.handle_ack(packet).await {\n                        self.callback(&Event::Error, err.to_string()).await?;\n                        return Err(err);\n                    }\n                }\n                PacketId::BinaryEvent => {\n                    if let Err(err) = self.handle_binary_event(packet).await {\n                        self.callback(&Event::Error, err.to_string()).await?;\n                    }\n                }\n                PacketId::Connect => {\n                    *(self.disconnect_reason.write().await) = DisconnectReason::default();\n                    self.callback(&Event::Connect, \"\").await?;\n                }\n                PacketId::Disconnect => {\n                    *(self.disconnect_reason.write().await) = DisconnectReason::Server;\n                    self.callback(&Event::Close, CloseReason::IOServerDisconnect.as_str())\n                        .await?;\n                }\n                PacketId::ConnectError => {\n                    self.callback(\n                        &Event::Error,\n                        String::from(\"Received an ConnectError frame: \")\n                            + packet\n                                .data\n                                .as_ref()\n                                .unwrap_or(&String::from(\"\\\"No error message provided\\\"\")),\n                    )\n                    .await?;\n                }\n                PacketId::Event => {\n                    if let Err(err) = self.handle_event(packet).await {\n                        self.callback(&Event::Error, err.to_string()).await?;\n                    }\n                }\n            }\n        }\n        Ok(())\n    }\n\n    /// Returns the packet stream for the client.\n    pub(crate) async fn as_stream<'a>(\n        &'a self,\n    ) -> Pin<Box<dyn Stream<Item = Result<Packet>> + Send + 'a>> {\n        let socket_clone = (*self.socket.read().await).clone();\n\n        stream::unfold(socket_clone, |mut socket| async {\n            // wait for the next payload\n            let packet: Option<std::result::Result<Packet, Error>> = socket.next().await;\n            match packet {\n                // end the stream if the underlying one is closed\n                None => None,\n                Some(Err(err)) => {\n                    // call the error callback\n                    match self.callback(&Event::Error, err.to_string()).await {\n                        Err(callback_err) => Some((Err(callback_err), socket)),\n                        Ok(_) => Some((Err(err), socket)),\n                    }\n                }\n                Some(Ok(packet)) => match self.handle_socketio_packet(&packet).await {\n                    Err(callback_err) => Some((Err(callback_err), socket)),\n                    Ok(_) => Some((Ok(packet), socket)),\n                },\n            }\n        })\n        .boxed()\n    }\n}\n\n#[cfg(test)]\nmod test {\n\n    use std::{\n        sync::{\n            atomic::{AtomicUsize, Ordering},\n            Arc,\n        },\n        time::Duration,\n    };\n\n    use bytes::Bytes;\n    use futures_util::{FutureExt, StreamExt};\n    use native_tls::TlsConnector;\n    use serde_json::json;\n    use serial_test::serial;\n    use tokio::{\n        sync::{mpsc, Mutex},\n        time::{sleep, timeout},\n    };\n\n    use crate::{\n        asynchronous::{\n            client::{builder::ClientBuilder, client::Client},\n            ReconnectSettings,\n        },\n        error::Result,\n        packet::{Packet, PacketId},\n        CloseReason, Event, Payload, TransportType,\n    };\n\n    #[tokio::test]\n    async fn socket_io_integration() -> Result<()> {\n        let url = crate::test::socket_io_server();\n\n        let socket = ClientBuilder::new(url)\n            .on(\"test\", |msg, _| {\n                async {\n                    match msg {\n                        Payload::Text(values) => println!(\"Received json: {:#?}\", values),\n                        #[allow(deprecated)]\n                        Payload::String(str) => println!(\"Received string: {}\", str),\n                        Payload::Binary(bin) => println!(\"Received binary data: {:#?}\", bin),\n                    }\n                }\n                .boxed()\n            })\n            .connect()\n            .await?;\n\n        let payload = json!({\"token\": 123_i32});\n        let result = socket.emit(\"test\", Payload::from(payload.clone())).await;\n\n        assert!(result.is_ok());\n\n        let ack = socket\n            .emit_with_ack(\n                \"test\",\n                Payload::from(payload),\n                Duration::from_secs(1),\n                |message: Payload, socket: Client| {\n                    async move {\n                        let result = socket\n                            .emit(\"test\", Payload::from(json!({\"got ack\": true})))\n                            .await;\n                        assert!(result.is_ok());\n\n                        println!(\"Yehaa! My ack got acked?\");\n                        if let Payload::Text(json) = message {\n                            println!(\"Received json Ack\");\n                            println!(\"Ack data: {:#?}\", json);\n                        }\n                    }\n                    .boxed()\n                },\n            )\n            .await;\n        assert!(ack.is_ok());\n\n        sleep(Duration::from_secs(2)).await;\n\n        assert!(socket.disconnect().await.is_ok());\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn socket_io_async_callback() -> Result<()> {\n        // Test whether asynchronous callbacks are fully executed.\n        let url = crate::test::socket_io_server();\n\n        // This synchronization mechanism is used to let the test know that the end of the\n        // async callback was reached.\n        let notify = Arc::new(tokio::sync::Notify::new());\n        let notify_clone = notify.clone();\n\n        let socket = ClientBuilder::new(url)\n            .on(\"test\", move |_, _| {\n                let cl = notify_clone.clone();\n                async move {\n                    sleep(Duration::from_secs(1)).await;\n                    // The async callback should be awaited and not aborted.\n                    // Thus, the notification should be called.\n                    cl.notify_one();\n                }\n                .boxed()\n            })\n            .connect()\n            .await?;\n\n        let payload = json!({\"token\": 123_i32});\n        let result = socket.emit(\"test\", Payload::from(payload)).await;\n\n        assert!(result.is_ok());\n        // If the timeout did not trigger, the async callback was fully executed.\n        let timeout = timeout(Duration::from_secs(5), notify.notified()).await;\n        assert!(timeout.is_ok());\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn socket_io_builder_integration() -> Result<()> {\n        let url = crate::test::socket_io_server();\n\n        // test socket build logic\n        let socket_builder = ClientBuilder::new(url);\n\n        let tls_connector = TlsConnector::builder()\n            .use_sni(true)\n            .build()\n            .expect(\"Found illegal configuration\");\n\n        let socket = socket_builder\n            .namespace(\"/admin\")\n            .tls_config(tls_connector)\n            .opening_header(\"accept-encoding\", \"application/json\")\n            .on(\"test\", |str, _| {\n                async move { println!(\"Received: {:#?}\", str) }.boxed()\n            })\n            .on(\"message\", |payload, _| {\n                async move { println!(\"{:#?}\", payload) }.boxed()\n            })\n            .connect()\n            .await?;\n\n        assert!(socket.emit(\"message\", json!(\"Hello World\")).await.is_ok());\n\n        assert!(socket\n            .emit(\"binary\", Bytes::from_static(&[46, 88]))\n            .await\n            .is_ok());\n\n        assert!(socket\n            .emit_with_ack(\n                \"binary\",\n                json!(\"pls ack\"),\n                Duration::from_secs(1),\n                |payload, _| async move {\n                    println!(\"Yehaa the ack got acked\");\n                    println!(\"With data: {:#?}\", payload);\n                }\n                .boxed()\n            )\n            .await\n            .is_ok());\n\n        sleep(Duration::from_secs(2)).await;\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    #[serial(reconnect)]\n    async fn socket_io_reconnect_integration() -> Result<()> {\n        static CONNECT_NUM: AtomicUsize = AtomicUsize::new(0);\n        static MESSAGE_NUM: AtomicUsize = AtomicUsize::new(0);\n        static ON_RECONNECT_CALLED: AtomicUsize = AtomicUsize::new(0);\n        let latest_message = Arc::new(Mutex::new(String::new()));\n        let handler_latest_message = latest_message.clone();\n\n        let url = crate::test::socket_io_restart_server();\n\n        let socket = ClientBuilder::new(url.clone())\n            .reconnect(true)\n            .max_reconnect_attempts(100)\n            .reconnect_delay(100, 100)\n            .on_reconnect(move || {\n                let url = url.clone();\n                async move {\n                    ON_RECONNECT_CALLED.fetch_add(1, Ordering::Release);\n\n                    let mut settings = ReconnectSettings::new();\n\n                    // Try setting the address to what we already have, just\n                    // to test. This is not strictly necessary in real usage.\n                    settings.address(url.to_string());\n                    settings.opening_header(\"MESSAGE_BACK\", \"updated\");\n                    settings\n                }\n                .boxed()\n            })\n            .on(\"open\", |_, socket| {\n                async move {\n                    CONNECT_NUM.fetch_add(1, Ordering::Release);\n                    let r = socket.emit_with_ack(\n                        \"message\",\n                        json!(\"\"),\n                        Duration::from_millis(100),\n                        |_, _| async move {}.boxed(),\n                    );\n                    assert!(r.await.is_ok(), \"should emit message success\");\n                }\n                .boxed()\n            })\n            .on(\"message\", move |payload, _socket| {\n                let latest_message = handler_latest_message.clone();\n                async move {\n                    // test the iterator implementation and make sure there is a constant\n                    // stream of packets, even when reconnecting\n                    MESSAGE_NUM.fetch_add(1, Ordering::Release);\n\n                    let msg = match payload {\n                        Payload::Text(msg) => msg\n                            .into_iter()\n                            .next()\n                            .expect(\"there should be one text payload\"),\n                        _ => panic!(),\n                    };\n\n                    let msg = serde_json::from_value(msg).expect(\"payload should be json string\");\n\n                    *latest_message.lock().await = msg;\n                }\n                .boxed()\n            })\n            .connect()\n            .await;\n\n        assert!(socket.is_ok(), \"should connect success\");\n        let socket = socket.unwrap();\n\n        // waiting for server to emit message\n        sleep(Duration::from_millis(500)).await;\n\n        assert_eq!(load(&CONNECT_NUM), 1, \"should connect once\");\n        assert_eq!(load(&MESSAGE_NUM), 1, \"should receive one\");\n        assert_eq!(\n            *latest_message.lock().await,\n            \"test\",\n            \"should receive test message\"\n        );\n\n        let r = socket.emit(\"restart_server\", json!(\"\")).await;\n        assert!(r.is_ok(), \"should emit restart success\");\n\n        // waiting for server to restart\n        for _ in 0..10 {\n            sleep(Duration::from_millis(400)).await;\n            if load(&CONNECT_NUM) == 2 && load(&MESSAGE_NUM) == 2 {\n                break;\n            }\n        }\n\n        assert_eq!(load(&CONNECT_NUM), 2, \"should connect twice\");\n        assert_eq!(load(&MESSAGE_NUM), 2, \"should receive two messages\");\n        assert!(\n            load(&ON_RECONNECT_CALLED) > 1,\n            \"should call on_reconnect at least once\"\n        );\n        assert_eq!(\n            *latest_message.lock().await,\n            \"updated\",\n            \"should receive updated message\"\n        );\n\n        socket.disconnect().await?;\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn socket_io_builder_integration_iterator() -> Result<()> {\n        let url = crate::test::socket_io_server();\n\n        // test socket build logic\n        let socket_builder = ClientBuilder::new(url);\n\n        let tls_connector = TlsConnector::builder()\n            .use_sni(true)\n            .build()\n            .expect(\"Found illegal configuration\");\n\n        let socket = socket_builder\n            .namespace(\"/admin\")\n            .tls_config(tls_connector)\n            .opening_header(\"accept-encoding\", \"application/json\")\n            .on(\"test\", |str, _| {\n                async move { println!(\"Received: {:#?}\", str) }.boxed()\n            })\n            .on(\"message\", |payload, _| {\n                async move { println!(\"{:#?}\", payload) }.boxed()\n            })\n            .connect_manual()\n            .await?;\n\n        assert!(socket.emit(\"message\", json!(\"Hello World\")).await.is_ok());\n\n        assert!(socket\n            .emit(\"binary\", Bytes::from_static(&[46, 88]))\n            .await\n            .is_ok());\n\n        assert!(socket\n            .emit_with_ack(\n                \"binary\",\n                json!(\"pls ack\"),\n                Duration::from_secs(1),\n                |payload, _| async move {\n                    println!(\"Yehaa the ack got acked\");\n                    println!(\"With data: {:#?}\", payload);\n                }\n                .boxed()\n            )\n            .await\n            .is_ok());\n\n        test_socketio_socket(socket, \"/admin\".to_owned()).await\n    }\n\n    #[tokio::test]\n    async fn socket_io_on_any_integration() -> Result<()> {\n        let url = crate::test::socket_io_server();\n\n        let (tx, mut rx) = mpsc::channel(2);\n\n        let mut _socket = ClientBuilder::new(url)\n            .namespace(\"/\")\n            .auth(json!({ \"password\": \"123\" }))\n            .on_any(move |event, payload, _| {\n                let clone_tx = tx.clone();\n                async move {\n                    if let Payload::Text(values) = payload {\n                        println!(\"{event}: {values:#?}\");\n                    }\n                    clone_tx.send(String::from(event)).await.unwrap();\n                }\n                .boxed()\n            })\n            .connect()\n            .await?;\n\n        let event = rx.recv().await.unwrap();\n        assert_eq!(event, \"message\");\n\n        let event = rx.recv().await.unwrap();\n        assert_eq!(event, \"test\");\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn socket_io_auth_builder_integration() -> Result<()> {\n        let url = crate::test::socket_io_auth_server();\n        let nsp = String::from(\"/admin\");\n        let socket = ClientBuilder::new(url)\n            .namespace(nsp.clone())\n            .auth(json!({ \"password\": \"123\" }))\n            .connect_manual()\n            .await?;\n\n        // open packet\n        let mut socket_stream = socket.as_stream().await;\n        let _ = socket_stream.next().await.unwrap()?;\n\n        let packet = socket_stream.next().await.unwrap()?;\n        assert_eq!(\n            packet,\n            Packet::new(\n                PacketId::Event,\n                nsp,\n                Some(\"[\\\"auth\\\",\\\"success\\\"]\".to_owned()),\n                None,\n                0,\n                None\n            )\n        );\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn socket_io_transport_close() -> Result<()> {\n        let url = crate::test::socket_io_server();\n\n        let (tx, mut rx) = mpsc::channel(1);\n\n        let notify = Arc::new(tokio::sync::Notify::new());\n        let notify_clone = notify.clone();\n\n        let socket = ClientBuilder::new(url)\n            .on(Event::Connect, move |_, _| {\n                let cl = notify_clone.clone();\n                async move {\n                    cl.notify_one();\n                }\n                .boxed()\n            })\n            .on(Event::Close, move |payload, _| {\n                let clone_tx = tx.clone();\n                async move { clone_tx.send(payload).await.unwrap() }.boxed()\n            })\n            .connect()\n            .await?;\n\n        // Wait until socket is connected\n        let connect_timeout = timeout(Duration::from_secs(1), notify.notified()).await;\n        assert!(connect_timeout.is_ok());\n\n        // Instruct server to close transport\n        let result = socket.emit(\"close_transport\", Payload::from(\"\")).await;\n        assert!(result.is_ok());\n\n        // Wait for Event::Close\n        let rx_timeout = timeout(Duration::from_secs(1), rx.recv()).await;\n        assert!(rx_timeout.is_ok());\n\n        assert_eq!(\n            rx_timeout.unwrap(),\n            Some(Payload::from(CloseReason::TransportClose.as_str()))\n        );\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn socketio_polling_integration() -> Result<()> {\n        let url = crate::test::socket_io_server();\n        let socket = ClientBuilder::new(url.clone())\n            .transport_type(TransportType::Polling)\n            .connect_manual()\n            .await?;\n        test_socketio_socket(socket, \"/\".to_owned()).await\n    }\n\n    #[tokio::test]\n    async fn socket_io_websocket_integration() -> Result<()> {\n        let url = crate::test::socket_io_server();\n        let socket = ClientBuilder::new(url.clone())\n            .transport_type(TransportType::Websocket)\n            .connect_manual()\n            .await?;\n        test_socketio_socket(socket, \"/\".to_owned()).await\n    }\n\n    #[tokio::test]\n    async fn socket_io_websocket_upgrade_integration() -> Result<()> {\n        let url = crate::test::socket_io_server();\n        let socket = ClientBuilder::new(url)\n            .transport_type(TransportType::WebsocketUpgrade)\n            .connect_manual()\n            .await?;\n        test_socketio_socket(socket, \"/\".to_owned()).await\n    }\n\n    #[tokio::test]\n    async fn socket_io_any_integration() -> Result<()> {\n        let url = crate::test::socket_io_server();\n        let socket = ClientBuilder::new(url)\n            .transport_type(TransportType::Any)\n            .connect_manual()\n            .await?;\n        test_socketio_socket(socket, \"/\".to_owned()).await\n    }\n\n    async fn test_socketio_socket(socket: Client, nsp: String) -> Result<()> {\n        // open packet\n        let mut socket_stream = socket.as_stream().await;\n        let _: Option<Packet> = Some(socket_stream.next().await.unwrap()?);\n\n        let packet: Option<Packet> = Some(socket_stream.next().await.unwrap()?);\n\n        assert!(packet.is_some());\n\n        let packet = packet.unwrap();\n\n        assert_eq!(\n            packet,\n            Packet::new(\n                PacketId::Event,\n                nsp.clone(),\n                Some(\"[\\\"Hello from the message event!\\\"]\".to_owned()),\n                None,\n                0,\n                None,\n            )\n        );\n\n        let packet: Option<Packet> = Some(socket_stream.next().await.unwrap()?);\n\n        assert!(packet.is_some());\n\n        let packet = packet.unwrap();\n\n        assert_eq!(\n            packet,\n            Packet::new(\n                PacketId::Event,\n                nsp.clone(),\n                Some(\"[\\\"test\\\",\\\"Hello from the test event!\\\"]\".to_owned()),\n                None,\n                0,\n                None\n            )\n        );\n        let packet: Option<Packet> = Some(socket_stream.next().await.unwrap()?);\n\n        assert!(packet.is_some());\n\n        let packet = packet.unwrap();\n        assert_eq!(\n            packet,\n            Packet::new(\n                PacketId::BinaryEvent,\n                nsp.clone(),\n                None,\n                None,\n                1,\n                Some(vec![Bytes::from_static(&[4, 5, 6])]),\n            )\n        );\n\n        let packet: Option<Packet> = Some(socket_stream.next().await.unwrap()?);\n\n        assert!(packet.is_some());\n\n        let packet = packet.unwrap();\n        assert_eq!(\n            packet,\n            Packet::new(\n                PacketId::BinaryEvent,\n                nsp.clone(),\n                Some(\"\\\"test\\\"\".to_owned()),\n                None,\n                1,\n                Some(vec![Bytes::from_static(&[1, 2, 3])]),\n            )\n        );\n\n        let packet: Option<Packet> = Some(socket_stream.next().await.unwrap()?);\n\n        assert!(packet.is_some());\n\n        let packet = packet.unwrap();\n        assert_eq!(\n            packet,\n            Packet::new(\n                PacketId::Event,\n                nsp.clone(),\n                Some(\n                    serde_json::Value::Array(vec![\n                        serde_json::Value::from(\"This is the first argument\"),\n                        serde_json::Value::from(\"This is the second argument\"),\n                        serde_json::json!({\"argCount\":3})\n                    ])\n                    .to_string()\n                ),\n                None,\n                0,\n                None,\n            )\n        );\n\n        let packet: Option<Packet> = Some(socket_stream.next().await.unwrap()?);\n\n        assert!(packet.is_some());\n\n        let packet = packet.unwrap();\n        assert_eq!(\n            packet,\n            Packet::new(\n                PacketId::Event,\n                nsp.clone(),\n                Some(\n                    serde_json::json!([\n                        \"on_abc_event\",\n                        \"\",\n                        {\n                        \"abc\": 0,\n                        \"some_other\": \"value\",\n                        }\n                    ])\n                    .to_string()\n                ),\n                None,\n                0,\n                None,\n            )\n        );\n\n        let cb = |message: Payload, _| {\n            async {\n                println!(\"Yehaa! My ack got acked?\");\n                if let Payload::Text(values) = message {\n                    println!(\"Received json ack\");\n                    println!(\"Ack data: {:#?}\", values);\n                }\n            }\n            .boxed()\n        };\n\n        assert!(socket\n            .emit_with_ack(\n                \"test\",\n                Payload::from(\"123\".to_owned()),\n                Duration::from_secs(10),\n                cb\n            )\n            .await\n            .is_ok());\n\n        let packet: Option<Packet> = Some(socket_stream.next().await.unwrap()?);\n\n        assert!(packet.is_some());\n        let packet = packet.unwrap();\n        assert_eq!(\n            packet,\n            Packet::new(\n                PacketId::Event,\n                nsp.clone(),\n                Some(\"[\\\"test-received\\\",123]\".to_owned()),\n                None,\n                0,\n                None,\n            )\n        );\n\n        let packet: Option<Packet> = Some(socket_stream.next().await.unwrap()?);\n\n        assert!(packet.is_some());\n        let packet = packet.unwrap();\n        assert!(matches!(\n            packet,\n            Packet {\n                packet_type: PacketId::Ack,\n                nsp: _,\n                data: Some(_),\n                id: Some(_),\n                attachment_count: 0,\n                attachments: None,\n            }\n        ));\n\n        Ok(())\n    }\n\n    fn load(num: &AtomicUsize) -> usize {\n        num.load(Ordering::Acquire)\n    }\n}\n"
  },
  {
    "path": "socketio/src/asynchronous/client/mod.rs",
    "content": "mod ack;\npub(crate) mod builder;\n#[cfg(feature = \"async-callbacks\")]\nmod callback;\npub(crate) mod client;\n"
  },
  {
    "path": "socketio/src/asynchronous/generator.rs",
    "content": "use std::{pin::Pin, sync::Arc};\n\nuse crate::error::Result;\nuse futures_util::{ready, FutureExt, Stream, StreamExt};\nuse tokio::sync::Mutex;\n\n/// A handy type alias for a pinned + boxed Stream trait object that iterates\n/// over object of a certain type `T`.\npub(crate) type Generator<T> = Pin<Box<dyn Stream<Item = T> + 'static + Send>>;\n\n/// An internal type that implements stream by repeatedly calling [`Stream::poll_next`] on an\n/// underlying stream. Note that the generic parameter will be wrapped in a [`Result`].\n#[derive(Clone)]\npub(crate) struct StreamGenerator<T> {\n    inner: Arc<Mutex<Generator<Result<T>>>>,\n}\n\nimpl<T> Stream for StreamGenerator<T> {\n    type Item = Result<T>;\n\n    fn poll_next(\n        self: Pin<&mut Self>,\n        cx: &mut std::task::Context<'_>,\n    ) -> std::task::Poll<Option<Self::Item>> {\n        let mut lock = ready!(Box::pin(self.inner.lock()).poll_unpin(cx));\n        lock.poll_next_unpin(cx)\n    }\n}\n\nimpl<T> StreamGenerator<T> {\n    pub(crate) fn new(generator_stream: Generator<Result<T>>) -> Self {\n        StreamGenerator {\n            inner: Arc::new(Mutex::new(generator_stream)),\n        }\n    }\n}\n"
  },
  {
    "path": "socketio/src/asynchronous/mod.rs",
    "content": "mod client;\nmod generator;\nmod socket;\n\n#[cfg(feature = \"async\")]\npub use client::builder::ClientBuilder;\npub use client::client::{Client, ReconnectSettings};\n\n// re-export the macro\npub use crate::{async_any_callback, async_callback};\n\n#[doc = r#\"\nA macro to wrap an async callback function to be used in the client.\n\nThis macro is used to wrap a callback function that can handle a specific event.\n\n```rust\nuse rust_socketio::async_callback;\nuse rust_socketio::asynchronous::{Client, ClientBuilder};\nuse rust_socketio::{Event, Payload};\n\npub async fn callback(payload: Payload, client: Client) {}\n\n#[tokio::main]\nasync fn main() {\n    let socket = ClientBuilder::new(\"http://example.com\")\n            .on(\"message\", async_callback!(callback))\n            .connect()\n            .await;\n}\n```\n\"#]\n#[macro_export]\nmacro_rules! async_callback {\n    ($f:expr) => {{\n        use futures_util::FutureExt;\n        |payload: Payload, client: Client| $f(payload, client).boxed()\n    }};\n}\n\n#[doc = r#\"\nA macro to wrap an async callback function to be used in the client.\n\nThis macro is used to wrap a callback function that can handle any event.\n\n```rust\nuse rust_socketio::async_any_callback;\nuse rust_socketio::asynchronous::{Client, ClientBuilder};\nuse rust_socketio::{Event, Payload};\n\npub async fn callback_any(event: Event, payload: Payload, client: Client) {}\n\n#[tokio::main]\nasync fn main() {\n    let socket = ClientBuilder::new(\"http://example.com\")\n            .on_any(async_any_callback!(callback_any))\n            .connect()\n            .await;\n}\n```\n\"#]\n#[macro_export]\nmacro_rules! async_any_callback {\n    ($f:expr) => {{\n        use futures_util::FutureExt;\n        |event: Event, payload: Payload, client: Client| $f(event, payload, client).boxed()\n    }};\n}\n"
  },
  {
    "path": "socketio/src/asynchronous/socket.rs",
    "content": "use super::generator::StreamGenerator;\nuse crate::{\n    error::Result,\n    packet::{Packet, PacketId},\n    Error, Event, Payload,\n};\nuse async_stream::try_stream;\nuse bytes::Bytes;\nuse futures_util::{Stream, StreamExt};\nuse rust_engineio::{\n    asynchronous::Client as EngineClient, Packet as EnginePacket, PacketId as EnginePacketId,\n};\nuse std::{\n    fmt::Debug,\n    pin::Pin,\n    sync::{\n        atomic::{AtomicBool, Ordering},\n        Arc,\n    },\n};\n\n#[derive(Clone)]\npub(crate) struct Socket {\n    engine_client: Arc<EngineClient>,\n    connected: Arc<AtomicBool>,\n    generator: StreamGenerator<Packet>,\n}\n\nimpl Socket {\n    /// Creates an instance of `Socket`.\n    pub(super) fn new(engine_client: EngineClient) -> Result<Self> {\n        let connected = Arc::new(AtomicBool::default());\n        Ok(Socket {\n            engine_client: Arc::new(engine_client.clone()),\n            connected: connected.clone(),\n            generator: StreamGenerator::new(Self::stream(engine_client, connected)),\n        })\n    }\n\n    /// Connects to the server. This includes a connection of the underlying\n    /// engine.io client and afterwards an opening socket.io request.\n    pub async fn connect(&self) -> Result<()> {\n        self.engine_client.connect().await?;\n\n        // store the connected value as true, if the connection process fails\n        // later, the value will be updated\n        self.connected.store(true, Ordering::Release);\n\n        Ok(())\n    }\n\n    /// Disconnects from the server by sending a socket.io `Disconnect` packet. This results\n    /// in the underlying engine.io transport to get closed as well.\n    pub async fn disconnect(&self) -> Result<()> {\n        if self.is_engineio_connected() {\n            self.engine_client.disconnect().await?;\n        }\n        if self.connected.load(Ordering::Acquire) {\n            self.connected.store(false, Ordering::Release);\n        }\n        Ok(())\n    }\n\n    /// Sends a `socket.io` packet to the server using the `engine.io` client.\n    pub async fn send(&self, packet: Packet) -> Result<()> {\n        if !self.is_engineio_connected() || !self.connected.load(Ordering::Acquire) {\n            return Err(Error::IllegalActionBeforeOpen());\n        }\n\n        // the packet, encoded as an engine.io message packet\n        let engine_packet = EnginePacket::new(EnginePacketId::Message, Bytes::from(&packet));\n        self.engine_client.emit(engine_packet).await?;\n\n        if let Some(attachments) = packet.attachments {\n            for attachment in attachments {\n                let engine_packet = EnginePacket::new(EnginePacketId::MessageBinary, attachment);\n                self.engine_client.emit(engine_packet).await?;\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Emits to certain event with given data. The data needs to be JSON,\n    /// otherwise this returns an `InvalidJson` error.\n    pub async fn emit(&self, nsp: &str, event: Event, data: Payload) -> Result<()> {\n        let socket_packet = Packet::new_from_payload(data, event, nsp, None)?;\n\n        self.send(socket_packet).await\n    }\n\n    fn stream(\n        client: EngineClient,\n        is_connected: Arc<AtomicBool>,\n    ) -> Pin<Box<impl Stream<Item = Result<Packet>> + Send>> {\n        Box::pin(try_stream! {\n                for await received_data in client.clone() {\n                    let packet = received_data?;\n\n                    if packet.packet_id == EnginePacketId::Message\n                        || packet.packet_id == EnginePacketId::MessageBinary\n                    {\n                        let packet = Self::handle_engineio_packet(packet, client.clone()).await?;\n                        Self::handle_socketio_packet(&packet, is_connected.clone());\n\n                        yield packet;\n                    }\n                }\n        })\n    }\n\n    /// Handles the connection/disconnection.\n    #[inline]\n    fn handle_socketio_packet(socket_packet: &Packet, is_connected: Arc<AtomicBool>) {\n        match socket_packet.packet_type {\n            PacketId::Connect => {\n                is_connected.store(true, Ordering::Release);\n            }\n            PacketId::ConnectError => {\n                is_connected.store(false, Ordering::Release);\n            }\n            PacketId::Disconnect => {\n                is_connected.store(false, Ordering::Release);\n            }\n            _ => (),\n        }\n    }\n\n    /// Handles new incoming engineio packets\n    async fn handle_engineio_packet(\n        packet: EnginePacket,\n        mut client: EngineClient,\n    ) -> Result<Packet> {\n        let mut socket_packet = Packet::try_from(&packet.data)?;\n\n        // Only handle attachments if there are any\n        if socket_packet.attachment_count > 0 {\n            let mut attachments_left = socket_packet.attachment_count;\n            let mut attachments = Vec::new();\n            while attachments_left > 0 {\n                // TODO: This is not nice! Find a different way to peek the next element while mapping the stream\n                let next = client.next().await.unwrap();\n                match next {\n                    Err(err) => return Err(err.into()),\n                    Ok(packet) => match packet.packet_id {\n                        EnginePacketId::MessageBinary | EnginePacketId::Message => {\n                            attachments.push(packet.data);\n                            attachments_left -= 1;\n                        }\n                        _ => {\n                            return Err(Error::InvalidAttachmentPacketType(\n                                packet.packet_id.into(),\n                            ));\n                        }\n                    },\n                }\n            }\n            socket_packet.attachments = Some(attachments);\n        }\n\n        Ok(socket_packet)\n    }\n\n    fn is_engineio_connected(&self) -> bool {\n        self.engine_client.is_connected()\n    }\n}\n\nimpl Stream for Socket {\n    type Item = Result<Packet>;\n\n    fn poll_next(\n        mut self: Pin<&mut Self>,\n        cx: &mut std::task::Context<'_>,\n    ) -> std::task::Poll<Option<Self::Item>> {\n        self.generator.poll_next_unpin(cx)\n    }\n}\n\nimpl Debug for Socket {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Socket\")\n            .field(\"engine_client\", &self.engine_client)\n            .field(\"connected\", &self.connected)\n            .finish()\n    }\n}\n"
  },
  {
    "path": "socketio/src/client/builder.rs",
    "content": "use super::super::{event::Event, payload::Payload};\nuse super::callback::Callback;\nuse super::client::Client;\nuse crate::RawClient;\nuse native_tls::TlsConnector;\nuse rust_engineio::client::ClientBuilder as EngineIoClientBuilder;\nuse rust_engineio::header::{HeaderMap, HeaderValue};\nuse url::Url;\n\nuse crate::client::callback::{SocketAnyCallback, SocketCallback};\nuse crate::error::Result;\nuse std::collections::HashMap;\nuse std::sync::{Arc, Mutex};\n\nuse crate::socket::Socket as InnerSocket;\n\n/// Flavor of Engine.IO transport.\n#[derive(Clone, Eq, PartialEq)]\npub enum TransportType {\n    /// Handshakes with polling, upgrades if possible\n    Any,\n    /// Handshakes with websocket. Does not use polling.\n    Websocket,\n    /// Handshakes with polling, errors if upgrade fails\n    WebsocketUpgrade,\n    /// Handshakes with polling\n    Polling,\n}\n\n/// A builder class for a `socket.io` socket. This handles setting up the client and\n/// configuring the callback, the namespace and metadata of the socket. If no\n/// namespace is specified, the default namespace `/` is taken. The `connect` method\n/// acts the `build` method and returns a connected [`Client`].\n#[derive(Clone)]\npub struct ClientBuilder {\n    pub(crate) address: String,\n    on: Arc<Mutex<HashMap<Event, Callback<SocketCallback>>>>,\n    on_any: Arc<Mutex<Option<Callback<SocketAnyCallback>>>>,\n    namespace: String,\n    tls_config: Option<TlsConnector>,\n    opening_headers: Option<HeaderMap>,\n    transport_type: TransportType,\n    auth: Option<serde_json::Value>,\n    pub(crate) reconnect: bool,\n    pub(crate) reconnect_on_disconnect: bool,\n    // None reconnect attempts represent infinity.\n    pub(crate) max_reconnect_attempts: Option<u8>,\n    pub(crate) reconnect_delay_min: u64,\n    pub(crate) reconnect_delay_max: u64,\n}\n\nimpl ClientBuilder {\n    /// Create as client builder from a URL. URLs must be in the form\n    /// `[ws or wss or http or https]://[domain]:[port]/[path]`. The\n    /// path of the URL is optional and if no port is given, port 80\n    /// will be used.\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{ClientBuilder, Payload, RawClient};\n    /// use serde_json::json;\n    ///\n    ///\n    /// let callback = |payload: Payload, socket: RawClient| {\n    ///            match payload {\n    ///                Payload::Text(values) => println!(\"Received: {:#?}\", values),\n    ///                Payload::Binary(bin_data) => println!(\"Received bytes: {:#?}\", bin_data),\n    ///                // This payload type is deprecated, use Payload::Text instead\n    ///                Payload::String(str) => println!(\"Received: {}\", str),\n    ///            }\n    /// };\n    ///\n    /// let mut socket = ClientBuilder::new(\"http://localhost:4200\")\n    ///     .namespace(\"/admin\")\n    ///     .on(\"test\", callback)\n    ///     .connect()\n    ///     .expect(\"error while connecting\");\n    ///\n    /// // use the socket\n    /// let json_payload = json!({\"token\": 123});\n    ///\n    /// let result = socket.emit(\"foo\", json_payload);\n    ///\n    /// assert!(result.is_ok());\n    /// ```\n    pub fn new<T: Into<String>>(address: T) -> Self {\n        Self {\n            address: address.into(),\n            on: Arc::new(Mutex::new(HashMap::new())),\n            on_any: Arc::new(Mutex::new(None)),\n            namespace: \"/\".to_owned(),\n            tls_config: None,\n            opening_headers: None,\n            transport_type: TransportType::Any,\n            auth: None,\n            reconnect: true,\n            reconnect_on_disconnect: false,\n            // None means infinity\n            max_reconnect_attempts: None,\n            reconnect_delay_min: 1000,\n            reconnect_delay_max: 5000,\n        }\n    }\n\n    /// Sets the target namespace of the client. The namespace should start\n    /// with a leading `/`. Valid examples are e.g. `/admin`, `/foo`.\n    pub fn namespace<T: Into<String>>(mut self, namespace: T) -> Self {\n        let mut nsp = namespace.into();\n        if !nsp.starts_with('/') {\n            nsp = \"/\".to_owned() + &nsp;\n        }\n        self.namespace = nsp;\n        self\n    }\n\n    pub fn reconnect(mut self, reconnect: bool) -> Self {\n        self.reconnect = reconnect;\n        self\n    }\n\n    /// If set to `true` automatically set try to reconnect when the server\n    /// disconnects the client.\n    /// Defaults to `false`.\n    ///\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::ClientBuilder;\n    ///\n    /// let socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///     .reconnect_on_disconnect(true)\n    ///     .connect();\n    /// ```\n    pub fn reconnect_on_disconnect(mut self, reconnect_on_disconnect: bool) -> Self {\n        self.reconnect_on_disconnect = reconnect_on_disconnect;\n        self\n    }\n\n    pub fn reconnect_delay(mut self, min: u64, max: u64) -> Self {\n        self.reconnect_delay_min = min;\n        self.reconnect_delay_max = max;\n\n        self\n    }\n\n    pub fn max_reconnect_attempts(mut self, reconnect_attempts: u8) -> Self {\n        self.max_reconnect_attempts = Some(reconnect_attempts);\n        self\n    }\n\n    /// Registers a new callback for a certain [`crate::event::Event`]. The event could either be\n    /// one of the common events like `message`, `error`, `open`, `close` or a custom\n    /// event defined by a string, e.g. `onPayment` or `foo`.\n    ///\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{ClientBuilder, Payload};\n    ///\n    /// let socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///     .namespace(\"/admin\")\n    ///     .on(\"test\", |payload: Payload, _| {\n    ///            match payload {\n    ///                Payload::Text(values) => println!(\"Received: {:#?}\", values),\n    ///                Payload::Binary(bin_data) => println!(\"Received bytes: {:#?}\", bin_data),\n    ///                // This payload type is deprecated, use Payload::Text instead\n    ///                Payload::String(str) => println!(\"Received: {}\", str),\n    ///            }\n    ///     })\n    ///     .on(\"error\", |err, _| eprintln!(\"Error: {:#?}\", err))\n    ///     .connect();\n    ///\n    /// ```\n    // While present implementation doesn't require mut, it's reasonable to require mutability.\n    #[allow(unused_mut)]\n    pub fn on<T: Into<Event>, F>(mut self, event: T, callback: F) -> Self\n    where\n        F: FnMut(Payload, RawClient) + 'static + Send,\n    {\n        let callback = Callback::<SocketCallback>::new(callback);\n        // SAFETY: Lock is held for such amount of time no code paths lead to a panic while lock is held\n        self.on.lock().unwrap().insert(event.into(), callback);\n        self\n    }\n\n    /// Registers a Callback for all [`crate::event::Event::Custom`] and [`crate::event::Event::Message`].\n    ///\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{ClientBuilder, Payload};\n    ///\n    /// let client = ClientBuilder::new(\"http://localhost:4200/\")\n    ///     .namespace(\"/admin\")\n    ///     .on_any(|event, payload, _client| {\n    ///         if let Payload::String(str) = payload {\n    ///           println!(\"{} {}\", String::from(event), str);\n    ///         }\n    ///     })\n    ///     .connect();\n    ///\n    /// ```\n    // While present implementation doesn't require mut, it's reasonable to require mutability.\n    #[allow(unused_mut)]\n    pub fn on_any<F>(mut self, callback: F) -> Self\n    where\n        F: FnMut(Event, Payload, RawClient) + 'static + Send,\n    {\n        let callback = Some(Callback::<SocketAnyCallback>::new(callback));\n        // SAFETY: Lock is held for such amount of time no code paths lead to a panic while lock is held\n        *self.on_any.lock().unwrap() = callback;\n        self\n    }\n\n    /// Uses a preconfigured TLS connector for secure communication. This configures\n    /// both the `polling` as well as the `websocket` transport type.\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{ClientBuilder, Payload};\n    /// use native_tls::TlsConnector;\n    ///\n    /// let tls_connector =  TlsConnector::builder()\n    ///            .use_sni(true)\n    ///            .build()\n    ///            .expect(\"Found illegal configuration\");\n    ///\n    /// let socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///     .namespace(\"/admin\")\n    ///     .on(\"error\", |err, _| eprintln!(\"Error: {:#?}\", err))\n    ///     .tls_config(tls_connector)\n    ///     .connect();\n    ///\n    /// ```\n    pub fn tls_config(mut self, tls_config: TlsConnector) -> Self {\n        self.tls_config = Some(tls_config);\n        self\n    }\n\n    /// Sets custom http headers for the opening request. The headers will be passed to the underlying\n    /// transport type (either websockets or polling) and then get passed with every request thats made.\n    /// via the transport layer.\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{ClientBuilder, Payload};\n    ///\n    ///\n    /// let socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///     .namespace(\"/admin\")\n    ///     .on(\"error\", |err, _| eprintln!(\"Error: {:#?}\", err))\n    ///     .opening_header(\"accept-encoding\", \"application/json\")\n    ///     .connect();\n    ///\n    /// ```\n    pub fn opening_header<T: Into<HeaderValue>, K: Into<String>>(mut self, key: K, val: T) -> Self {\n        match self.opening_headers {\n            Some(ref mut map) => {\n                map.insert(key.into(), val.into());\n            }\n            None => {\n                let mut map = HeaderMap::default();\n                map.insert(key.into(), val.into());\n                self.opening_headers = Some(map);\n            }\n        }\n        self\n    }\n\n    /// Sets data sent in the opening request.\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{ClientBuilder};\n    /// use serde_json::json;\n    ///\n    /// let socket = ClientBuilder::new(\"http://localhost:4204/\")\n    ///     .namespace(\"/admin\")\n    ///     .auth(json!({ \"password\": \"1337\" }))\n    ///     .on(\"error\", |err, _| eprintln!(\"Error: {:#?}\", err))\n    ///     .connect()\n    ///     .expect(\"Connection error\");\n    ///\n    /// ```\n    pub fn auth(mut self, auth: serde_json::Value) -> Self {\n        self.auth = Some(auth);\n\n        self\n    }\n\n    /// Specifies which EngineIO [`TransportType`] to use.\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{ClientBuilder, TransportType};\n    /// use serde_json::json;\n    ///\n    /// let socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///     // Use websockets to handshake and connect.\n    ///     .transport_type(TransportType::Websocket)\n    ///     .connect()\n    ///     .expect(\"connection failed\");\n    ///\n    /// // use the socket\n    /// let json_payload = json!({\"token\": 123});\n    ///\n    /// let result = socket.emit(\"foo\", json_payload);\n    ///\n    /// assert!(result.is_ok());\n    /// ```\n    pub fn transport_type(mut self, transport_type: TransportType) -> Self {\n        self.transport_type = transport_type;\n\n        self\n    }\n\n    /// Connects the socket to a certain endpoint. This returns a connected\n    /// [`Client`] instance. This method returns an [`std::result::Result::Err`]\n    /// value if something goes wrong during connection. Also starts a separate\n    /// thread to start polling for packets. Used with callbacks.\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{ClientBuilder, Payload};\n    /// use serde_json::json;\n    ///\n    ///\n    /// let mut socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///     .namespace(\"/admin\")\n    ///     .on(\"error\", |err, _| eprintln!(\"Client error!: {:#?}\", err))\n    ///     .connect()\n    ///     .expect(\"connection failed\");\n    ///\n    /// // use the socket\n    /// let json_payload = json!({\"token\": 123});\n    ///\n    /// let result = socket.emit(\"foo\", json_payload);\n    ///\n    /// assert!(result.is_ok());\n    /// ```\n    pub fn connect(self) -> Result<Client> {\n        Client::new(self)\n    }\n\n    pub fn connect_raw(self) -> Result<RawClient> {\n        // Parse url here rather than in new to keep new returning Self.\n        let mut url = Url::parse(&self.address)?;\n\n        if url.path() == \"/\" {\n            url.set_path(\"/socket.io/\");\n        }\n\n        let mut builder = EngineIoClientBuilder::new(url);\n\n        if let Some(tls_config) = self.tls_config {\n            builder = builder.tls_config(tls_config);\n        }\n        if let Some(headers) = self.opening_headers {\n            builder = builder.headers(headers);\n        }\n\n        let engine_client = match self.transport_type {\n            TransportType::Any => builder.build_with_fallback()?,\n            TransportType::Polling => builder.build_polling()?,\n            TransportType::Websocket => builder.build_websocket()?,\n            TransportType::WebsocketUpgrade => builder.build_websocket_with_upgrade()?,\n        };\n\n        let inner_socket = InnerSocket::new(engine_client)?;\n\n        let socket = RawClient::new(\n            inner_socket,\n            &self.namespace,\n            self.on,\n            self.on_any,\n            self.auth,\n        )?;\n        socket.connect()?;\n\n        Ok(socket)\n    }\n}\n"
  },
  {
    "path": "socketio/src/client/callback.rs",
    "content": "use std::{\n    fmt::Debug,\n    ops::{Deref, DerefMut},\n};\n\nuse super::RawClient;\nuse crate::{Event, Payload};\n\npub(crate) type SocketCallback = Box<dyn FnMut(Payload, RawClient) + 'static + Send>;\npub(crate) type SocketAnyCallback = Box<dyn FnMut(Event, Payload, RawClient) + 'static + Send>;\n\npub(crate) struct Callback<T> {\n    inner: T,\n}\n\n// SocketCallback implementations\n\nimpl Debug for Callback<SocketCallback> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(\"Callback\")\n    }\n}\n\nimpl Deref for Callback<SocketCallback> {\n    type Target = dyn FnMut(Payload, RawClient) + 'static + Send;\n\n    fn deref(&self) -> &Self::Target {\n        self.inner.as_ref()\n    }\n}\n\nimpl DerefMut for Callback<SocketCallback> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.inner.as_mut()\n    }\n}\n\nimpl Callback<SocketCallback> {\n    pub(crate) fn new<T>(callback: T) -> Self\n    where\n        T: FnMut(Payload, RawClient) + 'static + Send,\n    {\n        Callback {\n            inner: Box::new(callback),\n        }\n    }\n}\n\n// SocketAnyCallback implementations\n\nimpl Debug for Callback<SocketAnyCallback> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(\"Callback\")\n    }\n}\n\nimpl Deref for Callback<SocketAnyCallback> {\n    type Target = dyn FnMut(Event, Payload, RawClient) + 'static + Send;\n\n    fn deref(&self) -> &Self::Target {\n        self.inner.as_ref()\n    }\n}\n\nimpl DerefMut for Callback<SocketAnyCallback> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.inner.as_mut()\n    }\n}\n\nimpl Callback<SocketAnyCallback> {\n    pub(crate) fn new<T>(callback: T) -> Self\n    where\n        T: FnMut(Event, Payload, RawClient) + 'static + Send,\n    {\n        Callback {\n            inner: Box::new(callback),\n        }\n    }\n}\n"
  },
  {
    "path": "socketio/src/client/client.rs",
    "content": "use std::{\n    sync::{Arc, Mutex, RwLock},\n    time::Duration,\n};\n\nuse super::{ClientBuilder, RawClient};\nuse crate::{\n    error::Result,\n    packet::{Packet, PacketId},\n    Error,\n};\npub(crate) use crate::{event::Event, payload::Payload};\nuse backoff::ExponentialBackoff;\nuse backoff::{backoff::Backoff, ExponentialBackoffBuilder};\n\n#[derive(Clone)]\npub struct Client {\n    builder: Arc<Mutex<ClientBuilder>>,\n    client: Arc<RwLock<RawClient>>,\n    backoff: ExponentialBackoff,\n}\n\nimpl Client {\n    pub(crate) fn new(builder: ClientBuilder) -> Result<Self> {\n        let builder_clone = builder.clone();\n        let client = builder_clone.connect_raw()?;\n        let backoff = ExponentialBackoffBuilder::new()\n            .with_initial_interval(Duration::from_millis(builder.reconnect_delay_min))\n            .with_max_interval(Duration::from_millis(builder.reconnect_delay_max))\n            .build();\n\n        let s = Self {\n            builder: Arc::new(Mutex::new(builder)),\n            client: Arc::new(RwLock::new(client)),\n            backoff,\n        };\n        s.poll_callback();\n\n        Ok(s)\n    }\n\n    /// Updates the URL the client will connect to when reconnecting.\n    /// This is especially useful for updating query parameters.\n    pub fn set_reconnect_url<T: Into<String>>(&self, address: T) -> Result<()> {\n        self.builder.lock()?.address = address.into();\n        Ok(())\n    }\n\n    /// Sends a message to the server using the underlying `engine.io` protocol.\n    /// This message takes an event, which could either be one of the common\n    /// events like \"message\" or \"error\" or a custom event like \"foo\". But be\n    /// careful, the data string needs to be valid JSON. It's recommended to use\n    /// a library like `serde_json` to serialize the data properly.\n    ///\n    /// # Example\n    /// ```\n    /// use rust_socketio::{ClientBuilder, RawClient, Payload};\n    /// use serde_json::json;\n    ///\n    /// let mut socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///     .on(\"test\", |payload: Payload, socket: RawClient| {\n    ///         println!(\"Received: {:#?}\", payload);\n    ///         socket.emit(\"test\", json!({\"hello\": true})).expect(\"Server unreachable\");\n    ///      })\n    ///     .connect()\n    ///     .expect(\"connection failed\");\n    ///\n    /// let json_payload = json!({\"token\": 123});\n    ///\n    /// let result = socket.emit(\"foo\", json_payload);\n    ///\n    /// assert!(result.is_ok());\n    /// ```\n    pub fn emit<E, D>(&self, event: E, data: D) -> Result<()>\n    where\n        E: Into<Event>,\n        D: Into<Payload>,\n    {\n        let client = self.client.read()?;\n        // TODO(#230): like js client, buffer emit, resend after reconnect\n        client.emit(event, data)\n    }\n\n    /// Sends a message to the server but `alloc`s an `ack` to check whether the\n    /// server responded in a given time span. This message takes an event, which\n    /// could either be one of the common events like \"message\" or \"error\" or a\n    /// custom event like \"foo\", as well as a data parameter. But be careful,\n    /// in case you send a [`Payload::String`], the string needs to be valid JSON.\n    /// It's even recommended to use a library like serde_json to serialize the data properly.\n    /// It also requires a timeout `Duration` in which the client needs to answer.\n    /// If the ack is acked in the correct time span, the specified callback is\n    /// called. The callback consumes a [`Payload`] which represents the data send\n    /// by the server.\n    ///\n    /// # Example\n    /// ```\n    /// use rust_socketio::{ClientBuilder, Payload, RawClient};\n    /// use serde_json::json;\n    /// use std::time::Duration;\n    /// use std::thread::sleep;\n    ///\n    /// let mut socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///     .on(\"foo\", |payload: Payload, _| println!(\"Received: {:#?}\", payload))\n    ///     .connect()\n    ///     .expect(\"connection failed\");\n    ///\n    /// let ack_callback = |message: Payload, socket: RawClient| {\n    ///     match message {\n    ///         Payload::Text(values) => println!(\"{:#?}\", values),\n    ///         Payload::Binary(bytes) => println!(\"Received bytes: {:#?}\", bytes),\n    ///         // This is deprecated, use Payload::Text instead.\n    ///         Payload::String(str) => println!(\"{}\", str),\n    ///    }\n    /// };\n    ///\n    /// let payload = json!({\"token\": 123});\n    /// socket.emit_with_ack(\"foo\", payload, Duration::from_secs(2), ack_callback).unwrap();\n    ///\n    /// sleep(Duration::from_secs(2));\n    /// ```\n    pub fn emit_with_ack<F, E, D>(\n        &self,\n        event: E,\n        data: D,\n        timeout: Duration,\n        callback: F,\n    ) -> Result<()>\n    where\n        F: FnMut(Payload, RawClient) + 'static + Send,\n        E: Into<Event>,\n        D: Into<Payload>,\n    {\n        let client = self.client.read()?;\n        // TODO(#230): like js client, buffer emit, resend after reconnect\n        client.emit_with_ack(event, data, timeout, callback)\n    }\n\n    /// Disconnects this client from the server by sending a `socket.io` closing\n    /// packet.\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{ClientBuilder, Payload, RawClient};\n    /// use serde_json::json;\n    ///\n    /// fn handle_test(payload: Payload, socket: RawClient) {\n    ///     println!(\"Received: {:#?}\", payload);\n    ///     socket.emit(\"test\", json!({\"hello\": true})).expect(\"Server unreachable\");\n    /// }\n    ///\n    /// let mut socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///     .on(\"test\", handle_test)\n    ///     .connect()\n    ///     .expect(\"connection failed\");\n    ///\n    /// let json_payload = json!({\"token\": 123});\n    ///\n    /// socket.emit(\"foo\", json_payload);\n    ///\n    /// // disconnect from the server\n    /// socket.disconnect();\n    ///\n    /// ```\n    pub fn disconnect(&self) -> Result<()> {\n        let client = self.client.read()?;\n        client.disconnect()\n    }\n\n    fn reconnect(&mut self) -> Result<()> {\n        let mut reconnect_attempts = 0;\n        let (reconnect, max_reconnect_attempts) = {\n            let builder = self.builder.lock()?;\n            (builder.reconnect, builder.max_reconnect_attempts)\n        };\n\n        if reconnect {\n            loop {\n                if let Some(max_reconnect_attempts) = max_reconnect_attempts {\n                    reconnect_attempts += 1;\n                    if reconnect_attempts > max_reconnect_attempts {\n                        break;\n                    }\n                }\n\n                if let Some(backoff) = self.backoff.next_backoff() {\n                    std::thread::sleep(backoff);\n                }\n\n                if self.do_reconnect().is_ok() {\n                    break;\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    fn do_reconnect(&self) -> Result<()> {\n        let builder = self.builder.lock()?;\n        let new_client = builder.clone().connect_raw()?;\n        let mut client = self.client.write()?;\n        *client = new_client;\n\n        Ok(())\n    }\n\n    pub(crate) fn iter(&self) -> Iter {\n        Iter {\n            socket: self.client.clone(),\n        }\n    }\n\n    fn poll_callback(&self) {\n        let mut self_clone = self.clone();\n        // Use thread to consume items in iterator in order to call callbacks\n        std::thread::spawn(move || {\n            // tries to restart a poll cycle whenever a 'normal' error occurs,\n            // it just panics on network errors, in case the poll cycle returned\n            // `Result::Ok`, the server receives a close frame so it's safe to\n            // terminate\n            for packet in self_clone.iter() {\n                let should_reconnect = match packet {\n                    Err(Error::IncompleteResponseFromEngineIo(_)) => {\n                        //TODO: 0.3.X handle errors\n                        //TODO: logging error\n                        true\n                    }\n                    Ok(Packet {\n                        packet_type: PacketId::Disconnect,\n                        ..\n                    }) => match self_clone.builder.lock() {\n                        Ok(builder) => builder.reconnect_on_disconnect,\n                        Err(_) => false,\n                    },\n                    _ => false,\n                };\n                if should_reconnect {\n                    let _ = self_clone.disconnect();\n                    let _ = self_clone.reconnect();\n                }\n            }\n        });\n    }\n}\n\npub(crate) struct Iter {\n    socket: Arc<RwLock<RawClient>>,\n}\n\nimpl Iterator for Iter {\n    type Item = Result<Packet>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        let socket = self.socket.read();\n        match socket {\n            Ok(socket) => match socket.poll() {\n                Err(err) => Some(Err(err)),\n                Ok(Some(packet)) => Some(Ok(packet)),\n                // If the underlying engineIO connection is closed,\n                // throw an error so we know to reconnect\n                Ok(None) => Some(Err(Error::StoppedEngineIoSocket)),\n            },\n            Err(_) => {\n                // Lock is poisoned, our iterator is useless.\n                None\n            }\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use std::{\n        sync::atomic::{AtomicUsize, Ordering},\n        time::UNIX_EPOCH,\n    };\n\n    use super::*;\n    use crate::error::Result;\n    use crate::ClientBuilder;\n    use serde_json::json;\n    use serial_test::serial;\n    use std::time::{Duration, SystemTime};\n    use url::Url;\n\n    #[test]\n    #[serial(reconnect)]\n    fn socket_io_reconnect_integration() -> Result<()> {\n        static CONNECT_NUM: AtomicUsize = AtomicUsize::new(0);\n        static CLOSE_NUM: AtomicUsize = AtomicUsize::new(0);\n        static MESSAGE_NUM: AtomicUsize = AtomicUsize::new(0);\n\n        let url = crate::test::socket_io_restart_server();\n\n        let socket = ClientBuilder::new(url)\n            .reconnect(true)\n            .max_reconnect_attempts(100)\n            .reconnect_delay(100, 100)\n            .on(Event::Connect, move |_, socket| {\n                CONNECT_NUM.fetch_add(1, Ordering::Release);\n                let r = socket.emit_with_ack(\n                    \"message\",\n                    json!(\"\"),\n                    Duration::from_millis(100),\n                    |_, _| {},\n                );\n                assert!(r.is_ok(), \"should emit message success\");\n            })\n            .on(Event::Close, move |_, _| {\n                CLOSE_NUM.fetch_add(1, Ordering::Release);\n            })\n            .on(\"message\", move |_, _socket| {\n                // test the iterator implementation and make sure there is a constant\n                // stream of packets, even when reconnecting\n                MESSAGE_NUM.fetch_add(1, Ordering::Release);\n            })\n            .connect();\n\n        assert!(socket.is_ok(), \"should connect success\");\n        let socket = socket.unwrap();\n\n        // waiting for server to emit message\n        std::thread::sleep(std::time::Duration::from_millis(500));\n\n        assert_eq!(load(&CONNECT_NUM), 1, \"should connect once\");\n        assert_eq!(load(&MESSAGE_NUM), 1, \"should receive one\");\n        assert_eq!(load(&CLOSE_NUM), 0, \"should not close\");\n\n        let r = socket.emit(\"restart_server\", json!(\"\"));\n        assert!(r.is_ok(), \"should emit restart success\");\n\n        // waiting for server to restart\n        for _ in 0..10 {\n            std::thread::sleep(std::time::Duration::from_millis(400));\n            if load(&CONNECT_NUM) == 2 && load(&MESSAGE_NUM) == 2 {\n                break;\n            }\n        }\n\n        assert_eq!(load(&CONNECT_NUM), 2, \"should connect twice\");\n        assert_eq!(load(&MESSAGE_NUM), 2, \"should receive two messages\");\n        assert_eq!(load(&CLOSE_NUM), 1, \"should close once\");\n\n        socket.disconnect()?;\n        Ok(())\n    }\n\n    #[test]\n    fn socket_io_reconnect_url_auth_integration() -> Result<()> {\n        static CONNECT_NUM: AtomicUsize = AtomicUsize::new(0);\n        static CLOSE_NUM: AtomicUsize = AtomicUsize::new(0);\n        static MESSAGE_NUM: AtomicUsize = AtomicUsize::new(0);\n\n        fn get_url() -> Url {\n            let timestamp = SystemTime::now()\n                .duration_since(UNIX_EPOCH)\n                .unwrap()\n                .as_millis();\n            let mut url = crate::test::socket_io_restart_url_auth_server();\n            url.set_query(Some(&format!(\"timestamp={timestamp}\")));\n            url\n        }\n\n        let socket = ClientBuilder::new(get_url())\n            .reconnect(true)\n            .max_reconnect_attempts(100)\n            .reconnect_delay(100, 100)\n            .on(Event::Connect, move |_, socket| {\n                CONNECT_NUM.fetch_add(1, Ordering::Release);\n                let result = socket.emit_with_ack(\n                    \"message\",\n                    json!(\"\"),\n                    Duration::from_millis(100),\n                    |_, _| {},\n                );\n                assert!(result.is_ok(), \"should emit message success\");\n            })\n            .on(Event::Close, move |_, _| {\n                CLOSE_NUM.fetch_add(1, Ordering::Release);\n            })\n            .on(\"message\", move |_, _| {\n                // test the iterator implementation and make sure there is a constant\n                // stream of packets, even when reconnecting\n                MESSAGE_NUM.fetch_add(1, Ordering::Release);\n            })\n            .connect();\n\n        assert!(socket.is_ok(), \"should connect success\");\n        let socket = socket.unwrap();\n\n        // waiting for server to emit message\n        std::thread::sleep(std::time::Duration::from_millis(500));\n\n        assert_eq!(load(&CONNECT_NUM), 1, \"should connect once\");\n        assert_eq!(load(&MESSAGE_NUM), 1, \"should receive one\");\n        assert_eq!(load(&CLOSE_NUM), 0, \"should not close\");\n\n        // waiting for timestamp in url to expire\n        std::thread::sleep(std::time::Duration::from_secs(1));\n\n        socket.set_reconnect_url(get_url())?;\n\n        let result = socket.emit(\"restart_server\", json!(\"\"));\n        assert!(result.is_ok(), \"should emit restart success\");\n\n        // waiting for server to restart\n        for _ in 0..10 {\n            std::thread::sleep(std::time::Duration::from_millis(400));\n            if load(&CONNECT_NUM) == 2 && load(&MESSAGE_NUM) == 2 {\n                break;\n            }\n        }\n\n        assert_eq!(load(&CONNECT_NUM), 2, \"should connect twice\");\n        assert_eq!(load(&MESSAGE_NUM), 2, \"should receive two messages\");\n        assert_eq!(load(&CLOSE_NUM), 1, \"should close once\");\n\n        socket.disconnect()?;\n        Ok(())\n    }\n\n    #[test]\n    fn socket_io_iterator_integration() -> Result<()> {\n        let url = crate::test::socket_io_server();\n        let builder = ClientBuilder::new(url);\n        let builder_clone = builder.clone();\n\n        let client = Arc::new(RwLock::new(builder_clone.connect_raw()?));\n        let mut socket = Client {\n            builder: Arc::new(Mutex::new(builder)),\n            client,\n            backoff: Default::default(),\n        };\n        let socket_clone = socket.clone();\n\n        let packets: Arc<RwLock<Vec<Packet>>> = Default::default();\n        let packets_clone = packets.clone();\n\n        std::thread::spawn(move || {\n            for packet in socket_clone.iter() {\n                {\n                    let mut packets = packets_clone.write().unwrap();\n                    if let Ok(packet) = packet {\n                        (*packets).push(packet);\n                    }\n                }\n            }\n        });\n\n        // waiting for client to emit messages\n        std::thread::sleep(Duration::from_millis(100));\n        let lock = packets.read().unwrap();\n        let pre_num = lock.len();\n        drop(lock);\n\n        let _ = socket.disconnect();\n        socket.reconnect()?;\n\n        // waiting for client to emit messages\n        std::thread::sleep(Duration::from_millis(100));\n\n        let lock = packets.read().unwrap();\n        let post_num = lock.len();\n        drop(lock);\n\n        assert!(\n            pre_num < post_num,\n            \"pre_num {} should less than post_num {}\",\n            pre_num,\n            post_num\n        );\n\n        Ok(())\n    }\n\n    fn load(num: &AtomicUsize) -> usize {\n        num.load(Ordering::Acquire)\n    }\n}\n"
  },
  {
    "path": "socketio/src/client/mod.rs",
    "content": "mod builder;\nmod raw_client;\n\npub use builder::ClientBuilder;\npub use builder::TransportType;\npub use client::Client;\npub use raw_client::RawClient;\n\n/// Internal callback type\nmod callback;\nmod client;\n"
  },
  {
    "path": "socketio/src/client/raw_client.rs",
    "content": "use super::callback::Callback;\nuse crate::packet::{Packet, PacketId};\nuse crate::Error;\npub(crate) use crate::{event::CloseReason, event::Event, payload::Payload};\nuse rand::{thread_rng, Rng};\nuse serde_json::Value;\n\nuse crate::client::callback::{SocketAnyCallback, SocketCallback};\nuse crate::error::Result;\nuse std::collections::HashMap;\nuse std::ops::DerefMut;\nuse std::sync::{Arc, Mutex};\nuse std::time::Duration;\nuse std::time::Instant;\n\nuse crate::socket::Socket as InnerSocket;\n\n/// Represents an `Ack` as given back to the caller. Holds the internal `id` as\n/// well as the current ack'ed state. Holds data which will be accessible as\n/// soon as the ack'ed state is set to true. An `Ack` that didn't get ack'ed\n/// won't contain data.\n#[derive(Debug)]\npub struct Ack {\n    pub id: i32,\n    timeout: Duration,\n    time_started: Instant,\n    callback: Callback<SocketCallback>,\n}\n\n/// A socket which handles communication with the server. It's initialized with\n/// a specific address as well as an optional namespace to connect to. If `None`\n/// is given the server will connect to the default namespace `\"/\"`.\n#[derive(Clone)]\npub struct RawClient {\n    /// The inner socket client to delegate the methods to.\n    socket: InnerSocket,\n    on: Arc<Mutex<HashMap<Event, Callback<SocketCallback>>>>,\n    on_any: Arc<Mutex<Option<Callback<SocketAnyCallback>>>>,\n    outstanding_acks: Arc<Mutex<Vec<Ack>>>,\n    // namespace, for multiplexing messages\n    nsp: String,\n    // Data send in the opening packet (commonly used as for auth)\n    auth: Option<Value>,\n}\n\nimpl RawClient {\n    /// Creates a socket with a certain address to connect to as well as a\n    /// namespace. If `None` is passed in as namespace, the default namespace\n    /// `\"/\"` is taken.\n    /// ```\n    pub(crate) fn new<T: Into<String>>(\n        socket: InnerSocket,\n        namespace: T,\n        on: Arc<Mutex<HashMap<Event, Callback<SocketCallback>>>>,\n        on_any: Arc<Mutex<Option<Callback<SocketAnyCallback>>>>,\n        auth: Option<Value>,\n    ) -> Result<Self> {\n        Ok(RawClient {\n            socket,\n            nsp: namespace.into(),\n            on,\n            on_any,\n            outstanding_acks: Arc::new(Mutex::new(Vec::new())),\n            auth,\n        })\n    }\n\n    /// Connects the client to a server. Afterwards the `emit_*` methods can be\n    /// called to interact with the server. Attention: it's not allowed to add a\n    /// callback after a call to this method.\n    pub(crate) fn connect(&self) -> Result<()> {\n        // Connect the underlying socket\n        self.socket.connect()?;\n\n        let auth = self.auth.as_ref().map(|data| data.to_string());\n\n        // construct the opening packet\n        let open_packet = Packet::new(PacketId::Connect, self.nsp.clone(), auth, None, 0, None);\n\n        self.socket.send(open_packet)?;\n\n        Ok(())\n    }\n\n    /// Sends a message to the server using the underlying `engine.io` protocol.\n    /// This message takes an event, which could either be one of the common\n    /// events like \"message\" or \"error\" or a custom event like \"foo\". But be\n    /// careful, the data string needs to be valid JSON. It's recommended to use\n    /// a library like `serde_json` to serialize the data properly.\n    ///\n    /// # Example\n    /// ```\n    /// use rust_socketio::{ClientBuilder, RawClient, Payload};\n    /// use serde_json::json;\n    ///\n    /// let mut socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///     .on(\"test\", |payload: Payload, socket: RawClient| {\n    ///         println!(\"Received: {:#?}\", payload);\n    ///         socket.emit(\"test\", json!({\"hello\": true})).expect(\"Server unreachable\");\n    ///      })\n    ///     .connect()\n    ///     .expect(\"connection failed\");\n    ///\n    /// let json_payload = json!({\"token\": 123});\n    ///\n    /// let result = socket.emit(\"foo\", json_payload);\n    ///\n    /// assert!(result.is_ok());\n    /// ```\n    #[inline]\n    pub fn emit<E, D>(&self, event: E, data: D) -> Result<()>\n    where\n        E: Into<Event>,\n        D: Into<Payload>,\n    {\n        self.socket.emit(&self.nsp, event.into(), data.into())\n    }\n\n    /// Disconnects this client from the server by sending a `socket.io` closing\n    /// packet.\n    /// # Example\n    /// ```rust\n    /// use rust_socketio::{ClientBuilder, Payload, RawClient};\n    /// use serde_json::json;\n    ///\n    /// fn handle_test(payload: Payload, socket: RawClient) {\n    ///     println!(\"Received: {:#?}\", payload);\n    ///     socket.emit(\"test\", json!({\"hello\": true})).expect(\"Server unreachable\");\n    /// }\n    ///\n    /// let mut socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///     .on(\"test\", handle_test)\n    ///     .connect()\n    ///     .expect(\"connection failed\");\n    ///\n    /// let json_payload = json!({\"token\": 123});\n    ///\n    /// socket.emit(\"foo\", json_payload);\n    ///\n    /// // disconnect from the server\n    /// socket.disconnect();\n    ///\n    /// ```\n    pub fn disconnect(&self) -> Result<()> {\n        let disconnect_packet =\n            Packet::new(PacketId::Disconnect, self.nsp.clone(), None, None, 0, None);\n\n        // TODO: logging\n        let _ = self.socket.send(disconnect_packet);\n        self.socket.disconnect()?;\n\n        let _ = self.callback(&Event::Close, CloseReason::IOClientDisconnect.as_str()); // trigger on_close\n        Ok(())\n    }\n\n    /// Sends a message to the server but `alloc`s an `ack` to check whether the\n    /// server responded in a given time span. This message takes an event, which\n    /// could either be one of the common events like \"message\" or \"error\" or a\n    /// custom event like \"foo\", as well as a data parameter. But be careful,\n    /// in case you send a [`Payload::String`], the string needs to be valid JSON.\n    /// It's even recommended to use a library like serde_json to serialize the data properly.\n    /// It also requires a timeout `Duration` in which the client needs to answer.\n    /// If the ack is acked in the correct time span, the specified callback is\n    /// called. The callback consumes a [`Payload`] which represents the data send\n    /// by the server.\n    ///\n    /// # Example\n    /// ```\n    /// use rust_socketio::{ClientBuilder, Payload, RawClient};\n    /// use serde_json::json;\n    /// use std::time::Duration;\n    /// use std::thread::sleep;\n    ///\n    /// let mut socket = ClientBuilder::new(\"http://localhost:4200/\")\n    ///     .on(\"foo\", |payload: Payload, _| println!(\"Received: {:#?}\", payload))\n    ///     .connect()\n    ///     .expect(\"connection failed\");\n    ///\n    /// let ack_callback = |message: Payload, socket: RawClient| {\n    ///     match message {\n    ///         Payload::Text(values) => println!(\"{:#?}\", values),\n    ///         Payload::Binary(bytes) => println!(\"Received bytes: {:#?}\", bytes),\n    ///         // This is deprecated, use Payload::Text instead\n    ///         Payload::String(str) => println!(\"{}\", str),\n    ///    }\n    /// };\n    ///\n    /// let payload = json!({\"token\": 123});\n    /// socket.emit_with_ack(\"foo\", payload, Duration::from_secs(2), ack_callback).unwrap();\n    ///\n    /// sleep(Duration::from_secs(2));\n    /// ```\n    #[inline]\n    pub fn emit_with_ack<F, E, D>(\n        &self,\n        event: E,\n        data: D,\n        timeout: Duration,\n        callback: F,\n    ) -> Result<()>\n    where\n        F: FnMut(Payload, RawClient) + 'static + Send,\n        E: Into<Event>,\n        D: Into<Payload>,\n    {\n        let id = thread_rng().gen_range(0..999);\n        let socket_packet =\n            Packet::new_from_payload(data.into(), event.into(), &self.nsp, Some(id))?;\n\n        let ack = Ack {\n            id,\n            time_started: Instant::now(),\n            timeout,\n            callback: Callback::<SocketCallback>::new(callback),\n        };\n\n        // add the ack to the tuple of outstanding acks\n        self.outstanding_acks.lock()?.push(ack);\n\n        self.socket.send(socket_packet)?;\n        Ok(())\n    }\n\n    pub(crate) fn poll(&self) -> Result<Option<Packet>> {\n        loop {\n            match self.socket.poll() {\n                Err(err) => {\n                    self.callback(&Event::Error, err.to_string())?;\n                    return Err(err);\n                }\n                Ok(Some(packet)) => {\n                    if packet.nsp == self.nsp {\n                        self.handle_socketio_packet(&packet)?;\n                        return Ok(Some(packet));\n                    } else {\n                        // Not our namespace continue polling\n                    }\n                }\n                Ok(None) => return Ok(None),\n            }\n        }\n    }\n\n    #[cfg(test)]\n    pub(crate) fn iter(&self) -> Iter {\n        Iter { socket: self }\n    }\n\n    fn callback<P: Into<Payload>>(&self, event: &Event, payload: P) -> Result<()> {\n        let mut on = self.on.lock()?;\n        let mut on_any = self.on_any.lock()?;\n        let lock = on.deref_mut();\n        let on_any_lock = on_any.deref_mut();\n\n        let payload = payload.into();\n\n        if let Some(callback) = lock.get_mut(event) {\n            callback(payload.clone(), self.clone());\n        }\n        match event {\n            Event::Message | Event::Custom(_) => {\n                if let Some(callback) = on_any_lock {\n                    callback(event.clone(), payload, self.clone())\n                }\n            }\n            _ => {}\n        }\n        drop(on);\n        drop(on_any);\n        Ok(())\n    }\n\n    /// Handles the incoming acks and classifies what callbacks to call and how.\n    #[inline]\n    fn handle_ack(&self, socket_packet: &Packet) -> Result<()> {\n        let Some(id) = socket_packet.id else {\n            return Ok(());\n        };\n\n        let outstanding_ack = {\n            let mut outstanding_acks = self.outstanding_acks.lock()?;\n            outstanding_acks\n                .iter()\n                .position(|ack| ack.id == id)\n                .map(|pos| outstanding_acks.remove(pos))\n        };\n\n        // If we found a matching ack, call its callback otherwise ignore it.\n        // The official implementation just removes the ack id on timeout:\n        // https://github.com/socketio/socket.io-client/blob/main/lib/socket.ts#L467-L495\n        if let Some(mut ack) = outstanding_ack {\n            if ack.time_started.elapsed() < ack.timeout {\n                if let Some(ref payload) = socket_packet.data {\n                    ack.callback.deref_mut()(Payload::from(payload.to_owned()), self.clone());\n                }\n\n                if let Some(ref attachments) = socket_packet.attachments {\n                    if let Some(payload) = attachments.first() {\n                        ack.callback.deref_mut()(Payload::Binary(payload.to_owned()), self.clone());\n                    }\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Handles a binary event.\n    #[inline]\n    fn handle_binary_event(&self, packet: &Packet) -> Result<()> {\n        let event = if let Some(string_data) = &packet.data {\n            string_data.replace('\\\"', \"\").into()\n        } else {\n            Event::Message\n        };\n\n        if let Some(attachments) = &packet.attachments {\n            if let Some(binary_payload) = attachments.first() {\n                self.callback(&event, Payload::Binary(binary_payload.to_owned()))?;\n            }\n        }\n        Ok(())\n    }\n\n    /// A method that parses a packet and eventually calls the corresponding\n    /// callback with the supplied data.\n    fn handle_event(&self, packet: &Packet) -> Result<()> {\n        let Some(ref data) = packet.data else {\n            return Ok(());\n        };\n\n        // a socketio message always comes in one of the following two flavors (both JSON):\n        // 1: `[\"event\", \"msg\", ...]`\n        // 2: `[\"msg\"]`\n        // in case 2, the message is ment for the default message event, in case 1 the event\n        // is specified\n        if let Ok(Value::Array(contents)) = serde_json::from_str::<Value>(data) {\n            let (event, payloads) = match contents.len() {\n                0 => return Err(Error::IncompletePacket()),\n                1 => (Event::Message, contents.as_slice()),\n                // get rest of data if first is a event\n                _ => match contents.first() {\n                    Some(Value::String(ev)) => (Event::from(ev.as_str()), &contents[1..]),\n                    // get rest(1..) of them as data, not just take the 2nd element\n                    _ => (Event::Message, contents.as_slice()),\n                    // take them all as data\n                },\n            };\n\n            // call the correct callback\n            self.callback(&event, payloads.to_vec())?;\n        }\n\n        Ok(())\n    }\n\n    /// Handles the incoming messages and classifies what callbacks to call and how.\n    /// This method is later registered as the callback for the `on_data` event of the\n    /// engineio client.\n    #[inline]\n    fn handle_socketio_packet(&self, packet: &Packet) -> Result<()> {\n        if packet.nsp == self.nsp {\n            match packet.packet_type {\n                PacketId::Ack | PacketId::BinaryAck => {\n                    if let Err(err) = self.handle_ack(packet) {\n                        self.callback(&Event::Error, err.to_string())?;\n                        return Err(err);\n                    }\n                }\n                PacketId::BinaryEvent => {\n                    if let Err(err) = self.handle_binary_event(packet) {\n                        self.callback(&Event::Error, err.to_string())?;\n                    }\n                }\n                PacketId::Connect => {\n                    self.callback(&Event::Connect, \"\")?;\n                }\n                PacketId::Disconnect => {\n                    self.callback(&Event::Close, CloseReason::IOServerDisconnect.as_str())?;\n                }\n                PacketId::ConnectError => {\n                    self.callback(\n                        &Event::Error,\n                        String::from(\"Received an ConnectError frame: \")\n                            + &packet\n                                .clone()\n                                .data\n                                .unwrap_or_else(|| String::from(\"\\\"No error message provided\\\"\")),\n                    )?;\n                }\n                PacketId::Event => {\n                    if let Err(err) = self.handle_event(packet) {\n                        self.callback(&Event::Error, err.to_string())?;\n                    }\n                }\n            }\n        }\n        Ok(())\n    }\n}\n\npub struct Iter<'a> {\n    socket: &'a RawClient,\n}\n\nimpl<'a> Iterator for Iter<'a> {\n    type Item = Result<Packet>;\n    fn next(&mut self) -> std::option::Option<<Self as std::iter::Iterator>::Item> {\n        match self.socket.poll() {\n            Err(err) => Some(Err(err)),\n            Ok(Some(packet)) => Some(Ok(packet)),\n            Ok(None) => None,\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use std::sync::mpsc;\n    use std::thread::sleep;\n\n    use super::*;\n    use crate::{client::TransportType, payload::Payload, ClientBuilder};\n    use bytes::Bytes;\n    use native_tls::TlsConnector;\n    use serde_json::json;\n    use std::time::Duration;\n\n    #[test]\n    fn socket_io_integration() -> Result<()> {\n        let url = crate::test::socket_io_server();\n\n        let socket = ClientBuilder::new(url)\n            .on(\"test\", |msg, _| match msg {\n                #[allow(deprecated)]\n                Payload::String(str) => println!(\"Received string: {}\", str),\n                Payload::Text(text) => println!(\"Received json: {:#?}\", text),\n                Payload::Binary(bin) => println!(\"Received binary data: {:#?}\", bin),\n            })\n            .connect()?;\n\n        let payload = json!({\"token\": 123});\n        #[allow(deprecated)]\n        let result = socket.emit(\"test\", Payload::String(payload.to_string()));\n\n        assert!(result.is_ok());\n\n        let ack_callback = move |message: Payload, socket: RawClient| {\n            let result = socket.emit(\"test\", Payload::Text(vec![json!({\"got ack\": true})]));\n            assert!(result.is_ok());\n\n            println!(\"Yehaa! My ack got acked?\");\n            if let Payload::Text(values) = message {\n                println!(\"Received json Ack\");\n                println!(\"Ack data: {:#?}\", values);\n            }\n        };\n\n        let ack = socket.emit_with_ack(\n            \"test\",\n            Payload::Text(vec![payload]),\n            Duration::from_secs(1),\n            ack_callback,\n        );\n        assert!(ack.is_ok());\n\n        sleep(Duration::from_secs(2));\n\n        assert!(socket.disconnect().is_ok());\n\n        Ok(())\n    }\n\n    #[test]\n    fn socket_io_builder_integration() -> Result<()> {\n        let url = crate::test::socket_io_server();\n\n        // test socket build logic\n        let socket_builder = ClientBuilder::new(url);\n\n        let tls_connector = TlsConnector::builder()\n            .use_sni(true)\n            .build()\n            .expect(\"Found illegal configuration\");\n\n        let socket = socket_builder\n            .namespace(\"/admin\")\n            .tls_config(tls_connector)\n            .opening_header(\"accept-encoding\", \"application/json\")\n            .on(\"test\", |str, _| println!(\"Received: {:#?}\", str))\n            .on(\"message\", |payload, _| println!(\"{:#?}\", payload))\n            .connect()?;\n\n        assert!(socket.emit(\"message\", json!(\"Hello World\")).is_ok());\n\n        assert!(socket.emit(\"binary\", Bytes::from_static(&[46, 88])).is_ok());\n\n        assert!(socket\n            .emit_with_ack(\n                \"binary\",\n                json!(\"pls ack\"),\n                Duration::from_secs(1),\n                |payload, _| {\n                    println!(\"Yehaa the ack got acked\");\n                    println!(\"With data: {:#?}\", payload);\n                }\n            )\n            .is_ok());\n\n        sleep(Duration::from_secs(2));\n\n        Ok(())\n    }\n\n    #[test]\n    fn socket_io_builder_integration_iterator() -> Result<()> {\n        let url = crate::test::socket_io_server();\n\n        // test socket build logic\n        let socket_builder = ClientBuilder::new(url);\n\n        let tls_connector = TlsConnector::builder()\n            .use_sni(true)\n            .build()\n            .expect(\"Found illegal configuration\");\n\n        let socket = socket_builder\n            .namespace(\"/admin\")\n            .tls_config(tls_connector)\n            .opening_header(\"accept-encoding\", \"application/json\")\n            .on(\"test\", |str, _| println!(\"Received: {:#?}\", str))\n            .on(\"message\", |payload, _| println!(\"{:#?}\", payload))\n            .connect_raw()?;\n\n        assert!(socket.emit(\"message\", json!(\"Hello World\")).is_ok());\n\n        assert!(socket.emit(\"binary\", Bytes::from_static(&[46, 88])).is_ok());\n\n        assert!(socket\n            .emit_with_ack(\n                \"binary\",\n                json!(\"pls ack\"),\n                Duration::from_secs(1),\n                |payload, _| {\n                    println!(\"Yehaa the ack got acked\");\n                    println!(\"With data: {:#?}\", payload);\n                }\n            )\n            .is_ok());\n\n        test_socketio_socket(socket, \"/admin\".to_owned())\n    }\n\n    #[test]\n    fn socket_io_on_any_integration() -> Result<()> {\n        let url = crate::test::socket_io_server();\n\n        let (tx, rx) = mpsc::sync_channel(1);\n\n        let _socket = ClientBuilder::new(url)\n            .namespace(\"/\")\n            .auth(json!({ \"password\": \"123\" }))\n            .on(\"auth\", |payload, _client| {\n                if let Payload::Text(payload) = payload {\n                    println!(\"{:#?}\", payload);\n                }\n            })\n            .on_any(move |event, payload, _client| {\n                if let Payload::Text(payload) = payload {\n                    println!(\"{event} {payload:#?}\");\n                }\n                tx.send(String::from(event)).unwrap();\n            })\n            .connect()?;\n\n        // Sleep to give server enough time to send 2 events\n        sleep(Duration::from_secs(2));\n\n        let event = rx.recv().unwrap();\n        assert_eq!(event, \"message\");\n        let event = rx.recv().unwrap();\n        assert_eq!(event, \"test\");\n\n        Ok(())\n    }\n\n    #[test]\n    fn socket_io_auth_builder_integration() -> Result<()> {\n        let url = crate::test::socket_io_auth_server();\n        let nsp = String::from(\"/admin\");\n        let socket = ClientBuilder::new(url)\n            .namespace(nsp.clone())\n            .auth(json!({ \"password\": \"123\" }))\n            .connect_raw()?;\n\n        let mut iter = socket\n            .iter()\n            .map(|packet| packet.unwrap())\n            .filter(|packet| packet.packet_type != PacketId::Connect);\n\n        let packet: Option<Packet> = iter.next();\n        assert!(packet.is_some());\n\n        let packet = packet.unwrap();\n\n        assert_eq!(\n            packet,\n            Packet::new(\n                PacketId::Event,\n                nsp,\n                Some(\"[\\\"auth\\\",\\\"success\\\"]\".to_owned()),\n                None,\n                0,\n                None\n            )\n        );\n\n        Ok(())\n    }\n\n    #[test]\n    fn socketio_polling_integration() -> Result<()> {\n        let url = crate::test::socket_io_server();\n        let socket = ClientBuilder::new(url)\n            .transport_type(TransportType::Polling)\n            .connect_raw()?;\n        test_socketio_socket(socket, \"/\".to_owned())\n    }\n\n    #[test]\n    fn socket_io_websocket_integration() -> Result<()> {\n        let url = crate::test::socket_io_server();\n        let socket = ClientBuilder::new(url)\n            .transport_type(TransportType::Websocket)\n            .connect_raw()?;\n        test_socketio_socket(socket, \"/\".to_owned())\n    }\n\n    #[test]\n    fn socket_io_websocket_upgrade_integration() -> Result<()> {\n        let url = crate::test::socket_io_server();\n        let socket = ClientBuilder::new(url)\n            .transport_type(TransportType::WebsocketUpgrade)\n            .connect_raw()?;\n        test_socketio_socket(socket, \"/\".to_owned())\n    }\n\n    #[test]\n    fn socket_io_any_integration() -> Result<()> {\n        let url = crate::test::socket_io_server();\n        let socket = ClientBuilder::new(url)\n            .transport_type(TransportType::Any)\n            .connect_raw()?;\n        test_socketio_socket(socket, \"/\".to_owned())\n    }\n\n    fn test_socketio_socket(socket: RawClient, nsp: String) -> Result<()> {\n        let mut iter = socket\n            .iter()\n            .map(|packet| packet.unwrap())\n            .filter(|packet| packet.packet_type != PacketId::Connect);\n\n        let packet: Option<Packet> = iter.next();\n        assert!(packet.is_some());\n\n        let packet = packet.unwrap();\n\n        assert_eq!(\n            packet,\n            Packet::new(\n                PacketId::Event,\n                nsp.clone(),\n                Some(\"[\\\"Hello from the message event!\\\"]\".to_owned()),\n                None,\n                0,\n                None,\n            )\n        );\n\n        let packet: Option<Packet> = iter.next();\n        assert!(packet.is_some());\n\n        let packet = packet.unwrap();\n\n        assert_eq!(\n            packet,\n            Packet::new(\n                PacketId::Event,\n                nsp.clone(),\n                Some(\"[\\\"test\\\",\\\"Hello from the test event!\\\"]\".to_owned()),\n                None,\n                0,\n                None\n            )\n        );\n\n        let packet: Option<Packet> = iter.next();\n        assert!(packet.is_some());\n\n        let packet = packet.unwrap();\n        assert_eq!(\n            packet,\n            Packet::new(\n                PacketId::BinaryEvent,\n                nsp.clone(),\n                None,\n                None,\n                1,\n                Some(vec![Bytes::from_static(&[4, 5, 6])]),\n            )\n        );\n\n        let packet: Option<Packet> = iter.next();\n        assert!(packet.is_some());\n\n        let packet = packet.unwrap();\n        assert_eq!(\n            packet,\n            Packet::new(\n                PacketId::BinaryEvent,\n                nsp.clone(),\n                Some(\"\\\"test\\\"\".to_owned()),\n                None,\n                1,\n                Some(vec![Bytes::from_static(&[1, 2, 3])]),\n            )\n        );\n\n        let packet: Option<Packet> = iter.next();\n\n        assert!(packet.is_some());\n\n        let packet = packet.unwrap();\n        assert_eq!(\n            packet,\n            Packet::new(\n                PacketId::Event,\n                nsp.clone(),\n                Some(\n                    serde_json::Value::Array(vec![\n                        serde_json::Value::from(\"This is the first argument\"),\n                        serde_json::Value::from(\"This is the second argument\"),\n                        serde_json::json!({\"argCount\":3})\n                    ])\n                    .to_string()\n                ),\n                None,\n                0,\n                None,\n            )\n        );\n\n        let packet: Option<Packet> = iter.next();\n\n        assert!(packet.is_some());\n\n        let packet = packet.unwrap();\n        assert_eq!(\n            packet,\n            Packet::new(\n                PacketId::Event,\n                nsp.clone(),\n                Some(\n                    serde_json::json!([\n                        \"on_abc_event\",\n                        \"\",\n                        {\n                        \"abc\": 0,\n                        \"some_other\": \"value\",\n                        }\n                    ])\n                    .to_string()\n                ),\n                None,\n                0,\n                None,\n            )\n        );\n\n        assert!(socket\n            .emit_with_ack(\n                \"test\",\n                Payload::from(\"123\"),\n                Duration::from_secs(10),\n                |message: Payload, _| {\n                    println!(\"Yehaa! My ack got acked?\");\n                    if let Payload::Text(values) = message {\n                        println!(\"Received ack\");\n                        println!(\"Ack data: {values:#?}\");\n                    }\n                }\n            )\n            .is_ok());\n\n        Ok(())\n    }\n\n    // TODO: add secure socketio server\n}\n"
  },
  {
    "path": "socketio/src/error.rs",
    "content": "use base64::DecodeError;\nuse serde_json::Error as JsonError;\nuse std::io::Error as IoError;\nuse std::num::ParseIntError;\nuse std::str::Utf8Error;\nuse thiserror::Error;\nuse url::ParseError as UrlParseError;\n\n/// Enumeration of all possible errors in the `socket.io` context.\n/// TODO: 0.4.X Do not expose non-trivial internal errors. Convert error to string.\n#[derive(Error, Debug)]\n#[non_exhaustive]\n#[cfg_attr(tarpaulin, ignore)]\npub enum Error {\n    // Conform to https://rust-lang.github.io/api-guidelines/naming.html#names-use-a-consistent-word-order-c-word-order\n    // Negative verb-object\n    #[error(\"Invalid packet id: {0}\")]\n    InvalidPacketId(char),\n    #[error(\"Error while parsing an incomplete packet\")]\n    IncompletePacket(),\n    #[error(\"Got an invalid packet which did not follow the protocol format\")]\n    InvalidPacket(),\n    #[error(\"An error occurred while decoding the utf-8 text: {0}\")]\n    InvalidUtf8(#[from] Utf8Error),\n    #[error(\"An error occurred while encoding/decoding base64: {0}\")]\n    InvalidBase64(#[from] DecodeError),\n    #[error(\"Invalid Url during parsing\")]\n    InvalidUrl(#[from] UrlParseError),\n    #[error(\"Invalid Url Scheme: {0}\")]\n    InvalidUrlScheme(String),\n    #[error(\"Got illegal handshake response: {0}\")]\n    InvalidHandshake(String),\n    #[error(\"Called an action before the connection was established\")]\n    IllegalActionBeforeOpen(),\n    #[error(\"string is not json serializable: {0}\")]\n    InvalidJson(#[from] JsonError),\n    #[error(\"A lock was poisoned\")]\n    InvalidPoisonedLock(),\n    #[error(\"Got an IO-Error: {0}\")]\n    IncompleteIo(#[from] IoError),\n    #[error(\"Error while parsing an integer\")]\n    InvalidInteger(#[from] ParseIntError),\n    #[error(\"EngineIO Error\")]\n    IncompleteResponseFromEngineIo(#[from] rust_engineio::Error),\n    #[error(\"Invalid packet type while reading attachments\")]\n    InvalidAttachmentPacketType(u8),\n    #[error(\"Underlying Engine.IO connection has closed\")]\n    StoppedEngineIoSocket,\n}\n\npub(crate) type Result<T> = std::result::Result<T, Error>;\n\nimpl<T> From<std::sync::PoisonError<T>> for Error {\n    fn from(_: std::sync::PoisonError<T>) -> Self {\n        Self::InvalidPoisonedLock()\n    }\n}\n\nimpl From<Error> for std::io::Error {\n    fn from(err: Error) -> std::io::Error {\n        std::io::Error::new(std::io::ErrorKind::Other, err)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::sync::{Mutex, PoisonError};\n\n    use super::*;\n\n    /// This just tests the own implementations and relies on `thiserror` for the others.\n    #[test]\n    fn test_error_conversion() {\n        let mutex = Mutex::new(0);\n        let _error = Error::from(PoisonError::new(mutex.lock()));\n        assert!(matches!(Error::InvalidPoisonedLock(), _error));\n\n        let _io_error = std::io::Error::from(Error::IncompletePacket());\n        let _error = std::io::Error::new(std::io::ErrorKind::Other, Error::IncompletePacket());\n        assert!(matches!(_io_error, _error));\n    }\n}\n"
  },
  {
    "path": "socketio/src/event.rs",
    "content": "use std::fmt::{Display, Formatter, Result as FmtResult};\n\n/// An `Event` in `socket.io` could either (`Message`, `Error`) or custom.\n#[derive(Debug, PartialEq, PartialOrd, Clone, Eq, Hash)]\npub enum Event {\n    Message,\n    Error,\n    Custom(String),\n    Connect,\n    Close,\n}\n\nimpl Event {\n    pub fn as_str(&self) -> &str {\n        match self {\n            Event::Message => \"message\",\n            Event::Error => \"error\",\n            Event::Connect => \"connect\",\n            Event::Close => \"close\",\n            Event::Custom(string) => string,\n        }\n    }\n}\n\nimpl From<String> for Event {\n    fn from(string: String) -> Self {\n        match &string.to_lowercase()[..] {\n            \"message\" => Event::Message,\n            \"error\" => Event::Error,\n            \"open\" => Event::Connect,\n            \"close\" => Event::Close,\n            _ => Event::Custom(string),\n        }\n    }\n}\n\nimpl From<&str> for Event {\n    fn from(string: &str) -> Self {\n        Event::from(String::from(string))\n    }\n}\n\nimpl From<Event> for String {\n    fn from(event: Event) -> Self {\n        match event {\n            Event::Message => Self::from(\"message\"),\n            Event::Connect => Self::from(\"open\"),\n            Event::Close => Self::from(\"close\"),\n            Event::Error => Self::from(\"error\"),\n            Event::Custom(string) => string,\n        }\n    }\n}\n\nimpl Display for Event {\n    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {\n        f.write_str(self.as_str())\n    }\n}\n\n/// A `CloseReason` is the payload of the [`Event::Close`] and specifies the reason for\n/// why it was fired.\n/// These are aligned with the official Socket.IO disconnect reasons, see\n/// https://socket.io/docs/v4/client-socket-instance/#disconnect\n#[derive(Debug, PartialEq, PartialOrd, Clone, Eq, Hash)]\npub enum CloseReason {\n    IOServerDisconnect,\n    IOClientDisconnect,\n    TransportClose,\n}\n\nimpl CloseReason {\n    pub fn as_str(&self) -> &str {\n        match self {\n            // Inspired by https://github.com/socketio/socket.io/blob/d0fc72042068e7eaef448941add617f05e1ec236/packages/socket.io-client/lib/socket.ts#L865\n            CloseReason::IOServerDisconnect => \"io server disconnect\",\n            // Inspired by https://github.com/socketio/socket.io/blob/d0fc72042068e7eaef448941add617f05e1ec236/packages/socket.io-client/lib/socket.ts#L911\n            CloseReason::IOClientDisconnect => \"io client disconnect\",\n            CloseReason::TransportClose => \"transport close\",\n        }\n    }\n}\n\nimpl From<CloseReason> for String {\n    fn from(event: CloseReason) -> Self {\n        Self::from(event.as_str())\n    }\n}\n\nimpl Display for CloseReason {\n    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {\n        f.write_str(self.as_str())\n    }\n}\n"
  },
  {
    "path": "socketio/src/lib.rs",
    "content": "//! Rust-socket.io is a socket.io client written in the Rust Programming Language.\n//! ## Example usage\n//!\n//! ``` rust\n//! use rust_socketio::{ClientBuilder, Payload, RawClient};\n//! use serde_json::json;\n//! use std::time::Duration;\n//!\n//! // define a callback which is called when a payload is received\n//! // this callback gets the payload as well as an instance of the\n//! // socket to communicate with the server\n//! let callback = |payload: Payload, socket: RawClient| {\n//!        match payload {\n//!            Payload::Text(values) => println!(\"Received: {:#?}\", values),\n//!            Payload::Binary(bin_data) => println!(\"Received bytes: {:#?}\", bin_data),\n//!            // This variant is deprecated, use Payload::Text instead\n//!            Payload::String(str) => println!(\"Received: {}\", str),\n//!        }\n//!        socket.emit(\"test\", json!({\"got ack\": true})).expect(\"Server unreachable\")\n//! };\n//!\n//! // get a socket that is connected to the admin namespace\n//! let mut socket = ClientBuilder::new(\"http://localhost:4200/\")\n//!      .namespace(\"/admin\")\n//!      .on(\"test\", callback)\n//!      .on(\"error\", |err, _| eprintln!(\"Error: {:#?}\", err))\n//!      .connect()\n//!      .expect(\"Connection failed\");\n//!\n//! // emit to the \"foo\" event\n//! let json_payload = json!({\"token\": 123});\n//!\n//! socket.emit(\"foo\", json_payload).expect(\"Server unreachable\");\n//!\n//! // define a callback, that's executed when the ack got acked\n//! let ack_callback = |message: Payload, _: RawClient| {\n//!     println!(\"Yehaa! My ack got acked?\");\n//!     println!(\"Ack data: {:#?}\", message);\n//! };\n//!\n//! let json_payload = json!({\"myAckData\": 123});\n//!\n//! // emit with an ack\n//! let ack = socket\n//!     .emit_with_ack(\"test\", json_payload, Duration::from_secs(2), ack_callback)\n//!     .expect(\"Server unreachable\");\n//! ```\n//!\n//! The main entry point for using this crate is the [`ClientBuilder`] which provides\n//! a way to easily configure a socket in the needed way. When the `connect` method\n//! is called on the builder, it returns a connected client which then could be used\n//! to emit messages to certain events. One client can only be connected to one namespace.\n//! If you need to listen to the messages in different namespaces you need to\n//! allocate multiple sockets.\n//!\n//! ## Current features\n//!\n//! This implementation now supports all of the features of the socket.io protocol mentioned\n//! [here](https://github.com/socketio/socket.io-protocol).\n//! It generally tries to make use of websockets as often as possible. This means most times\n//! only the opening request uses http and as soon as the server mentions that he is able to use\n//! websockets, an upgrade  is performed. But if this upgrade is not successful or the server\n//! does not mention an upgrade possibility, http-long polling is used (as specified in the protocol specs).\n//!\n//! Here's an overview of possible use-cases:\n//!\n//! - connecting to a server.\n//! - register callbacks for the following event types:\n//!     - open\n//!     - close\n//!     - error\n//!     - message\n//!     - custom events like \"foo\", \"on_payment\", etc.\n//! - send JSON data to the server (via `serde_json` which provides safe\n//! handling).\n//! - send JSON data to the server and receive an `ack`.\n//! - send and handle Binary data.\n#![cfg_attr(\n    feature = \"async\",\n    doc = r#\"\n## Async version\nThis library provides an ability for being executed in an asynchronous context using `tokio` as\nthe execution runtime.\nPlease note that the current async implementation is in beta, the interface can be object to\ndrastic changes.\nThe async `Client` and `ClientBuilder` support a similar interface to the sync version and live\nin the [`asynchronous`] module. In order to enable the support, you need to enable the `async`\nfeature flag:\n```toml\nrust_socketio = { version = \"^0.4.1\", features = [\"async\"] }\n```\n\nThe following code shows the example above in async fashion:\n\n``` rust\nuse futures_util::FutureExt;\nuse rust_socketio::{\n    asynchronous::{Client, ClientBuilder},\n    Payload,\n};\nuse serde_json::json;\nuse std::time::Duration;\n\n#[tokio::main]\nasync fn main() {\n    // define a callback which is called when a payload is received\n    // this callback gets the payload as well as an instance of the\n    // socket to communicate with the server\n    let callback = |payload: Payload, socket: Client| {\n        async move {\n            match payload {\n                Payload::Text(values) => println!(\"Received: {:#?}\", values),\n                Payload::Binary(bin_data) => println!(\"Received bytes: {:#?}\", bin_data),\n                // This is deprecated use Payload::Text instead\n                Payload::String(str) => println!(\"Received: {}\", str),\n            }\n            socket\n                .emit(\"test\", json!({\"got ack\": true}))\n                .await\n                .expect(\"Server unreachable\");\n        }\n        .boxed()\n    };\n\n    // get a socket that is connected to the admin namespace\n    let socket = ClientBuilder::new(\"http://localhost:4200/\")\n        .namespace(\"/admin\")\n        .on(\"test\", callback)\n        .on(\"error\", |err, _| {\n            async move { eprintln!(\"Error: {:#?}\", err) }.boxed()\n        })\n        .connect()\n        .await\n        .expect(\"Connection failed\");\n\n    // emit to the \"foo\" event\n    let json_payload = json!({\"token\": 123});\n    socket\n        .emit(\"foo\", json_payload)\n        .await\n        .expect(\"Server unreachable\");\n\n    // define a callback, that's executed when the ack got acked\n    let ack_callback = |message: Payload, _: Client| {\n        async move {\n            println!(\"Yehaa! My ack got acked?\");\n            println!(\"Ack data: {:#?}\", message);\n        }\n        .boxed()\n    };\n\n    let json_payload = json!({\"myAckData\": 123});\n    // emit with an ack\n    socket\n        .emit_with_ack(\"test\", json_payload, Duration::from_secs(2), ack_callback)\n        .await\n        .expect(\"Server unreachable\");\n\n    socket.disconnect().await.expect(\"Disconnect failed\");\n}\n```\"#\n)]\n#![allow(clippy::rc_buffer)]\n#![warn(clippy::complexity)]\n#![warn(clippy::style)]\n#![warn(clippy::perf)]\n#![warn(clippy::correctness)]\n\n/// Defines client only structs\npub mod client;\n/// Deprecated import since 0.3.0-alpha-2, use Event in the crate root instead.\n/// Defines the events that could be sent or received.\npub mod event;\npub(crate) mod packet;\n/// Deprecated import since 0.3.0-alpha-2, use Event in the crate root instead.\n/// Defines the types of payload (binary or string), that\n/// could be sent or received.\npub mod payload;\npub(self) mod socket;\n\n/// Deprecated import since 0.3.0-alpha-2, use Error in the crate root instead.\n/// Contains the error type which will be returned with every result in this\n/// crate.\npub mod error;\n\n#[cfg(feature = \"async\")]\n/// Asynchronous version of the socket.io client. This module contains the async\n/// [`crate::asynchronous::Client`] as well as a builder\n/// ([`crate::asynchronous::ClientBuilder`]) that allows for configuring a client.\npub mod asynchronous;\n\npub use error::Error;\n\npub use {event::CloseReason, event::Event, payload::Payload};\n\npub use client::{ClientBuilder, RawClient, TransportType};\n\n// TODO: 0.4.0 remove\n#[deprecated(since = \"0.3.0-alpha-2\", note = \"Socket renamed to Client\")]\npub use client::{ClientBuilder as SocketBuilder, RawClient as Socket};\n\n#[cfg(test)]\npub(crate) mod test {\n    use url::Url;\n\n    /// The socket.io server for testing runs on port 4200\n    const SERVER_URL: &str = \"http://localhost:4200\";\n\n    pub(crate) fn socket_io_server() -> Url {\n        let url = std::env::var(\"SOCKET_IO_SERVER\").unwrap_or_else(|_| SERVER_URL.to_owned());\n        let mut url = Url::parse(&url).unwrap();\n\n        if url.path() == \"/\" {\n            url.set_path(\"/socket.io/\");\n        }\n\n        url\n    }\n\n    // The socket.io auth server for testing runs on port 4204\n    const AUTH_SERVER_URL: &str = \"http://localhost:4204\";\n\n    pub(crate) fn socket_io_auth_server() -> Url {\n        let url =\n            std::env::var(\"SOCKET_IO_AUTH_SERVER\").unwrap_or_else(|_| AUTH_SERVER_URL.to_owned());\n        let mut url = Url::parse(&url).unwrap();\n\n        if url.path() == \"/\" {\n            url.set_path(\"/socket.io/\");\n        }\n\n        url\n    }\n\n    // The socket.io restart server for testing runs on port 4205\n    const RESTART_SERVER_URL: &str = \"http://localhost:4205\";\n\n    pub(crate) fn socket_io_restart_server() -> Url {\n        let url = std::env::var(\"SOCKET_IO_RESTART_SERVER\")\n            .unwrap_or_else(|_| RESTART_SERVER_URL.to_owned());\n        let mut url = Url::parse(&url).unwrap();\n\n        if url.path() == \"/\" {\n            url.set_path(\"/socket.io/\");\n        }\n\n        url\n    }\n\n    // The socket.io restart url auth server for testing runs on port 4206\n    const RESTART_URL_AUTH_SERVER_URL: &str = \"http://localhost:4206\";\n\n    pub(crate) fn socket_io_restart_url_auth_server() -> Url {\n        let url = std::env::var(\"SOCKET_IO_RESTART_URL_AUTH_SERVER\")\n            .unwrap_or_else(|_| RESTART_URL_AUTH_SERVER_URL.to_owned());\n        let mut url = Url::parse(&url).unwrap();\n\n        if url.path() == \"/\" {\n            url.set_path(\"/socket.io/\");\n        }\n\n        url\n    }\n}\n"
  },
  {
    "path": "socketio/src/packet.rs",
    "content": "use crate::error::{Error, Result};\nuse crate::{Event, Payload};\nuse bytes::Bytes;\nuse serde::de::IgnoredAny;\n\nuse std::convert::TryFrom;\nuse std::fmt::Write;\nuse std::str::from_utf8 as str_from_utf8;\n\n/// An enumeration of the different `Packet` types in the `socket.io` protocol.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub enum PacketId {\n    Connect = 0,\n    Disconnect = 1,\n    Event = 2,\n    Ack = 3,\n    ConnectError = 4,\n    BinaryEvent = 5,\n    BinaryAck = 6,\n}\n\n/// A packet which gets sent or received during in the `socket.io` protocol.\n#[derive(Debug, PartialEq, Eq, Clone)]\npub struct Packet {\n    pub packet_type: PacketId,\n    pub nsp: String,\n    pub data: Option<String>,\n    pub id: Option<i32>,\n    pub attachment_count: u8,\n    pub attachments: Option<Vec<Bytes>>,\n}\n\nimpl Packet {\n    /// Returns a packet for a payload, could be used for both binary and non binary\n    /// events and acks. Convenience method.\n    #[inline]\n    pub(crate) fn new_from_payload<'a>(\n        payload: Payload,\n        event: Event,\n        nsp: &'a str,\n        id: Option<i32>,\n    ) -> Result<Packet> {\n        match payload {\n            Payload::Binary(bin_data) => Ok(Packet::new(\n                if id.is_some() {\n                    PacketId::BinaryAck\n                } else {\n                    PacketId::BinaryEvent\n                },\n                nsp.to_owned(),\n                Some(serde_json::Value::String(event.into()).to_string()),\n                id,\n                1,\n                Some(vec![bin_data]),\n            )),\n            #[allow(deprecated)]\n            Payload::String(str_data) => {\n                let payload = if serde_json::from_str::<IgnoredAny>(&str_data).is_ok() {\n                    format!(\"[\\\"{event}\\\",{str_data}]\")\n                } else {\n                    format!(\"[\\\"{event}\\\",\\\"{str_data}\\\"]\")\n                };\n\n                Ok(Packet::new(\n                    PacketId::Event,\n                    nsp.to_owned(),\n                    Some(payload),\n                    id,\n                    0,\n                    None,\n                ))\n            }\n            Payload::Text(mut data) => {\n                let mut payload_args = vec![serde_json::Value::String(event.to_string())];\n                payload_args.append(&mut data);\n                drop(data);\n\n                let payload = serde_json::Value::Array(payload_args).to_string();\n\n                Ok(Packet::new(\n                    PacketId::Event,\n                    nsp.to_owned(),\n                    Some(payload),\n                    id,\n                    0,\n                    None,\n                ))\n            }\n        }\n    }\n}\n\nimpl Default for Packet {\n    fn default() -> Self {\n        Self {\n            packet_type: PacketId::Event,\n            nsp: String::from(\"/\"),\n            data: None,\n            id: None,\n            attachment_count: 0,\n            attachments: None,\n        }\n    }\n}\n\nimpl TryFrom<u8> for PacketId {\n    type Error = Error;\n    fn try_from(b: u8) -> Result<Self> {\n        PacketId::try_from(b as char)\n    }\n}\n\nimpl TryFrom<char> for PacketId {\n    type Error = Error;\n    fn try_from(b: char) -> Result<Self> {\n        match b {\n            '0' => Ok(PacketId::Connect),\n            '1' => Ok(PacketId::Disconnect),\n            '2' => Ok(PacketId::Event),\n            '3' => Ok(PacketId::Ack),\n            '4' => Ok(PacketId::ConnectError),\n            '5' => Ok(PacketId::BinaryEvent),\n            '6' => Ok(PacketId::BinaryAck),\n            _ => Err(Error::InvalidPacketId(b)),\n        }\n    }\n}\n\nimpl Packet {\n    /// Creates an instance.\n    pub const fn new(\n        packet_type: PacketId,\n        nsp: String,\n        data: Option<String>,\n        id: Option<i32>,\n        attachment_count: u8,\n        attachments: Option<Vec<Bytes>>,\n    ) -> Self {\n        Packet {\n            packet_type,\n            nsp,\n            data,\n            id,\n            attachment_count,\n            attachments,\n        }\n    }\n}\n\nimpl From<Packet> for Bytes {\n    fn from(packet: Packet) -> Self {\n        Bytes::from(&packet)\n    }\n}\n\nimpl From<&Packet> for Bytes {\n    /// Method for encoding from a `Packet` to a `u8` byte stream.\n    /// The binary payload of a packet is not put at the end of the\n    /// stream as it gets handled and send by it's own logic via the socket.\n    fn from(packet: &Packet) -> Bytes {\n        // first the packet type\n        let mut buffer = String::new();\n        buffer.push((packet.packet_type as u8 + b'0') as char);\n\n        // eventually a number of attachments, followed by '-'\n        if let PacketId::BinaryAck | PacketId::BinaryEvent = packet.packet_type {\n            let _ = write!(buffer, \"{}-\", packet.attachment_count);\n        }\n\n        // if the namespace is different from the default one append it as well,\n        // followed by ','\n        if packet.nsp != \"/\" {\n            buffer.push_str(&packet.nsp);\n            buffer.push(',');\n        }\n\n        // if an id is present append it...\n        if let Some(id) = packet.id {\n            let _ = write!(buffer, \"{id}\");\n        }\n\n        if packet.attachments.is_some() {\n            let num = packet.attachment_count - 1;\n\n            // check if an event type is present\n            if let Some(event_type) = packet.data.as_ref() {\n                let _ = write!(\n                    buffer,\n                    \"[{event_type},{{\\\"_placeholder\\\":true,\\\"num\\\":{num}}}]\",\n                );\n            } else {\n                let _ = write!(buffer, \"[{{\\\"_placeholder\\\":true,\\\"num\\\":{num}}}]\");\n            }\n        } else if let Some(data) = packet.data.as_ref() {\n            buffer.push_str(data);\n        }\n\n        Bytes::from(buffer)\n    }\n}\n\nimpl TryFrom<Bytes> for Packet {\n    type Error = Error;\n    fn try_from(value: Bytes) -> Result<Self> {\n        Packet::try_from(&value)\n    }\n}\n\nimpl TryFrom<&Bytes> for Packet {\n    type Error = Error;\n    /// Decodes a packet given a `Bytes` type.\n    /// The binary payload of a packet is not put at the end of the\n    /// stream as it gets handled and send by it's own logic via the socket.\n    /// Therefore this method does not return the correct value for the\n    /// binary data, instead the socket is responsible for handling\n    /// this member. This is done because the attachment is usually\n    /// send in another packet.\n    fn try_from(payload: &Bytes) -> Result<Packet> {\n        let mut payload = str_from_utf8(&payload).map_err(Error::InvalidUtf8)?;\n        let mut packet = Packet::default();\n\n        // packet_type\n        let id_char = payload.chars().next().ok_or(Error::IncompletePacket())?;\n        packet.packet_type = PacketId::try_from(id_char)?;\n        payload = &payload[id_char.len_utf8()..];\n\n        // attachment_count\n        if let PacketId::BinaryAck | PacketId::BinaryEvent = packet.packet_type {\n            let (prefix, rest) = payload.split_once('-').ok_or(Error::IncompletePacket())?;\n            payload = rest;\n            packet.attachment_count = prefix.parse().map_err(|_| Error::InvalidPacket())?;\n        }\n\n        // namespace\n        if payload.starts_with('/') {\n            let (prefix, rest) = payload.split_once(',').ok_or(Error::IncompletePacket())?;\n            payload = rest;\n            packet.nsp.clear(); // clearing the default\n            packet.nsp.push_str(prefix);\n        }\n\n        // id\n        let Some((non_digit_idx, _)) = payload.char_indices().find(|(_, c)| !c.is_ascii_digit())\n        else {\n            return Ok(packet);\n        };\n\n        if non_digit_idx > 0 {\n            let (prefix, rest) = payload.split_at(non_digit_idx);\n            payload = rest;\n            packet.id = Some(prefix.parse().map_err(|_| Error::InvalidPacket())?);\n        }\n\n        // validate json\n        serde_json::from_str::<IgnoredAny>(payload).map_err(Error::InvalidJson)?;\n\n        match packet.packet_type {\n            PacketId::BinaryAck | PacketId::BinaryEvent => {\n                if payload.starts_with('[') && payload.ends_with(']') {\n                    payload = &payload[1..payload.len() - 1];\n                }\n\n                let mut str = payload.replace(\"{\\\"_placeholder\\\":true,\\\"num\\\":0}\", \"\");\n\n                if str.ends_with(',') {\n                    str.pop();\n                }\n\n                if !str.is_empty() {\n                    packet.data = Some(str);\n                }\n            }\n            _ => packet.data = Some(payload.to_string()),\n        }\n\n        Ok(packet)\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    /// This test suite is taken from the explanation section here:\n    /// https://github.com/socketio/socket.io-protocol\n    fn test_decode() {\n        let payload = Bytes::from_static(b\"0{\\\"token\\\":\\\"123\\\"}\");\n        let packet = Packet::try_from(&payload);\n        assert!(packet.is_ok());\n\n        assert_eq!(\n            Packet::new(\n                PacketId::Connect,\n                \"/\".to_owned(),\n                Some(String::from(\"{\\\"token\\\":\\\"123\\\"}\")),\n                None,\n                0,\n                None,\n            ),\n            packet.unwrap()\n        );\n\n        let utf8_data = \"{\\\"token™\\\":\\\"123\\\"}\".to_owned();\n        let utf8_payload = format!(\"0/admin™,{}\", utf8_data);\n        let payload = Bytes::from(utf8_payload);\n        let packet = Packet::try_from(&payload);\n        assert!(packet.is_ok());\n\n        assert_eq!(\n            Packet::new(\n                PacketId::Connect,\n                \"/admin™\".to_owned(),\n                Some(utf8_data),\n                None,\n                0,\n                None,\n            ),\n            packet.unwrap()\n        );\n\n        let payload = Bytes::from_static(b\"1/admin,\");\n        let packet = Packet::try_from(&payload);\n        assert!(packet.is_ok());\n\n        assert_eq!(\n            Packet::new(\n                PacketId::Disconnect,\n                \"/admin\".to_owned(),\n                None,\n                None,\n                0,\n                None,\n            ),\n            packet.unwrap()\n        );\n\n        let payload = Bytes::from_static(b\"2[\\\"hello\\\",1]\");\n        let packet = Packet::try_from(&payload);\n        assert!(packet.is_ok());\n\n        assert_eq!(\n            Packet::new(\n                PacketId::Event,\n                \"/\".to_owned(),\n                Some(String::from(\"[\\\"hello\\\",1]\")),\n                None,\n                0,\n                None,\n            ),\n            packet.unwrap()\n        );\n\n        let payload = Bytes::from_static(b\"2/admin,456[\\\"project:delete\\\",123]\");\n        let packet = Packet::try_from(&payload);\n        assert!(packet.is_ok());\n\n        assert_eq!(\n            Packet::new(\n                PacketId::Event,\n                \"/admin\".to_owned(),\n                Some(String::from(\"[\\\"project:delete\\\",123]\")),\n                Some(456),\n                0,\n                None,\n            ),\n            packet.unwrap()\n        );\n\n        let payload = Bytes::from_static(b\"3/admin,456[]\");\n        let packet = Packet::try_from(&payload);\n        assert!(packet.is_ok());\n\n        assert_eq!(\n            Packet::new(\n                PacketId::Ack,\n                \"/admin\".to_owned(),\n                Some(String::from(\"[]\")),\n                Some(456),\n                0,\n                None,\n            ),\n            packet.unwrap()\n        );\n\n        let payload = Bytes::from_static(b\"4/admin,{\\\"message\\\":\\\"Not authorized\\\"}\");\n        let packet = Packet::try_from(&payload);\n        assert!(packet.is_ok());\n\n        assert_eq!(\n            Packet::new(\n                PacketId::ConnectError,\n                \"/admin\".to_owned(),\n                Some(String::from(\"{\\\"message\\\":\\\"Not authorized\\\"}\")),\n                None,\n                0,\n                None,\n            ),\n            packet.unwrap()\n        );\n\n        let payload = Bytes::from_static(b\"51-[\\\"hello\\\",{\\\"_placeholder\\\":true,\\\"num\\\":0}]\");\n        let packet = Packet::try_from(&payload);\n        assert!(packet.is_ok());\n\n        assert_eq!(\n            Packet::new(\n                PacketId::BinaryEvent,\n                \"/\".to_owned(),\n                Some(String::from(\"\\\"hello\\\"\")),\n                None,\n                1,\n                None,\n            ),\n            packet.unwrap()\n        );\n\n        let payload = Bytes::from_static(\n            b\"51-/admin,456[\\\"project:delete\\\",{\\\"_placeholder\\\":true,\\\"num\\\":0}]\",\n        );\n        let packet = Packet::try_from(&payload);\n        assert!(packet.is_ok());\n\n        assert_eq!(\n            Packet::new(\n                PacketId::BinaryEvent,\n                \"/admin\".to_owned(),\n                Some(String::from(\"\\\"project:delete\\\"\")),\n                Some(456),\n                1,\n                None,\n            ),\n            packet.unwrap()\n        );\n\n        let payload = Bytes::from_static(b\"61-/admin,456[{\\\"_placeholder\\\":true,\\\"num\\\":0}]\");\n        let packet = Packet::try_from(&payload);\n        assert!(packet.is_ok());\n\n        assert_eq!(\n            Packet::new(\n                PacketId::BinaryAck,\n                \"/admin\".to_owned(),\n                None,\n                Some(456),\n                1,\n                None,\n            ),\n            packet.unwrap()\n        );\n    }\n\n    #[test]\n    /// This test suites is taken from the explanation section here:\n    /// https://github.com/socketio/socket.io-protocol\n    fn test_encode() {\n        let packet = Packet::new(\n            PacketId::Connect,\n            \"/\".to_owned(),\n            Some(String::from(\"{\\\"token\\\":\\\"123\\\"}\")),\n            None,\n            0,\n            None,\n        );\n\n        assert_eq!(\n            Bytes::from(&packet),\n            \"0{\\\"token\\\":\\\"123\\\"}\".to_string().into_bytes()\n        );\n\n        let packet = Packet::new(\n            PacketId::Connect,\n            \"/admin\".to_owned(),\n            Some(String::from(\"{\\\"token\\\":\\\"123\\\"}\")),\n            None,\n            0,\n            None,\n        );\n\n        assert_eq!(\n            Bytes::from(&packet),\n            \"0/admin,{\\\"token\\\":\\\"123\\\"}\".to_string().into_bytes()\n        );\n\n        let packet = Packet::new(\n            PacketId::Disconnect,\n            \"/admin\".to_owned(),\n            None,\n            None,\n            0,\n            None,\n        );\n\n        assert_eq!(Bytes::from(&packet), \"1/admin,\".to_string().into_bytes());\n\n        let packet = Packet::new(\n            PacketId::Event,\n            \"/\".to_owned(),\n            Some(String::from(\"[\\\"hello\\\",1]\")),\n            None,\n            0,\n            None,\n        );\n\n        assert_eq!(\n            Bytes::from(&packet),\n            \"2[\\\"hello\\\",1]\".to_string().into_bytes()\n        );\n\n        let packet = Packet::new(\n            PacketId::Event,\n            \"/admin\".to_owned(),\n            Some(String::from(\"[\\\"project:delete\\\",123]\")),\n            Some(456),\n            0,\n            None,\n        );\n\n        assert_eq!(\n            Bytes::from(&packet),\n            \"2/admin,456[\\\"project:delete\\\",123]\"\n                .to_string()\n                .into_bytes()\n        );\n\n        let packet = Packet::new(\n            PacketId::Ack,\n            \"/admin\".to_owned(),\n            Some(String::from(\"[]\")),\n            Some(456),\n            0,\n            None,\n        );\n\n        assert_eq!(\n            Bytes::from(&packet),\n            \"3/admin,456[]\".to_string().into_bytes()\n        );\n\n        let packet = Packet::new(\n            PacketId::ConnectError,\n            \"/admin\".to_owned(),\n            Some(String::from(\"{\\\"message\\\":\\\"Not authorized\\\"}\")),\n            None,\n            0,\n            None,\n        );\n\n        assert_eq!(\n            Bytes::from(&packet),\n            \"4/admin,{\\\"message\\\":\\\"Not authorized\\\"}\"\n                .to_string()\n                .into_bytes()\n        );\n\n        let packet = Packet::new(\n            PacketId::BinaryEvent,\n            \"/\".to_owned(),\n            Some(String::from(\"\\\"hello\\\"\")),\n            None,\n            1,\n            Some(vec![Bytes::from_static(&[1, 2, 3])]),\n        );\n\n        assert_eq!(\n            Bytes::from(&packet),\n            \"51-[\\\"hello\\\",{\\\"_placeholder\\\":true,\\\"num\\\":0}]\"\n                .to_string()\n                .into_bytes()\n        );\n\n        let packet = Packet::new(\n            PacketId::BinaryEvent,\n            \"/admin\".to_owned(),\n            Some(String::from(\"\\\"project:delete\\\"\")),\n            Some(456),\n            1,\n            Some(vec![Bytes::from_static(&[1, 2, 3])]),\n        );\n\n        assert_eq!(\n            Bytes::from(&packet),\n            \"51-/admin,456[\\\"project:delete\\\",{\\\"_placeholder\\\":true,\\\"num\\\":0}]\"\n                .to_string()\n                .into_bytes()\n        );\n\n        let packet = Packet::new(\n            PacketId::BinaryAck,\n            \"/admin\".to_owned(),\n            None,\n            Some(456),\n            1,\n            Some(vec![Bytes::from_static(&[3, 2, 1])]),\n        );\n\n        assert_eq!(\n            Bytes::from(&packet),\n            \"61-/admin,456[{\\\"_placeholder\\\":true,\\\"num\\\":0}]\"\n                .to_string()\n                .into_bytes()\n        );\n    }\n\n    #[test]\n    fn test_illegal_packet_id() {\n        let _sut = PacketId::try_from(42).expect_err(\"error!\");\n        assert!(matches!(Error::InvalidPacketId(42 as char), _sut))\n    }\n\n    #[test]\n    fn new_from_payload_binary() {\n        let payload = Payload::Binary(Bytes::from_static(&[0, 4, 9]));\n        let result =\n            Packet::new_from_payload(payload.clone(), \"test_event\".into(), \"namespace\", None)\n                .unwrap();\n        assert_eq!(\n            result,\n            Packet {\n                packet_type: PacketId::BinaryEvent,\n                nsp: \"namespace\".to_owned(),\n                data: Some(\"\\\"test_event\\\"\".to_owned()),\n                id: None,\n                attachment_count: 1,\n                attachments: Some(vec![Bytes::from_static(&[0, 4, 9])])\n            }\n        )\n    }\n\n    #[test]\n    #[allow(deprecated)]\n    fn new_from_payload_string() {\n        let payload = Payload::String(\"test\".to_owned());\n        let result = Packet::new_from_payload(\n            payload.clone(),\n            \"other_event\".into(),\n            \"other_namespace\",\n            Some(10),\n        )\n        .unwrap();\n        assert_eq!(\n            result,\n            Packet {\n                packet_type: PacketId::Event,\n                nsp: \"other_namespace\".to_owned(),\n                data: Some(\"[\\\"other_event\\\",\\\"test\\\"]\".to_owned()),\n                id: Some(10),\n                attachment_count: 0,\n                attachments: None\n            }\n        )\n    }\n\n    #[test]\n    fn new_from_payload_json() {\n        let payload = Payload::Text(vec![\n            serde_json::json!(\"String test\"),\n            serde_json::json!({\"type\":\"object\"}),\n        ]);\n        let result =\n            Packet::new_from_payload(payload.clone(), \"third_event\".into(), \"/\", Some(10)).unwrap();\n        assert_eq!(\n            result,\n            Packet {\n                packet_type: PacketId::Event,\n                nsp: \"/\".to_owned(),\n                data: Some(\"[\\\"third_event\\\",\\\"String test\\\",{\\\"type\\\":\\\"object\\\"}]\".to_owned()),\n                id: Some(10),\n                attachment_count: 0,\n                attachments: None\n            }\n        )\n    }\n}\n"
  },
  {
    "path": "socketio/src/payload.rs",
    "content": "use bytes::Bytes;\n\n/// A type which represents a `payload` in the `socket.io` context.\n/// A payload could either be of the type `Payload::Binary`, which holds\n/// data in the [`Bytes`] type that represents the payload or of the type\n/// `Payload::String` which holds a [`std::string::String`]. The enum is\n/// used for both representing data that's send and data that's received.\n#[derive(Debug, PartialEq, Eq, Clone)]\npub enum Payload {\n    Binary(Bytes),\n    Text(Vec<serde_json::Value>),\n    #[deprecated = \"Use `Payload::Text` instead. Continue existing behavior with: Payload::from(String)\"]\n    /// String that is sent as JSON if this is a JSON string, or as a raw string if it isn't\n    String(String),\n}\n\nimpl Payload {\n    pub(crate) fn string_to_value(string: String) -> serde_json::Value {\n        if let Ok(value) = serde_json::from_str::<serde_json::Value>(&string) {\n            value\n        } else {\n            serde_json::Value::String(string)\n        }\n    }\n}\n\nimpl From<&str> for Payload {\n    fn from(string: &str) -> Self {\n        Payload::from(string.to_owned())\n    }\n}\n\nimpl From<String> for Payload {\n    fn from(string: String) -> Self {\n        Self::Text(vec![Payload::string_to_value(string)])\n    }\n}\n\nimpl From<Vec<String>> for Payload {\n    fn from(arr: Vec<String>) -> Self {\n        Self::Text(arr.into_iter().map(Payload::string_to_value).collect())\n    }\n}\n\nimpl From<Vec<serde_json::Value>> for Payload {\n    fn from(values: Vec<serde_json::Value>) -> Self {\n        Self::Text(values)\n    }\n}\n\nimpl From<serde_json::Value> for Payload {\n    fn from(value: serde_json::Value) -> Self {\n        Self::Text(vec![value])\n    }\n}\n\nimpl From<Vec<u8>> for Payload {\n    fn from(val: Vec<u8>) -> Self {\n        Self::Binary(Bytes::from(val))\n    }\n}\n\nimpl From<&'static [u8]> for Payload {\n    fn from(val: &'static [u8]) -> Self {\n        Self::Binary(Bytes::from_static(val))\n    }\n}\n\nimpl From<Bytes> for Payload {\n    fn from(bytes: Bytes) -> Self {\n        Self::Binary(bytes)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use serde_json::json;\n\n    use super::*;\n\n    #[test]\n    fn test_from_string() {\n        let sut = Payload::from(\"foo ™\");\n\n        assert_eq!(\n            Payload::Text(vec![serde_json::Value::String(String::from(\"foo ™\"))]),\n            sut\n        );\n\n        let sut = Payload::from(String::from(\"foo ™\"));\n        assert_eq!(\n            Payload::Text(vec![serde_json::Value::String(String::from(\"foo ™\"))]),\n            sut\n        );\n\n        let sut = Payload::from(json!(\"foo ™\"));\n        assert_eq!(\n            Payload::Text(vec![serde_json::Value::String(String::from(\"foo ™\"))]),\n            sut\n        );\n    }\n\n    #[test]\n    fn test_from_multiple_strings() {\n        let input = vec![\n            \"one\".to_owned(),\n            \"two\".to_owned(),\n            json!([\"foo\", \"bar\"]).to_string(),\n        ];\n\n        assert_eq!(\n            Payload::Text(vec![\n                serde_json::Value::String(String::from(\"one\")),\n                serde_json::Value::String(String::from(\"two\")),\n                json!([\"foo\", \"bar\"])\n            ]),\n            Payload::from(input)\n        );\n    }\n\n    #[test]\n    fn test_from_multiple_json() {\n        let input = vec![json!({\"foo\": \"bar\"}), json!(\"foo\"), json!([\"foo\", \"bar\"])];\n\n        assert_eq!(Payload::Text(input.clone()), Payload::from(input.clone()));\n    }\n\n    #[test]\n    fn test_from_json() {\n        let json = json!({\n            \"foo\": \"bar\"\n        });\n        let sut = Payload::from(json.clone());\n\n        assert_eq!(Payload::Text(vec![json.clone()]), sut);\n\n        // From JSON encoded string\n        let sut = Payload::from(json.to_string());\n\n        assert_eq!(Payload::Text(vec![json]), sut);\n    }\n\n    #[test]\n    fn test_from_binary() {\n        let sut = Payload::from(vec![1, 2, 3]);\n        assert_eq!(Payload::Binary(Bytes::from_static(&[1, 2, 3])), sut);\n\n        let sut = Payload::from(&[1_u8, 2_u8, 3_u8][..]);\n        assert_eq!(Payload::Binary(Bytes::from_static(&[1, 2, 3])), sut);\n\n        let sut = Payload::from(Bytes::from_static(&[1, 2, 3]));\n        assert_eq!(Payload::Binary(Bytes::from_static(&[1, 2, 3])), sut);\n    }\n}\n"
  },
  {
    "path": "socketio/src/socket.rs",
    "content": "use crate::error::{Error, Result};\nuse crate::packet::{Packet, PacketId};\nuse bytes::Bytes;\nuse rust_engineio::{Client as EngineClient, Packet as EnginePacket, PacketId as EnginePacketId};\nuse std::convert::TryFrom;\nuse std::sync::{atomic::AtomicBool, Arc};\nuse std::{fmt::Debug, sync::atomic::Ordering};\n\nuse super::{event::Event, payload::Payload};\n\n/// Handles communication in the `socket.io` protocol.\n#[derive(Clone, Debug)]\npub(crate) struct Socket {\n    //TODO: 0.4.0 refactor this\n    engine_client: Arc<EngineClient>,\n    connected: Arc<AtomicBool>,\n}\n\nimpl Socket {\n    /// Creates an instance of `Socket`.\n\n    pub(super) fn new(engine_client: EngineClient) -> Result<Self> {\n        Ok(Socket {\n            engine_client: Arc::new(engine_client),\n            connected: Arc::new(AtomicBool::default()),\n        })\n    }\n\n    /// Connects to the server. This includes a connection of the underlying\n    /// engine.io client and afterwards an opening socket.io request.\n    pub fn connect(&self) -> Result<()> {\n        self.engine_client.connect()?;\n\n        // store the connected value as true, if the connection process fails\n        // later, the value will be updated\n        self.connected.store(true, Ordering::Release);\n\n        Ok(())\n    }\n\n    /// Disconnects from the server by sending a socket.io `Disconnect` packet. This results\n    /// in the underlying engine.io transport to get closed as well.\n    pub fn disconnect(&self) -> Result<()> {\n        if self.is_engineio_connected()? {\n            self.engine_client.disconnect()?;\n        }\n        if self.connected.load(Ordering::Acquire) {\n            self.connected.store(false, Ordering::Release);\n        }\n        Ok(())\n    }\n\n    /// Sends a `socket.io` packet to the server using the `engine.io` client.\n    pub fn send(&self, packet: Packet) -> Result<()> {\n        if !self.is_engineio_connected()? || !self.connected.load(Ordering::Acquire) {\n            return Err(Error::IllegalActionBeforeOpen());\n        }\n\n        // the packet, encoded as an engine.io message packet\n        let engine_packet = EnginePacket::new(EnginePacketId::Message, Bytes::from(&packet));\n        self.engine_client.emit(engine_packet)?;\n\n        if let Some(attachments) = packet.attachments {\n            for attachment in attachments {\n                let engine_packet = EnginePacket::new(EnginePacketId::MessageBinary, attachment);\n                self.engine_client.emit(engine_packet)?;\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Emits to certain event with given data. The data needs to be JSON,\n    /// otherwise this returns an `InvalidJson` error.\n    pub fn emit(&self, nsp: &str, event: Event, data: Payload) -> Result<()> {\n        let socket_packet = Packet::new_from_payload(data, event, nsp, None)?;\n\n        self.send(socket_packet)\n    }\n\n    pub(crate) fn poll(&self) -> Result<Option<Packet>> {\n        loop {\n            match self.engine_client.poll() {\n                Ok(Some(packet)) => {\n                    if packet.packet_id == EnginePacketId::Message\n                        || packet.packet_id == EnginePacketId::MessageBinary\n                    {\n                        let packet = self.handle_engineio_packet(packet)?;\n                        self.handle_socketio_packet(&packet);\n                        return Ok(Some(packet));\n                    } else {\n                        continue;\n                    }\n                }\n                Ok(None) => {\n                    return Ok(None);\n                }\n                Err(err) => return Err(err.into()),\n            }\n        }\n    }\n\n    /// Handles the connection/disconnection.\n    #[inline]\n    fn handle_socketio_packet(&self, socket_packet: &Packet) {\n        match socket_packet.packet_type {\n            PacketId::Connect => {\n                self.connected.store(true, Ordering::Release);\n            }\n            PacketId::ConnectError => {\n                self.connected.store(false, Ordering::Release);\n            }\n            PacketId::Disconnect => {\n                self.connected.store(false, Ordering::Release);\n            }\n            _ => (),\n        }\n    }\n\n    /// Handles new incoming engineio packets\n    fn handle_engineio_packet(&self, packet: EnginePacket) -> Result<Packet> {\n        let mut socket_packet = Packet::try_from(&packet.data)?;\n\n        // Only handle attachments if there are any\n        if socket_packet.attachment_count > 0 {\n            let mut attachments_left = socket_packet.attachment_count;\n            let mut attachments = Vec::new();\n            while attachments_left > 0 {\n                let next = self.engine_client.poll();\n                match next {\n                    Err(err) => return Err(err.into()),\n                    Ok(Some(packet)) => match packet.packet_id {\n                        EnginePacketId::MessageBinary | EnginePacketId::Message => {\n                            attachments.push(packet.data);\n                            attachments_left -= 1;\n                        }\n                        _ => {\n                            return Err(Error::InvalidAttachmentPacketType(\n                                packet.packet_id.into(),\n                            ));\n                        }\n                    },\n                    Ok(None) => {\n                        // Engineio closed before attachments completed.\n                        return Err(Error::IncompletePacket());\n                    }\n                }\n            }\n            socket_packet.attachments = Some(attachments);\n        }\n\n        Ok(socket_packet)\n    }\n\n    fn is_engineio_connected(&self) -> Result<bool> {\n        Ok(self.engine_client.is_connected()?)\n    }\n}\n"
  }
]