[
  {
    "path": ".dockerignore",
    "content": "target\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# see https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file\n\nversion: 2\nupdates:\n\n  # Maintain dependencies for GitHub Actions\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n      day: friday\n\n  # Maintain dependencies for Bundler\n  - package-ecosystem: \"cargo\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n      day: wednesday\n"
  },
  {
    "path": ".github/workflows/ci.yaml",
    "content": "name: CI\n\non:\n  push:\n    branches: [ \"master\" ]\n  pull_request:\n    branches: [ \"master\" ]\n\nenv:\n  CARGO_TERM_COLOR: always\n\njobs:\n  clippy:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v4\n    - uses: dtolnay/rust-toolchain@stable\n    - name: Run clippy\n      run: cargo clippy\n\n  build:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v4\n    - uses: dtolnay/rust-toolchain@stable\n    - name: Build\n      run: cargo build --verbose\n    - name: Run tests\n      run: cargo test --verbose\n"
  },
  {
    "path": ".github/workflows/container-image-buildah.yml",
    "content": "name: Container Image\n\non:\n  workflow_dispatch:\n    inputs:\n      platforms:\n        description: \"comma-separated list of platforms to build for, e.g. linux/amd64,linux/arm64,linux/s390x,linux/ppc64le,linux/riscv64 (leave empty for defaults)\"\n        default: ''\n      custom_tag:\n        description: optional custom tag on remote repo you want image to be tagged with\n        default: scratch\n  workflow_call:\n    inputs:\n      platforms:\n        required: false\n        default: ''\n        type: string\n      custom_tag:\n        required: false\n        default: ''\n        type: string\n  #schedule:\n  #  # every Wednesday morning\n  #  - cron: 7 7 * * 3\n  push:\n    branches: [ master ]\n    tags:\n      - '*'           # Push events to every tag not containing /\n  #pull_request:\n  #  types: [opened, reopened, synchronize]\n\nconcurrency:\n  group: ci-container-build-${{ github.ref }}-1\n  cancel-in-progress: true\n\nenv:\n  # Use docker.io for Docker Hub if empty\n  REGISTRY: ghcr.io\n  # github.repository as <account>/<repo>\n  IMAGE_NAME: ${{ github.repository }}\n\n# Sets permissions of the GITHUB_TOKEN to allow deployment to ghcr.io\npermissions:\n  contents: read\n  packages: write\n  id-token: write\n\njobs:\n  buildah:\n    strategy:\n      matrix:\n        include:\n          - architecture: amd64\n            runner: ubuntu-latest\n          - architecture: arm64\n            runner: ubuntu-24.04-arm\n    runs-on: ${{ matrix.runner }}\n    steps:\n      - name: Sanitize Platforms\n        id: platforms\n        run: |\n          platforms=\"${{ inputs.platforms == '' && 'linux/amd64,linux/arm64' || inputs.platforms }}\"\n          if [ \"${{ matrix.architecture }}\" = \"arm64\" ]; then\n            platforms=\"linux/arm64\"\n          else\n            platforms=\"$( sed -e 's#linux/arm64,##g' -e 's#,linux/arm64##g' -e 's#linux/arm64##g' <<< $platforms)\"\n            archs=\"$( sed -e 's#linux/##g' <<< $platforms )\"\n            echo \"archs=$archs\" >> $GITHUB_OUTPUT\n          fi\n          echo \"platforms=$platforms\" >> $GITHUB_OUTPUT\n\n      - name: Install Podman on ubuntu-24.04-arm\n        if: matrix.architecture == 'arm64'\n        run: |\n          sudo apt-get update\n          sudo apt-get install -y podman\n          echo -e \"[registries.search]\\nregistries = ['docker.io']\" | sudo tee /etc/containers/registries.conf\n\n      # Allow multi-target builds\n      - name: Set up QEMU\n        if: matrix.architecture == 'amd64'\n        uses: docker/setup-qemu-action@v3\n        with:\n          platforms: ${{ steps.platforms.outputs.archs }}\n      # Login against a Docker registry except on PR\n      # https://github.com/docker/login-action\n      - name: Log into registry ${{ env.REGISTRY }}\n        if: github.event_name != 'pull_request'\n        uses: redhat-actions/podman-login@v1\n        with:\n          registry: ${{ env.REGISTRY }}\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n      # Extract metadata (tags, labels) for Docker\n      # https://github.com/docker/metadata-action\n      - name: Docker meta\n        id: meta\n        uses: docker/metadata-action@v5\n        with:\n          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-${{ matrix.architecture }}\n          tags: |\n            type=schedule\n            type=raw,value=latest,enable=${{ github.ref_name == 'master' }}\n            ${{ github.ref_name == 'master' && 'type=raw,value=nightly' }}\n            type=ref,event=branch,enable=${{ github.ref_name != 'master' && inputs.custom_tag == '' }}\n            ${{ inputs.custom_tag }}\n            type=ref,event=tag\n            type=ref,event=pr\n\n      # https://github.com/actions/checkout\n      - uses: actions/checkout@v4\n\n      - name: Build image\n        id: build-image\n        uses: redhat-actions/buildah-build@v2\n        with:\n          tags: ${{ steps.meta.outputs.tags }}\n          platforms: ${{ steps.platforms.outputs.platforms }}\n          labels: ${{ steps.meta.outputs.labels }}\n          layers: false\n          oci: true\n          tls-verify: true\n          extra-args: |\n            --squash\n            --jobs=3\n          containerfiles: |\n            Dockerfile\n\n      - name: Echo Outputs\n        run: |\n          echo \"Image: ${{ steps.build-image.outputs.image }}\"\n          echo \"Tags: ${{ steps.build-image.outputs.tags }}\"\n          echo \"Tagged Image: ${{ steps.build-image.outputs.image-with-tag }}\"\n\n      - name: Check images created\n        run: buildah images\n\n      - name: Smoke test the images\n        run: |\n          set -ex\n          # accessing a mapped port from a container did not work so lets\n          # create a pod where both - server and client have same localhost\n          podman pod create > podid\n          platforms=\"${{ steps.platforms.outputs.platforms }}\"\n\n          test_tag () {\n            podman run -d --pod-id-file=podid --name=listener -u 14:0 \"${{ steps.build-image.outputs.image-with-tag }}$1\" -s 0.0.0.0:1234\n            sleep 3\n            podman logs listener\n            podman run --pod-id-file=podid --rm -i \"${{ steps.build-image.outputs.image-with-tag }}$1\" ws://127.0.0.1:1234/ <<< \"Test Message $1\"\n            echo Expecting \"\\\"Test Message $1\\\"\" in listener log..\n            podman logs listener | tee /dev/stderr | grep -q \"Test Message $1\"\n            podman rm -f listener\n          }\n\n          if [ x$( sed -E -e 's#[^/]##g' <<< $platforms ) != \"x/\" ]; then\n            # if we are here, user has selected more than one build platform\n            arch_tags=$( tr ',' ' ' <<< $platforms | tr -d '/' )\n            # removed slashes to produce \"linuxamd64 linuxs390x linuxppc64le\"\n            for tag in $arch_tags; do test_tag -$tag; done\n          else\n            # if we are here, user has selected a single build platform\n            test_tag\n          fi\n\n      - name: Push To Container Registry\n        id: push-to-container-registry\n        uses: redhat-actions/push-to-registry@v2\n        if: github.event_name != 'pull_request'\n        with:\n          tags: ${{ steps.build-image.outputs.tags }}\n\n      - name: Print image url\n        run: echo \"Image pushed to ${{ steps.push-to-container-registry.outputs.registry-paths }}\"\n\n  merge-archs:\n    runs-on: ubuntu-latest\n    needs: buildah\n    steps:\n      - name: Log into registry ${{ env.REGISTRY }}\n        if: github.event_name != 'pull_request'\n        uses: redhat-actions/podman-login@v1\n        with:\n          registry: ${{ env.REGISTRY }}\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Docker meta\n        id: meta\n        uses: docker/metadata-action@v5\n        with:\n          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}\n          tags: |\n            type=schedule\n            type=raw,value=latest,enable=${{ github.ref_name == 'master' }}\n            ${{ github.ref_name == 'master' && 'type=raw,value=nightly' }}\n            type=ref,event=branch,enable=${{ github.ref_name != 'master' && inputs.custom_tag == '' }}\n            ${{ inputs.custom_tag }}\n            type=ref,event=tag\n            type=ref,event=pr\n\n      - name: Create and push manifest\n        run: |\n          set -x\n\n          img=\"${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}\"\n          podman manifest create $img\n\n          archs=\"amd64 arm64\"\n          for arch in $archs; do\n            podman manifest add $img \"${img}-${arch}:latest\"\n          done\n\n          tags=\"${{ steps.meta.outputs.tags }}\"\n          for tag in $tags; do\n            podman tag $img $tag\n            podman manifest push --all $tag\n          done\n"
  },
  {
    "path": ".gitignore",
    "content": "target\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "<a name=\"v1.14.0\"></a>\n# [Sunset (v1.14.0)](https://github.com/vi/websocat/releases/tag/v1.14.0) - 12 Nov 2024\n\n* Attempt to priorize ping requests and replies over normal traffic\n* More options to supply password for basic auth: `--basic-auth-file` and `WEBSOCAT_BASIC_AUTH`\n* `drop_on_backpressure:` overlay\n* `--ua` shortcut for `-H User-Agent:`\n* SOCKS5 authentication\n\n<a name=\"v1.13.0\"></a>\n# [Happy eyeballs (v1.13.0)](https://github.com/vi/websocat/releases/tag/v1.13.0) - 31 Mar 2024\n\n* `waitfordata:` overlay to delay connection initiation until first data is attempted to be written to it\n* Dockerfile updates\n* `lengthprefixed:` overlay - alternative to base64 mode\n* Fix for #23 - \"happy eyeballs\" for ws:// and wss:// URLs.\n\n<a name=\"v1.12.0\"></a>\n# [Maintainance release (v1.12.0)](https://github.com/vi/websocat/releases/tag/v1.12.0) - 17 Sep 2023\n\n* Option to stop sending or replying to WebSocket pings after specified amount of sent or received pings (for testing idling disconnection behaviour of counterparts).\n* `--exec-exit-on-disconnect`\n* Print `Location:` header value in error message when facing a redirect instead of a WebSocket connection.\n* Other minor fixes\n\n[Changes][v1.12.0]\n\n\n<a name=\"v1.11.0\"></a>\n# [Still keeping v1 afloat instead of concentrating on v3 (v1.11.0)](https://github.com/vi/websocat/releases/tag/v1.11.0) - 24 Sep 2022\n\n* `--preamble` (`-p`) options to prepend static text to Websocat sessions. For use to authenticate and subscribe to something over WebSocket. Note that specifying passwords on command line may be insecure. Also command line handling around `-p` is finicky. There is also `--preamble-reverse` (`-P`) option to prepend similar chunk in the reverse direction.\n* `--compress-{zlib,deflate,gzip}` and respective `--uncompress-...` options to modify binary WebSocket messages going to/from a WebSocket. Note that it is not related to [permessage-deflate](https://www.rfc-editor.org/rfc/rfc7692.html), which does similar thing, but on lower level.\n* `exit_on_specific_byte:` overlay to trigger exit when specific byte is encountered. For interactive tty usage.\n* `--client-pkcs12-der` to specify client identity certificate for connecting to `wss://` or `ssl:` that requires mutual authentication.\n* `openssl-probe` is now active by default on Linux, to support for overriding CA lists using environment variables.\n* Incoming WebSocket frames and message are now limited by default, to prevent memory stuffing denial of service. But the default limit is big (100 megabytes). Use `--max-ws-frame-length` and `--max-ws-message-length` options to override.\n* `Cargo.lock` is now oriented for building with modern Rust compiler. There is `Cargo.lock.legacy` with dependencies manually locked to versions that support Rust 1.46.0.\n\n[Changes][v1.11.0]\n\n<a name=\"v1.10.0\"></a>\n# [Some fixes, some features. (v1.10.0)](https://github.com/vi/websocat/releases/tag/v1.10.0) - 17 May 2022\n\n* Add `--close-status-code` and ` --close-reason`\n* Fix `--queue-len` option that took no effect\n* Fix racing to connect to multiple resolved addresses in `tcp:` specifier (i.e. \"happy eyeballs\") - now it skips errors if there is a working connection. This does not fix `ws://localhost` unfortunately.\n* `crypto:` overlay and associated options\n* `prometheus:` overlay and associated options\n* `random:` specifier\n\n[Changes][v1.10.0]\n\n<a name=\"v1.9.0\"></a>\n# [Supposedly without yanked crates (v1.9.0)](https://github.com/vi/websocat/releases/tag/v1.9.0) - 30 Oct 2021\n\n* `ssl` Cargo feature is now enabled by default\n* `vendored_openssl` Cargo feature is now not enabled by default\n* `--stdout-announce-listening-ports` option to print message when server port is ready to accept clients.\n* `--no-close` option now also affects Websocket server mode, not just client\n* `timestamp:` overlay to mangle message, prepending current timestamp as text\n* `--print-ping-rtts` option\n* Updated deps for [#138](https://github.com/vi/websocat/issues/138) (not checked whether all yanks are resolved although).\n\n[Changes][v1.9.0]\n\n<a name=\"v1.8.0\"></a>\n# [Fix some bugs (v1.8.0)](https://github.com/vi/websocat/releases/tag/v1.8.0) - 15 Apr 2021\n\n* `--accept-from-fd` option for better systemd intergration\n* `exec:`/`cmd:`/`sh-c:` specifiers now don't terminate process prematurely\n* `--foreachmsg-wait-read` for better `foreachmsg:` overlay behaviour. Now `foreachmsg:exec:./myscript` is more meaningul.\n* ` --basic-auth` option to insert basic authentication header more easily\n* Websocket close message is now logged in debug mode\n\n[Changes][v1.8.0]\n\n<a name=\"v1.7.0\"></a>\n# [Default threaded stdio, `log:` filter (v1.7.0)](https://github.com/vi/websocat/releases/tag/v1.7.0) - 22 Feb 2021\n\n* Websocat now does not set terminal to nonblocking mode if isatty by default. This should help with [#76](https://github.com/vi/websocat/issues/76).\n* New overlay `log:` that prints bytes as they travel though Websocat, for debugging.\n\n[Changes][v1.7.0]\n\n<a name=\"v1.6.0\"></a>\n# [A heartbeat release (v1.6.0)](https://github.com/vi/websocat/releases/tag/v1.6.0) - 08 Jul 2020\n\n* UDP multicast options\n* `foreachmsg:` overlay - run specifier (i.e. connect somewhere or execute a program) on each WebSocket message instead of on each WebSocket connection.\n* Various minor options like `--max-messages` or zero-length message handling.\n* Low-level Websocket features: `--just-generate-key` and `--just-generate-accept` options which help generating HTTP headers for WebSockets. `ws-lowlevel-server:` and `ws-lowlevel-client:` overlays to use expose WebSocket's data encoder/decoder without HTTP part.\n* Basic `http://` client with arbitrary method, uri and so on.\n* Delay for `autoreconnect:` overlay\n* More pre-built release assets\n* Base64 mode for binary WebSocket messages\n* Prefixes for text and binary WebSocket messages, allowing to discriminate incoming binary and text WebSocket messages and intermix outgoing binary and text WebSocket messages.\n* Sort-of-unfinished `http-post-sse:` specifier allowing to use HTTP server-sent events (in one direction) and POST request bodies (in the other direction) instead of (or in addition to) a WebSocket and to bridge them together. This mode is not tested properly although.\n\n[Changes][v1.6.0]\n\n<a name=\"v1.5.0\"></a>\n# [Client basic auth, header-to-env (v1.5.0)](https://github.com/vi/websocat/releases/tag/v1.5.0) - 18 Aug 2019\n\n* Using client URI's like `websocat ws://user:password@host/` now adds basic authentication HTTP header to request\n* New command-line option: `--header-to-env`\n* Minor dependencies update\n* Built with newer Rust on newer Debian\n\n[Changes][v1.5.0]\n\n<a name=\"v1.4.0\"></a>\n# [WebSocket ping and Sec-WebSocket-Protocol improvements (v1.4.0)](https://github.com/vi/websocat/releases/tag/v1.4.0) - 21 Mar 2019\n\n* New options: `--server-protocol`, `--ping-timeout`, `--ping-interval`, `--server-header`\n* Fixed replying to WebSocket pings\n* Fixed replying to requests with `Sec-WebSocket-Protocol`.\n\n[Changes][v1.4.0]\n\n<a name=\"v1.3.0\"></a>\n# [tokio, conncap, pkcs12-passwd, typos (v1.3.0)](https://github.com/vi/websocat/releases/tag/v1.3.0) - 06 Mar 2019\n\n[Changes][v1.3.0]\n\n<a name=\"v1.2.0\"></a>\n# [-k (--insecure), native-tls (v1.2.0)](https://github.com/vi/websocat/releases/tag/v1.2.0) - 01 Nov 2018\n\n[Changes][v1.2.0]\n\n<a name=\"v1.1.0\"></a>\n# [More features (v1.1.0)](https://github.com/vi/websocat/releases/tag/v1.1.0) - 30 Aug 2018\n\n* Static files aside from the websocket for easy prototyping\n* SOCKS5 proxy client\n* wss:// listener\n* Setting environment variables for `exec:`\n* Sending SIGHUP signal to child process on client disconnect\n* `--jsonrpc` mode\n\n[Changes][v1.1.0]\n\n<a name=\"v1.1-pre\"></a>\n# [Preview of 1.1 (v1.1-pre)](https://github.com/vi/websocat/releases/tag/v1.1-pre) - 13 Jul 2018\n\n* --set-environment option and --static-file\n\n[Changes][v1.1-pre]\n\n\n<a name=\"v1.0.0\"></a>\n# [The release. Finally. (v1.0.0)](https://github.com/vi/websocat/releases/tag/v1.0.0) - 04 Jul 2018\n\n[Changes][v1.0.0]\n\n\n<a name=\"v1.0.0-beta\"></a>\n# [Refactor and more features (v1.0.0-beta)](https://github.com/vi/websocat/releases/tag/v1.0.0-beta) - 20 Jun 2018\n\n[Changes][v1.0.0-beta]\n\n<a name=\"v1.0.0-alpha\"></a>\n# [Async alpha (v1.0.0-alpha)](https://github.com/vi/websocat/releases/tag/v1.0.0-alpha) - 10 May 2018\n\n[Changes][v1.0.0-alpha]\n\n<a name=\"v0.5.1-alpha\"></a>\n# [Async preview (v0.5.1-alpha)](https://github.com/vi/websocat/releases/tag/v0.5.1-alpha) - 14 Mar 2018\n\n[Changes][v0.5.1-alpha]\n\n<a name=\"v0.4.0\"></a>\n# [Forked rust-websocket (v0.4.0)](https://github.com/vi/websocat/releases/tag/v0.4.0) - 18 Jan 2017\n\n[Changes][v0.4.0]\n\n<a name=\"v0.3.0\"></a>\n# [More features (v0.3.0)](https://github.com/vi/websocat/releases/tag/v0.3.0) - 22 Dec 2016\n\n- Unix sockets\n- Executing programs and command lines\n- Unidirectional mode\n- Text mode (don't rely on it)\n\n[Changes][v0.3.0]\n\n<a name=\"v0.2.0\"></a>\n# [First actual release (v0.2.0)](https://github.com/vi/websocat/releases/tag/v0.2.0) - 24 Nov 2016\n\n[Changes][v0.2.0]\n\n\n[v1.12.0]: https://github.com/vi/websocat/compare/v1.11.0...v1.12.0\n[v1.11.0]: https://github.com/vi/websocat/compare/v1.10.0...v1.11.0\n[v1.10.0]: https://github.com/vi/websocat/compare/v3.0.0-prealpha0...v1.10.0\n[v1.9.0]: https://github.com/vi/websocat/compare/v1.8.0...v1.9.0\n[v1.8.0]: https://github.com/vi/websocat/compare/v1.7.0...v1.8.0\n[v1.7.0]: https://github.com/vi/websocat/compare/v1.6.0...v1.7.0\n[v1.6.0]: https://github.com/vi/websocat/compare/v2.0.0-alpha0...v1.6.0\n[v1.5.0]: https://github.com/vi/websocat/compare/v1.4.0...v1.5.0\n[v1.4.0]: https://github.com/vi/websocat/compare/v1.3.0...v1.4.0\n[v1.3.0]: https://github.com/vi/websocat/compare/v1.2.0...v1.3.0\n[v1.2.0]: https://github.com/vi/websocat/compare/v1.1.0...v1.2.0\n[v1.1.0]: https://github.com/vi/websocat/compare/v1.1-pre...v1.1.0\n[v1.1-pre]: https://github.com/vi/websocat/compare/v1.0.0...v1.1-pre\n[v1.0.0]: https://github.com/vi/websocat/compare/v1.0.0-beta...v1.0.0\n[v1.0.0-beta]: https://github.com/vi/websocat/compare/v1.0.0-alpha...v1.0.0-beta\n[v1.0.0-alpha]: https://github.com/vi/websocat/compare/v0.5.1-alpha...v1.0.0-alpha\n[v0.5.1-alpha]: https://github.com/vi/websocat/compare/v0.4.0...v0.5.1-alpha\n[v0.4.0]: https://github.com/vi/websocat/compare/v0.3.0...v0.4.0\n[v0.3.0]: https://github.com/vi/websocat/compare/v0.2.0...v0.3.0\n[v0.2.0]: https://github.com/vi/websocat/tree/v0.2.0\n\n<!-- Generated by https://github.com/rhysd/changelog-from-release v3.7.1 -->\n"
  },
  {
    "path": "Cargo.lock.legacy",
    "content": "# This file is automatically @generated by Cargo.\n# It is not intended for manual editing.\n[[package]]\nname = \"adler\"\nversion = \"1.0.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe\"\n\n[[package]]\nname = \"aead\"\nversion = \"0.4.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877\"\ndependencies = [\n \"generic-array 0.14.5\",\n]\n\n[[package]]\nname = \"anymap\"\nversion = \"0.12.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344\"\n\n[[package]]\nname = \"argon2\"\nversion = \"0.4.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73\"\ndependencies = [\n \"base64ct\",\n \"blake2\",\n \"password-hash\",\n]\n\n[[package]]\nname = \"atty\"\nversion = \"0.2.14\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8\"\ndependencies = [\n \"hermit-abi\",\n \"libc\",\n \"winapi 0.3.9\",\n]\n\n[[package]]\nname = \"autocfg\"\nversion = \"0.1.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78\"\ndependencies = [\n \"autocfg 1.1.0\",\n]\n\n[[package]]\nname = \"autocfg\"\nversion = \"1.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa\"\n\n[[package]]\nname = \"base64\"\nversion = \"0.9.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643\"\ndependencies = [\n \"byteorder\",\n \"safemem\",\n]\n\n[[package]]\nname = \"base64\"\nversion = \"0.10.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e\"\ndependencies = [\n \"byteorder\",\n]\n\n[[package]]\nname = \"base64ct\"\nversion = \"1.5.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851\"\n\n[[package]]\nname = \"bitflags\"\nversion = \"1.3.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a\"\n\n[[package]]\nname = \"blake2\"\nversion = \"0.10.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388\"\ndependencies = [\n \"digest 0.10.3\",\n]\n\n[[package]]\nname = \"block-buffer\"\nversion = \"0.7.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b\"\ndependencies = [\n \"block-padding\",\n \"byte-tools\",\n \"byteorder\",\n \"generic-array 0.12.4\",\n]\n\n[[package]]\nname = \"block-buffer\"\nversion = \"0.10.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324\"\ndependencies = [\n \"generic-array 0.14.5\",\n]\n\n[[package]]\nname = \"block-padding\"\nversion = \"0.1.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5\"\ndependencies = [\n \"byte-tools\",\n]\n\n[[package]]\nname = \"byte-tools\"\nversion = \"0.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7\"\n\n[[package]]\nname = \"byteorder\"\nversion = \"1.4.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610\"\n\n[[package]]\nname = \"bytes\"\nversion = \"0.4.12\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c\"\ndependencies = [\n \"byteorder\",\n \"iovec\",\n]\n\n[[package]]\nname = \"cc\"\nversion = \"1.0.73\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11\"\n\n[[package]]\nname = \"cfg-if\"\nversion = \"0.1.10\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822\"\n\n[[package]]\nname = \"cfg-if\"\nversion = \"1.0.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd\"\n\n[[package]]\nname = \"chacha20\"\nversion = \"0.8.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6\"\ndependencies = [\n \"cfg-if 1.0.0\",\n \"cipher\",\n \"cpufeatures\",\n \"zeroize\",\n]\n\n[[package]]\nname = \"chacha20poly1305\"\nversion = \"0.9.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5\"\ndependencies = [\n \"aead\",\n \"chacha20\",\n \"cipher\",\n \"poly1305\",\n \"zeroize\",\n]\n\n[[package]]\nname = \"cipher\"\nversion = \"0.3.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7\"\ndependencies = [\n \"generic-array 0.14.5\",\n]\n\n[[package]]\nname = \"clap\"\nversion = \"2.34.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c\"\ndependencies = [\n \"bitflags\",\n \"textwrap\",\n \"unicode-width\",\n]\n\n[[package]]\nname = \"cloudabi\"\nversion = \"0.0.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f\"\ndependencies = [\n \"bitflags\",\n]\n\n[[package]]\nname = \"core-foundation\"\nversion = \"0.9.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146\"\ndependencies = [\n \"core-foundation-sys\",\n \"libc\",\n]\n\n[[package]]\nname = \"core-foundation-sys\"\nversion = \"0.8.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc\"\n\n[[package]]\nname = \"cpufeatures\"\nversion = \"0.2.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b\"\ndependencies = [\n \"libc\",\n]\n\n[[package]]\nname = \"crc32fast\"\nversion = \"1.3.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d\"\ndependencies = [\n \"cfg-if 1.0.0\",\n]\n\n[[package]]\nname = \"crossbeam-deque\"\nversion = \"0.7.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed\"\ndependencies = [\n \"crossbeam-epoch\",\n \"crossbeam-utils 0.7.2\",\n \"maybe-uninit\",\n]\n\n[[package]]\nname = \"crossbeam-epoch\"\nversion = \"0.8.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace\"\ndependencies = [\n \"autocfg 1.1.0\",\n \"cfg-if 0.1.10\",\n \"crossbeam-utils 0.7.2\",\n \"lazy_static\",\n \"maybe-uninit\",\n \"memoffset\",\n \"scopeguard\",\n]\n\n[[package]]\nname = \"crossbeam-queue\"\nversion = \"0.1.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b\"\ndependencies = [\n \"crossbeam-utils 0.6.6\",\n]\n\n[[package]]\nname = \"crossbeam-queue\"\nversion = \"0.2.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570\"\ndependencies = [\n \"cfg-if 0.1.10\",\n \"crossbeam-utils 0.7.2\",\n \"maybe-uninit\",\n]\n\n[[package]]\nname = \"crossbeam-utils\"\nversion = \"0.6.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6\"\ndependencies = [\n \"cfg-if 0.1.10\",\n \"lazy_static\",\n]\n\n[[package]]\nname = \"crossbeam-utils\"\nversion = \"0.7.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8\"\ndependencies = [\n \"autocfg 1.1.0\",\n \"cfg-if 0.1.10\",\n \"lazy_static\",\n]\n\n[[package]]\nname = \"crypto-common\"\nversion = \"0.1.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3\"\ndependencies = [\n \"generic-array 0.14.5\",\n \"typenum\",\n]\n\n[[package]]\nname = \"derivative\"\nversion = \"1.0.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3c6d883546668a3e2011b6a716a7330b82eabb0151b138217f632c8243e17135\"\ndependencies = [\n \"proc-macro2 0.4.30\",\n \"quote 0.6.13\",\n \"syn 0.15.44\",\n]\n\n[[package]]\nname = \"digest\"\nversion = \"0.8.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5\"\ndependencies = [\n \"generic-array 0.12.4\",\n]\n\n[[package]]\nname = \"digest\"\nversion = \"0.10.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506\"\ndependencies = [\n \"block-buffer 0.10.2\",\n \"crypto-common\",\n \"subtle\",\n]\n\n[[package]]\nname = \"env_logger\"\nversion = \"0.6.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3\"\ndependencies = [\n \"log 0.4.17\",\n]\n\n[[package]]\nname = \"fake-simd\"\nversion = \"0.1.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed\"\n\n[[package]]\nname = \"fastrand\"\nversion = \"1.8.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499\"\ndependencies = [\n \"instant\",\n]\n\n[[package]]\nname = \"flate2\"\nversion = \"1.0.24\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6\"\ndependencies = [\n \"crc32fast\",\n \"miniz_oxide\",\n]\n\n[[package]]\nname = \"fnv\"\nversion = \"1.0.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1\"\n\n[[package]]\nname = \"foreign-types\"\nversion = \"0.3.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1\"\ndependencies = [\n \"foreign-types-shared\",\n]\n\n[[package]]\nname = \"foreign-types-shared\"\nversion = \"0.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b\"\n\n[[package]]\nname = \"fuchsia-cprng\"\nversion = \"0.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba\"\n\n[[package]]\nname = \"fuchsia-zircon\"\nversion = \"0.3.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82\"\ndependencies = [\n \"bitflags\",\n \"fuchsia-zircon-sys\",\n]\n\n[[package]]\nname = \"fuchsia-zircon-sys\"\nversion = \"0.3.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7\"\n\n[[package]]\nname = \"futures\"\nversion = \"0.1.31\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678\"\n\n[[package]]\nname = \"generic-array\"\nversion = \"0.12.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd\"\ndependencies = [\n \"typenum\",\n]\n\n[[package]]\nname = \"generic-array\"\nversion = \"0.14.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803\"\ndependencies = [\n \"typenum\",\n \"version_check 0.9.4\",\n]\n\n[[package]]\nname = \"getrandom\"\nversion = \"0.2.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6\"\ndependencies = [\n \"cfg-if 1.0.0\",\n \"libc\",\n \"wasi 0.11.0+wasi-snapshot-preview1\",\n]\n\n[[package]]\nname = \"heck\"\nversion = \"0.3.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c\"\ndependencies = [\n \"unicode-segmentation\",\n]\n\n[[package]]\nname = \"hermit-abi\"\nversion = \"0.1.19\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33\"\ndependencies = [\n \"libc\",\n]\n\n[[package]]\nname = \"hex\"\nversion = \"0.4.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70\"\n\n[[package]]\nname = \"http\"\nversion = \"0.1.21\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0\"\ndependencies = [\n \"bytes\",\n \"fnv\",\n \"itoa\",\n]\n\n[[package]]\nname = \"http-bytes\"\nversion = \"0.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3332986b24440d485a8c31e5a823c098f9c1616e7c60945e09dbc6c8d45bed55\"\ndependencies = [\n \"base64 0.10.1\",\n \"bytes\",\n \"http\",\n \"httparse\",\n \"percent-encoding\",\n]\n\n[[package]]\nname = \"httparse\"\nversion = \"1.7.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c\"\n\n[[package]]\nname = \"hyper\"\nversion = \"0.10.16\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273\"\ndependencies = [\n \"base64 0.9.3\",\n \"httparse\",\n \"language-tags\",\n \"log 0.3.9\",\n \"mime\",\n \"num_cpus\",\n \"time\",\n \"traitobject\",\n \"typeable\",\n \"unicase\",\n \"url\",\n]\n\n[[package]]\nname = \"idna\"\nversion = \"0.1.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e\"\ndependencies = [\n \"matches\",\n \"unicode-bidi\",\n \"unicode-normalization\",\n]\n\n[[package]]\nname = \"instant\"\nversion = \"0.1.12\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c\"\ndependencies = [\n \"cfg-if 1.0.0\",\n]\n\n[[package]]\nname = \"iovec\"\nversion = \"0.1.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e\"\ndependencies = [\n \"libc\",\n]\n\n[[package]]\nname = \"itoa\"\nversion = \"0.4.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4\"\n\n[[package]]\nname = \"kernel32-sys\"\nversion = \"0.2.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d\"\ndependencies = [\n \"winapi 0.2.8\",\n \"winapi-build\",\n]\n\n[[package]]\nname = \"language-tags\"\nversion = \"0.2.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a\"\n\n[[package]]\nname = \"lazy_static\"\nversion = \"1.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646\"\n\n[[package]]\nname = \"libc\"\nversion = \"0.2.126\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836\"\n\n[[package]]\nname = \"lock_api\"\nversion = \"0.3.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75\"\ndependencies = [\n \"scopeguard\",\n]\n\n[[package]]\nname = \"lock_api\"\nversion = \"0.4.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53\"\ndependencies = [\n \"autocfg 1.1.0\",\n \"scopeguard\",\n]\n\n[[package]]\nname = \"log\"\nversion = \"0.3.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b\"\ndependencies = [\n \"log 0.4.17\",\n]\n\n[[package]]\nname = \"log\"\nversion = \"0.4.17\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e\"\ndependencies = [\n \"cfg-if 1.0.0\",\n]\n\n[[package]]\nname = \"matches\"\nversion = \"0.1.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f\"\n\n[[package]]\nname = \"maybe-uninit\"\nversion = \"2.0.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00\"\n\n[[package]]\nname = \"memchr\"\nversion = \"2.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d\"\n\n[[package]]\nname = \"memoffset\"\nversion = \"0.5.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa\"\ndependencies = [\n \"autocfg 1.1.0\",\n]\n\n[[package]]\nname = \"mime\"\nversion = \"0.2.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0\"\ndependencies = [\n \"log 0.3.9\",\n]\n\n[[package]]\nname = \"miniz_oxide\"\nversion = \"0.5.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34\"\ndependencies = [\n \"adler\",\n]\n\n[[package]]\nname = \"mio\"\nversion = \"0.6.23\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4\"\ndependencies = [\n \"cfg-if 0.1.10\",\n \"fuchsia-zircon\",\n \"fuchsia-zircon-sys\",\n \"iovec\",\n \"kernel32-sys\",\n \"libc\",\n \"log 0.4.17\",\n \"miow 0.2.2\",\n \"net2\",\n \"slab\",\n \"winapi 0.2.8\",\n]\n\n[[package]]\nname = \"mio-named-pipes\"\nversion = \"0.1.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656\"\ndependencies = [\n \"log 0.4.17\",\n \"mio\",\n \"miow 0.3.7\",\n \"winapi 0.3.9\",\n]\n\n[[package]]\nname = \"mio-uds\"\nversion = \"0.6.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0\"\ndependencies = [\n \"iovec\",\n \"libc\",\n \"mio\",\n]\n\n[[package]]\nname = \"miow\"\nversion = \"0.2.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d\"\ndependencies = [\n \"kernel32-sys\",\n \"net2\",\n \"winapi 0.2.8\",\n \"ws2_32-sys\",\n]\n\n[[package]]\nname = \"miow\"\nversion = \"0.3.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21\"\ndependencies = [\n \"winapi 0.3.9\",\n]\n\n[[package]]\nname = \"native-tls\"\nversion = \"0.2.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d\"\ndependencies = [\n \"lazy_static\",\n \"libc\",\n \"log 0.4.17\",\n \"openssl\",\n \"openssl-probe\",\n \"openssl-sys\",\n \"schannel\",\n \"security-framework\",\n \"security-framework-sys\",\n \"tempfile\",\n]\n\n[[package]]\nname = \"net2\"\nversion = \"0.2.37\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae\"\ndependencies = [\n \"cfg-if 0.1.10\",\n \"libc\",\n \"winapi 0.3.9\",\n]\n\n[[package]]\nname = \"num_cpus\"\nversion = \"1.13.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1\"\ndependencies = [\n \"hermit-abi\",\n \"libc\",\n]\n\n[[package]]\nname = \"once_cell\"\nversion = \"1.13.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1\"\n\n[[package]]\nname = \"opaque-debug\"\nversion = \"0.2.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c\"\n\n[[package]]\nname = \"opaque-debug\"\nversion = \"0.3.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5\"\n\n[[package]]\nname = \"openssl\"\nversion = \"0.10.38\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95\"\ndependencies = [\n \"bitflags\",\n \"cfg-if 1.0.0\",\n \"foreign-types\",\n \"libc\",\n \"once_cell\",\n \"openssl-sys\",\n]\n\n[[package]]\nname = \"openssl-probe\"\nversion = \"0.1.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf\"\n\n[[package]]\nname = \"openssl-src\"\nversion = \"111.22.0+1.1.1q\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8f31f0d509d1c1ae9cada2f9539ff8f37933831fd5098879e482aa687d659853\"\ndependencies = [\n \"cc\",\n]\n\n[[package]]\nname = \"openssl-sys\"\nversion = \"0.9.72\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb\"\ndependencies = [\n \"autocfg 1.1.0\",\n \"cc\",\n \"libc\",\n \"openssl-src\",\n \"pkg-config\",\n \"vcpkg\",\n]\n\n[[package]]\nname = \"parking_lot\"\nversion = \"0.9.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252\"\ndependencies = [\n \"lock_api 0.3.4\",\n \"parking_lot_core 0.6.2\",\n \"rustc_version\",\n]\n\n[[package]]\nname = \"parking_lot\"\nversion = \"0.12.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f\"\ndependencies = [\n \"lock_api 0.4.7\",\n \"parking_lot_core 0.9.3\",\n]\n\n[[package]]\nname = \"parking_lot_core\"\nversion = \"0.6.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b\"\ndependencies = [\n \"cfg-if 0.1.10\",\n \"cloudabi\",\n \"libc\",\n \"redox_syscall 0.1.57\",\n \"rustc_version\",\n \"smallvec 0.6.14\",\n \"winapi 0.3.9\",\n]\n\n[[package]]\nname = \"parking_lot_core\"\nversion = \"0.9.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929\"\ndependencies = [\n \"cfg-if 1.0.0\",\n \"libc\",\n \"redox_syscall 0.2.16\",\n \"smallvec 1.9.0\",\n \"windows-sys\",\n]\n\n[[package]]\nname = \"password-hash\"\nversion = \"0.4.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700\"\ndependencies = [\n \"base64ct\",\n \"rand_core 0.6.3\",\n \"subtle\",\n]\n\n[[package]]\nname = \"percent-encoding\"\nversion = \"1.0.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831\"\n\n[[package]]\nname = \"pkg-config\"\nversion = \"0.3.25\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae\"\n\n[[package]]\nname = \"poly1305\"\nversion = \"0.7.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede\"\ndependencies = [\n \"cpufeatures\",\n \"opaque-debug 0.3.0\",\n \"universal-hash\",\n]\n\n[[package]]\nname = \"ppv-lite86\"\nversion = \"0.2.16\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872\"\n\n[[package]]\nname = \"proc-macro2\"\nversion = \"0.4.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759\"\ndependencies = [\n \"unicode-xid\",\n]\n\n[[package]]\nname = \"proc-macro2\"\nversion = \"1.0.42\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b\"\ndependencies = [\n \"unicode-ident\",\n]\n\n[[package]]\nname = \"prometheus\"\nversion = \"0.13.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"cface98dfa6d645ea4c789839f176e4b072265d085bfcc48eaa8d137f58d3c39\"\ndependencies = [\n \"cfg-if 1.0.0\",\n \"fnv\",\n \"lazy_static\",\n \"memchr\",\n \"parking_lot 0.12.1\",\n \"thiserror\",\n]\n\n[[package]]\nname = \"prometheus-metric-storage\"\nversion = \"0.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3282447ea0b07baa9011e45de96794c5963db0162c1001d2867750715d63ff14\"\ndependencies = [\n \"lazy_static\",\n \"prometheus\",\n \"prometheus-metric-storage-derive\",\n]\n\n[[package]]\nname = \"prometheus-metric-storage-derive\"\nversion = \"0.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"239221aa10cd35277c58b8f6509280b2613321760b49584d7a7351a6aacb6963\"\ndependencies = [\n \"proc-macro2 1.0.42\",\n \"quote 1.0.20\",\n \"syn 1.0.98\",\n]\n\n[[package]]\nname = \"quote\"\nversion = \"0.6.13\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1\"\ndependencies = [\n \"proc-macro2 0.4.30\",\n]\n\n[[package]]\nname = \"quote\"\nversion = \"1.0.20\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804\"\ndependencies = [\n \"proc-macro2 1.0.42\",\n]\n\n[[package]]\nname = \"rand\"\nversion = \"0.6.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca\"\ndependencies = [\n \"autocfg 0.1.8\",\n \"libc\",\n \"rand_chacha 0.1.1\",\n \"rand_core 0.4.2\",\n \"rand_hc\",\n \"rand_isaac\",\n \"rand_jitter\",\n \"rand_os\",\n \"rand_pcg\",\n \"rand_xorshift\",\n \"winapi 0.3.9\",\n]\n\n[[package]]\nname = \"rand\"\nversion = \"0.8.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404\"\ndependencies = [\n \"libc\",\n \"rand_chacha 0.3.1\",\n \"rand_core 0.6.3\",\n]\n\n[[package]]\nname = \"rand_chacha\"\nversion = \"0.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef\"\ndependencies = [\n \"autocfg 0.1.8\",\n \"rand_core 0.3.1\",\n]\n\n[[package]]\nname = \"rand_chacha\"\nversion = \"0.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88\"\ndependencies = [\n \"ppv-lite86\",\n \"rand_core 0.6.3\",\n]\n\n[[package]]\nname = \"rand_core\"\nversion = \"0.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b\"\ndependencies = [\n \"rand_core 0.4.2\",\n]\n\n[[package]]\nname = \"rand_core\"\nversion = \"0.4.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc\"\n\n[[package]]\nname = \"rand_core\"\nversion = \"0.6.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7\"\ndependencies = [\n \"getrandom\",\n]\n\n[[package]]\nname = \"rand_hc\"\nversion = \"0.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4\"\ndependencies = [\n \"rand_core 0.3.1\",\n]\n\n[[package]]\nname = \"rand_isaac\"\nversion = \"0.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08\"\ndependencies = [\n \"rand_core 0.3.1\",\n]\n\n[[package]]\nname = \"rand_jitter\"\nversion = \"0.1.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b\"\ndependencies = [\n \"libc\",\n \"rand_core 0.4.2\",\n \"winapi 0.3.9\",\n]\n\n[[package]]\nname = \"rand_os\"\nversion = \"0.1.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071\"\ndependencies = [\n \"cloudabi\",\n \"fuchsia-cprng\",\n \"libc\",\n \"rand_core 0.4.2\",\n \"rdrand\",\n \"winapi 0.3.9\",\n]\n\n[[package]]\nname = \"rand_pcg\"\nversion = \"0.1.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44\"\ndependencies = [\n \"autocfg 0.1.8\",\n \"rand_core 0.4.2\",\n]\n\n[[package]]\nname = \"rand_xorshift\"\nversion = \"0.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c\"\ndependencies = [\n \"rand_core 0.3.1\",\n]\n\n[[package]]\nname = \"rdrand\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2\"\ndependencies = [\n \"rand_core 0.3.1\",\n]\n\n[[package]]\nname = \"readwrite\"\nversion = \"0.1.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"73891b98dabbe836d23a094941e6ec891bc4880e771faea98813f2ff27ede473\"\ndependencies = [\n \"futures\",\n \"tokio-io\",\n]\n\n[[package]]\nname = \"redox_syscall\"\nversion = \"0.1.57\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce\"\n\n[[package]]\nname = \"redox_syscall\"\nversion = \"0.2.16\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a\"\ndependencies = [\n \"bitflags\",\n]\n\n[[package]]\nname = \"remove_dir_all\"\nversion = \"0.5.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7\"\ndependencies = [\n \"winapi 0.3.9\",\n]\n\n[[package]]\nname = \"rustc_version\"\nversion = \"0.2.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a\"\ndependencies = [\n \"semver\",\n]\n\n[[package]]\nname = \"safemem\"\nversion = \"0.3.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072\"\n\n[[package]]\nname = \"schannel\"\nversion = \"0.1.20\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2\"\ndependencies = [\n \"lazy_static\",\n \"windows-sys\",\n]\n\n[[package]]\nname = \"scopeguard\"\nversion = \"1.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd\"\n\n[[package]]\nname = \"security-framework\"\nversion = \"2.6.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc\"\ndependencies = [\n \"bitflags\",\n \"core-foundation\",\n \"core-foundation-sys\",\n \"libc\",\n \"security-framework-sys\",\n]\n\n[[package]]\nname = \"security-framework-sys\"\nversion = \"2.6.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556\"\ndependencies = [\n \"core-foundation-sys\",\n \"libc\",\n]\n\n[[package]]\nname = \"semver\"\nversion = \"0.9.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403\"\ndependencies = [\n \"semver-parser\",\n]\n\n[[package]]\nname = \"semver-parser\"\nversion = \"0.7.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3\"\n\n[[package]]\nname = \"sha-1\"\nversion = \"0.8.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df\"\ndependencies = [\n \"block-buffer 0.7.3\",\n \"digest 0.8.1\",\n \"fake-simd\",\n \"opaque-debug 0.2.3\",\n]\n\n[[package]]\nname = \"signal-hook-registry\"\nversion = \"1.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0\"\ndependencies = [\n \"libc\",\n]\n\n[[package]]\nname = \"slab\"\nversion = \"0.4.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef\"\ndependencies = [\n \"autocfg 1.1.0\",\n]\n\n[[package]]\nname = \"slab_typesafe\"\nversion = \"0.1.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"56e1a2062526abda41283046a3149dc589cb760c8c6672dd88e209f7fba0c0c1\"\ndependencies = [\n \"slab\",\n]\n\n[[package]]\nname = \"smallvec\"\nversion = \"0.6.14\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0\"\ndependencies = [\n \"maybe-uninit\",\n]\n\n[[package]]\nname = \"smallvec\"\nversion = \"1.9.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1\"\n\n[[package]]\nname = \"smart-default\"\nversion = \"0.3.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"70e5c02ddada494809d36623d38050f3bd63446750abd21e7e13c01aa3a79b69\"\ndependencies = [\n \"proc-macro2 0.4.30\",\n \"quote 0.6.13\",\n \"syn 0.15.44\",\n]\n\n[[package]]\nname = \"structopt\"\nversion = \"0.2.16\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fa19a5a708e22bb5be31c1b6108a2a902f909c4b9ba85cba44c06632386bc0ff\"\ndependencies = [\n \"clap\",\n \"structopt-derive\",\n]\n\n[[package]]\nname = \"structopt-derive\"\nversion = \"0.2.16\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c6d59d0ae8ef8de16e49e3ca7afa16024a3e0dfd974a75ef93fdc5464e34523f\"\ndependencies = [\n \"heck\",\n \"proc-macro2 0.4.30\",\n \"quote 0.6.13\",\n \"syn 0.15.44\",\n]\n\n[[package]]\nname = \"subtle\"\nversion = \"2.4.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601\"\n\n[[package]]\nname = \"syn\"\nversion = \"0.15.44\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5\"\ndependencies = [\n \"proc-macro2 0.4.30\",\n \"quote 0.6.13\",\n \"unicode-xid\",\n]\n\n[[package]]\nname = \"syn\"\nversion = \"1.0.98\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd\"\ndependencies = [\n \"proc-macro2 1.0.42\",\n \"quote 1.0.20\",\n \"unicode-ident\",\n]\n\n[[package]]\nname = \"tempfile\"\nversion = \"3.3.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4\"\ndependencies = [\n \"cfg-if 1.0.0\",\n \"fastrand\",\n \"libc\",\n \"redox_syscall 0.2.16\",\n \"remove_dir_all\",\n \"winapi 0.3.9\",\n]\n\n[[package]]\nname = \"textwrap\"\nversion = \"0.11.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060\"\ndependencies = [\n \"unicode-width\",\n]\n\n[[package]]\nname = \"thiserror\"\nversion = \"1.0.31\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a\"\ndependencies = [\n \"thiserror-impl\",\n]\n\n[[package]]\nname = \"thiserror-impl\"\nversion = \"1.0.31\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a\"\ndependencies = [\n \"proc-macro2 1.0.42\",\n \"quote 1.0.20\",\n \"syn 1.0.98\",\n]\n\n[[package]]\nname = \"time\"\nversion = \"0.1.44\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255\"\ndependencies = [\n \"libc\",\n \"wasi 0.10.0+wasi-snapshot-preview1\",\n \"winapi 0.3.9\",\n]\n\n[[package]]\nname = \"tinyvec\"\nversion = \"1.6.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50\"\ndependencies = [\n \"tinyvec_macros\",\n]\n\n[[package]]\nname = \"tinyvec_macros\"\nversion = \"0.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c\"\n\n[[package]]\nname = \"tk-listen\"\nversion = \"0.2.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5462b0f968c0457efe38fcd2df7e487096b992419e4f5337b06775a614bbda4b\"\ndependencies = [\n \"futures\",\n \"log 0.4.17\",\n \"tokio\",\n \"tokio-io\",\n]\n\n[[package]]\nname = \"tokio\"\nversion = \"0.1.22\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6\"\ndependencies = [\n \"bytes\",\n \"futures\",\n \"mio\",\n \"num_cpus\",\n \"tokio-codec\",\n \"tokio-current-thread\",\n \"tokio-executor\",\n \"tokio-fs\",\n \"tokio-io\",\n \"tokio-reactor\",\n \"tokio-sync\",\n \"tokio-tcp\",\n \"tokio-threadpool\",\n \"tokio-timer\",\n \"tokio-udp\",\n \"tokio-uds\",\n]\n\n[[package]]\nname = \"tokio-codec\"\nversion = \"0.1.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b\"\ndependencies = [\n \"bytes\",\n \"futures\",\n \"tokio-io\",\n]\n\n[[package]]\nname = \"tokio-current-thread\"\nversion = \"0.1.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e\"\ndependencies = [\n \"futures\",\n \"tokio-executor\",\n]\n\n[[package]]\nname = \"tokio-executor\"\nversion = \"0.1.10\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671\"\ndependencies = [\n \"crossbeam-utils 0.7.2\",\n \"futures\",\n]\n\n[[package]]\nname = \"tokio-file-unix\"\nversion = \"0.5.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7742e2a421379472607d46e2641e66ee2d135f5274d12ad0aba1c1fbbcea2e8c\"\ndependencies = [\n \"bytes\",\n \"libc\",\n \"mio\",\n \"tokio-io\",\n \"tokio-reactor\",\n]\n\n[[package]]\nname = \"tokio-fs\"\nversion = \"0.1.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4\"\ndependencies = [\n \"futures\",\n \"tokio-io\",\n \"tokio-threadpool\",\n]\n\n[[package]]\nname = \"tokio-io\"\nversion = \"0.1.13\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674\"\ndependencies = [\n \"bytes\",\n \"futures\",\n \"log 0.4.17\",\n]\n\n[[package]]\nname = \"tokio-named-pipes\"\nversion = \"0.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9d282d483052288b2308ba5ee795f5673b159c9bdf63c385a05609da782a5eae\"\ndependencies = [\n \"bytes\",\n \"futures\",\n \"mio\",\n \"mio-named-pipes\",\n \"tokio\",\n]\n\n[[package]]\nname = \"tokio-process\"\nversion = \"0.2.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"382d90f43fa31caebe5d3bc6cfd854963394fff3b8cb59d5146607aaae7e7e43\"\ndependencies = [\n \"crossbeam-queue 0.1.2\",\n \"futures\",\n \"lazy_static\",\n \"libc\",\n \"log 0.4.17\",\n \"mio\",\n \"mio-named-pipes\",\n \"tokio-io\",\n \"tokio-reactor\",\n \"tokio-signal\",\n \"winapi 0.3.9\",\n]\n\n[[package]]\nname = \"tokio-reactor\"\nversion = \"0.1.12\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351\"\ndependencies = [\n \"crossbeam-utils 0.7.2\",\n \"futures\",\n \"lazy_static\",\n \"log 0.4.17\",\n \"mio\",\n \"num_cpus\",\n \"parking_lot 0.9.0\",\n \"slab\",\n \"tokio-executor\",\n \"tokio-io\",\n \"tokio-sync\",\n]\n\n[[package]]\nname = \"tokio-signal\"\nversion = \"0.2.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d0c34c6e548f101053321cba3da7cbb87a610b85555884c41b07da2eb91aff12\"\ndependencies = [\n \"futures\",\n \"libc\",\n \"mio\",\n \"mio-uds\",\n \"signal-hook-registry\",\n \"tokio-executor\",\n \"tokio-io\",\n \"tokio-reactor\",\n \"winapi 0.3.9\",\n]\n\n[[package]]\nname = \"tokio-stdin-stdout\"\nversion = \"0.1.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1fc480d205310fa52f8ea65e7f9443568b6b342f326e86431d2aeb176d720c17\"\ndependencies = [\n \"futures\",\n \"tokio-io\",\n]\n\n[[package]]\nname = \"tokio-sync\"\nversion = \"0.1.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee\"\ndependencies = [\n \"fnv\",\n \"futures\",\n]\n\n[[package]]\nname = \"tokio-tcp\"\nversion = \"0.1.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72\"\ndependencies = [\n \"bytes\",\n \"futures\",\n \"iovec\",\n \"mio\",\n \"tokio-io\",\n \"tokio-reactor\",\n]\n\n[[package]]\nname = \"tokio-threadpool\"\nversion = \"0.1.18\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89\"\ndependencies = [\n \"crossbeam-deque\",\n \"crossbeam-queue 0.2.3\",\n \"crossbeam-utils 0.7.2\",\n \"futures\",\n \"lazy_static\",\n \"log 0.4.17\",\n \"num_cpus\",\n \"slab\",\n \"tokio-executor\",\n]\n\n[[package]]\nname = \"tokio-timer\"\nversion = \"0.2.13\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296\"\ndependencies = [\n \"crossbeam-utils 0.7.2\",\n \"futures\",\n \"slab\",\n \"tokio-executor\",\n]\n\n[[package]]\nname = \"tokio-tls\"\nversion = \"0.2.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c\"\ndependencies = [\n \"futures\",\n \"native-tls\",\n \"tokio-io\",\n]\n\n[[package]]\nname = \"tokio-udp\"\nversion = \"0.1.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82\"\ndependencies = [\n \"bytes\",\n \"futures\",\n \"log 0.4.17\",\n \"mio\",\n \"tokio-codec\",\n \"tokio-io\",\n \"tokio-reactor\",\n]\n\n[[package]]\nname = \"tokio-uds\"\nversion = \"0.2.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0\"\ndependencies = [\n \"bytes\",\n \"futures\",\n \"iovec\",\n \"libc\",\n \"log 0.4.17\",\n \"mio\",\n \"mio-uds\",\n \"tokio-codec\",\n \"tokio-io\",\n \"tokio-reactor\",\n]\n\n[[package]]\nname = \"traitobject\"\nversion = \"0.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079\"\n\n[[package]]\nname = \"typeable\"\nversion = \"0.1.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887\"\n\n[[package]]\nname = \"typenum\"\nversion = \"1.15.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987\"\n\n[[package]]\nname = \"unicase\"\nversion = \"1.4.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33\"\ndependencies = [\n \"version_check 0.1.5\",\n]\n\n[[package]]\nname = \"unicode-bidi\"\nversion = \"0.3.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992\"\n\n[[package]]\nname = \"unicode-ident\"\nversion = \"1.0.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7\"\n\n[[package]]\nname = \"unicode-normalization\"\nversion = \"0.1.21\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6\"\ndependencies = [\n \"tinyvec\",\n]\n\n[[package]]\nname = \"unicode-segmentation\"\nversion = \"1.9.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99\"\n\n[[package]]\nname = \"unicode-width\"\nversion = \"0.1.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973\"\n\n[[package]]\nname = \"unicode-xid\"\nversion = \"0.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc\"\n\n[[package]]\nname = \"universal-hash\"\nversion = \"0.4.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05\"\ndependencies = [\n \"generic-array 0.14.5\",\n \"subtle\",\n]\n\n[[package]]\nname = \"url\"\nversion = \"1.7.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a\"\ndependencies = [\n \"idna\",\n \"matches\",\n \"percent-encoding\",\n]\n\n[[package]]\nname = \"vcpkg\"\nversion = \"0.2.15\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\n\n[[package]]\nname = \"version_check\"\nversion = \"0.1.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd\"\n\n[[package]]\nname = \"version_check\"\nversion = \"0.9.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f\"\n\n[[package]]\nname = \"wasi\"\nversion = \"0.10.0+wasi-snapshot-preview1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f\"\n\n[[package]]\nname = \"wasi\"\nversion = \"0.11.0+wasi-snapshot-preview1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423\"\n\n[[package]]\nname = \"websocat\"\nversion = \"1.11.0\"\ndependencies = [\n \"anymap\",\n \"argon2\",\n \"atty\",\n \"base64 0.10.1\",\n \"chacha20poly1305\",\n \"derivative\",\n \"env_logger\",\n \"flate2\",\n \"futures\",\n \"hex\",\n \"http-bytes\",\n \"hyper\",\n \"libc\",\n \"log 0.4.17\",\n \"native-tls\",\n \"net2\",\n \"openssl-probe\",\n \"openssl-sys\",\n \"prometheus\",\n \"prometheus-metric-storage\",\n \"rand 0.8.5\",\n \"readwrite\",\n \"slab_typesafe\",\n \"smart-default\",\n \"structopt\",\n \"structopt-derive\",\n \"tempfile\",\n \"tk-listen\",\n \"tokio\",\n \"tokio-codec\",\n \"tokio-current-thread\",\n \"tokio-file-unix\",\n \"tokio-io\",\n \"tokio-named-pipes\",\n \"tokio-process\",\n \"tokio-reactor\",\n \"tokio-signal\",\n \"tokio-stdin-stdout\",\n \"tokio-tcp\",\n \"tokio-timer\",\n \"tokio-tls\",\n \"tokio-udp\",\n \"tokio-uds\",\n \"url\",\n \"websocket\",\n \"websocket-base\",\n]\n\n[[package]]\nname = \"websocket\"\nversion = \"0.26.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"92aacab060eea423e4036820ddd28f3f9003b2c4d8048cbda985e5a14e18038d\"\ndependencies = [\n \"bytes\",\n \"futures\",\n \"hyper\",\n \"native-tls\",\n \"rand 0.6.5\",\n \"tokio-codec\",\n \"tokio-io\",\n \"tokio-reactor\",\n \"tokio-tcp\",\n \"tokio-tls\",\n \"unicase\",\n \"url\",\n \"websocket-base\",\n]\n\n[[package]]\nname = \"websocket-base\"\nversion = \"0.26.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"49aec794b07318993d1db16156d5a9c750120597a5ee40c6b928d416186cb138\"\ndependencies = [\n \"base64 0.10.1\",\n \"bitflags\",\n \"byteorder\",\n \"bytes\",\n \"futures\",\n \"native-tls\",\n \"rand 0.6.5\",\n \"sha-1\",\n \"tokio-codec\",\n \"tokio-io\",\n \"tokio-tcp\",\n \"tokio-tls\",\n]\n\n[[package]]\nname = \"winapi\"\nversion = \"0.2.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a\"\n\n[[package]]\nname = \"winapi\"\nversion = \"0.3.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419\"\ndependencies = [\n \"winapi-i686-pc-windows-gnu\",\n \"winapi-x86_64-pc-windows-gnu\",\n]\n\n[[package]]\nname = \"winapi-build\"\nversion = \"0.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc\"\n\n[[package]]\nname = \"winapi-i686-pc-windows-gnu\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6\"\n\n[[package]]\nname = \"winapi-x86_64-pc-windows-gnu\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f\"\n\n[[package]]\nname = \"windows-sys\"\nversion = \"0.36.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2\"\ndependencies = [\n \"windows_aarch64_msvc\",\n \"windows_i686_gnu\",\n \"windows_i686_msvc\",\n \"windows_x86_64_gnu\",\n \"windows_x86_64_msvc\",\n]\n\n[[package]]\nname = \"windows_aarch64_msvc\"\nversion = \"0.36.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47\"\n\n[[package]]\nname = \"windows_i686_gnu\"\nversion = \"0.36.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6\"\n\n[[package]]\nname = \"windows_i686_msvc\"\nversion = \"0.36.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024\"\n\n[[package]]\nname = \"windows_x86_64_gnu\"\nversion = \"0.36.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1\"\n\n[[package]]\nname = \"windows_x86_64_msvc\"\nversion = \"0.36.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680\"\n\n[[package]]\nname = \"ws2_32-sys\"\nversion = \"0.2.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e\"\ndependencies = [\n \"winapi 0.2.8\",\n \"winapi-build\",\n]\n\n[[package]]\nname = \"zeroize\"\nversion = \"1.5.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f\"\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"websocat\"\nversion = \"1.14.1\"\nauthors = [\"Vitaly \\\"_Vi\\\" Shukela <vi0oss@gmail.com>\"]\nlicense = \"MIT\"\nrepository = \"https://github.com/vi/websocat\"\ndescription = \"Command-line client for web sockets, like netcat/curl/socat for ws://.\"\nkeywords = [\"WebSocket\", \"socat\", \"rfc6455\", \"netcat\", \"cli\"]\ninclude = [\"src\",\"Cargo.toml\",\"LICENSE\",\"README.md\"]\nreadme = \"README.md\"\nedition = \"2018\"\n#msrv = \"1.48.0\"\n\n[package.metadata.deb]\nsection = \"utility\"\nextended-description = \"\"\"\\\nA tool allows you to interconnect two specifiers, like in socat, \\\nbut with Websocket and some other additional functions.\"\"\"\nfeatures = [\"ssl\", \"workaround1\", \"seqpacket\", \"unix_stdio\"]\n#depends = \"$auto\"\ndepends = \"libssl1.1, libc6 (>= 2.19), libgcc1 (>= 1:4.9.0)\"\n\n\n[dependencies]\nwebsocket = { version=\"0.27.1\", default-features = false, features=[\"async\"] }\nwebsocket-base = { version=\"0.26.5\", default-features = false, features=[\"async\"] }\nhttp-bytes = {version = \"0.1.0\"}\nenv_logger = { version = \"0.6.0\", default-features = false }\nlog = {version=\"0.4.1\", default-features = false, features=[\"release_max_level_debug\"]}\nfutures = {version = \"0.1.17\" }\ntokio-io = \"0.1.5\"\ntokio-stdin-stdout = \"0.1.5\"\nstructopt = { version = \"=0.2.16\", default-features = false }\nstructopt-derive = { version = \"=0.2.16\", default-features = false }\ntokio-process = { version = \"0.2.3\", optional = true }\nslab_typesafe = \"0.1\"\nhyper=\"0.10.13\"\nurl=\"1.7.1\"\nopenssl-probe = { version = \"0.1.2\", optional = true }\nsmart-default = \"0.3.0\"\ntokio-tls = {version = \"0.2.0\", optional = true}\nnative-tls = {version = \"0.2.1\", optional = true}\nreadwrite = {version = \"0.1.1\", optional = true, features = [\"tokio\"]}\nderivative=\"1.0.0\"\ntokio-codec = \"0.1.1\"\ntokio-tcp = \"0.1.2\"\ntokio-udp = \"0.1.3\"\ntokio-reactor = \"0.1.7\"\ntokio = \"0.1.11\"\ntokio-current-thread = \"0.1.4\"\ntk-listen = \"0.2.1\"\ntokio-timer = \"0.2.0\"\ntempfile = \"3.0.8\"\nnet2 = \"0.2.33\"\nanymap = \"0.12.1\"\nbase64 = \"0.10\"\natty = \"0.2.14\"\n#anymap = { path = \"/mnt/src/git/anymap\"}\nhex = \"0.4.2\"\nchacha20poly1305 = {version=\"0.9.0\",optional=true}\nrand = { version = \"0.8.4\", optional = false }\nargon2 = { version = \"0.4.0\", optional = true }\nprometheus = { version = \"0.13.0\", optional = true, default-features = false }\nprometheus-metric-storage = { version = \"0.5.0\", optional = true }\nflate2 = {version = \"1\", optional = true}\n\n# Rust 1.30.1 compat:\n#cfg-if=\"=0.1.9\"\n#unicode-width=\"=0.1.5\"\n#tokio-reactor = \"=0.1.9\"\n#cc=\"=1.0.41\"\n\n\n[target.'cfg(unix)'.dependencies]\ntokio-file-unix = \"0.5.1\"\ntokio-signal = { version = \"0.2.7\", optional = true }\ntokio-uds = \"0.2.3\"\nlibc = { version = \"0.2\" }\n\n[target.'cfg(windows)'.dependencies]\ntokio-named-pipes = {version=\"0.1.0\", optional=true}\n\n[features]\ndefault = [\"signal_handler\", \"tokio-process\", \"unix_stdio\", \"windows_named_pipes\", \"ssl\", \"compression\"]\nunix_stdio = []\nssl = [\"websocket/async-ssl\", \"tokio-tls\", \"native-tls\", \"readwrite\", \"openssl-sys\"]\nsignal_handler = [\"tokio-signal\"]\nworkaround1=[]\nseqpacket=[]\nwindows_named_pipes=[\"tokio-named-pipes\"]\nvendored_openssl = [\"openssl-sys/vendored\"]\ncrypto_peer = [\"chacha20poly1305\",\"argon2\"]\nprometheus_peer=[\"prometheus\",\"prometheus-metric-storage\"]\ncompression=[\"flate2\"]\n\n[target.'arm-linux-androideabi'.dependencies]\nopenssl-sys = { version=\"0.9\", features=[], optional=true }\n\n\n#[patch.\"crates-io\"]\n#\"tokio-process:0.1.6\" = {path=\"/mnt/src/git/tokio-process\"}\n#\"tokio-core:0.1.17\" = {path=\"/mnt/src/git/tokio-core\"}\n#\"websocket\" = {path=\"/home/vi/src/rust-websocket\"}\n#\"websocket-base\" = {path=\"/home/vi/src/rust-websocket/websocket-base\"}\n\n\n"
  },
  {
    "path": "Dockerfile",
    "content": "# Build stage\nFROM rust:1.87.0-alpine3.20 AS cargo-build\n\nRUN apk add --no-cache musl-dev pkgconfig openssl-dev\n\nWORKDIR /src/websocat\nENV RUSTFLAGS='-Ctarget-feature=-crt-static'\n\nCOPY Cargo.toml Cargo.toml\nARG CARGO_OPTS=\"--features=workaround1,seqpacket,prometheus_peer,prometheus/process,crypto_peer\"\n\nRUN mkdir src/ &&\\\n    echo \"fn main() {println!(\\\"if you see this, the build broke\\\")}\" > src/main.rs && \\\n    cargo build --release $CARGO_OPTS && \\\n    rm -f target/release/deps/websocat*\n\nCOPY src src\nRUN cargo build --release $CARGO_OPTS && \\\n    strip target/release/websocat\n\n# Final stage\nFROM alpine:3.20\n\nRUN apk add --no-cache libgcc\n\nWORKDIR /\nCOPY --from=cargo-build /src/websocat/target/release/websocat /usr/local/bin/\n\nENTRYPOINT [\"/usr/local/bin/websocat\"]\n"
  },
  {
    "path": "Dockerfile.debian",
    "content": "# Build stage\nFROM rust:1.87.0-bookworm AS cargo-build\n\nRUN apt-get update\nRUN apt-get install -y libgcc-12-dev libc6-dev pkg-config libssl-dev\n\nWORKDIR /src/websocat\nENV RUSTFLAGS='-Ctarget-feature=-crt-static'\n\nCOPY Cargo.toml Cargo.toml\n# ARG CARGO_OPTS=\"--features=workaround1,seqpacket,prometheus_peer,prometheus/process,crypto_peer\"\nARG CARGO_OPTS=\"--features=ssl,workaround1,seqpacket,unix_stdio,prometheus_peer,prometheus/process,crypto_peer\"\n\nRUN mkdir src/ &&\\\n    echo \"fn main() {println!(\\\"if you see this, the build broke\\\")}\" > src/main.rs && \\\n    cargo build --release $CARGO_OPTS && \\\n    rm -f target/release/deps/websocat*\n\nCOPY src src\nRUN cargo build --release $CARGO_OPTS && \\\n    strip target/release/websocat\n\n# Final stage\nFROM debian:bookworm\n\nRUN apt-get update\nRUN apt-get install -y libssl3\nRUN apt-get clean && \\\n    rm -rf /var/lib/apt/lists/*\n\nWORKDIR /\nCOPY --from=cargo-build /src/websocat/target/release/websocat /usr/local/bin/\n\nENTRYPOINT [\"/usr/local/bin/websocat\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2016 Vitaly Shukela\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": "README.md",
    "content": "# websocat\nNetcat, curl and socat for [WebSockets](https://en.wikipedia.org/wiki/WebSocket).\n\n[![Gitter](https://badges.gitter.im/websocat.svg)](https://gitter.im/websocat/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge)\n[![image-build](https://github.com/vi/websocat/actions/workflows/container-image-buildah.yml/badge.svg)](https://github.com/vi/websocat/pkgs/container/websocat)\n\n## Examples\n\n### Connect to public echo server (Ctrl+d to quit)\n\n```\n$ websocat ws://ws.vi-server.org/mirror\n123\n123\nABC\nABC\n\n$ docker run --rm -ti ghcr.io/vi/websocat:nightly wss://ws.vi-server.org/mirror\n123\n123\nABC\nABC\n```\n\n### Serve and connect\n\n```\nA$ websocat -s 1234\nListening on ws://127.0.0.1:1234/\nABC\n123\n\nB$ websocat ws://127.0.0.1:1234/\nABC\n123\n```\n\n### Open a tab in Chromium using remote debugging.\n\n```\n$ chromium --remote-debugging-port=9222&\n$ curl -sg http://127.0.0.1:9222/json/new | grep webSocketDebuggerUrl | cut -d'\"' -f4 | head -1\nws://127.0.0.1:9222/devtools/page/A331E56CCB8615EB4FCB720425A82259\n$ echo 'Page.navigate {\"url\":\"https://example.com\"}' | websocat -n1 --jsonrpc --jsonrpc-omit-jsonrpc ws://127.0.0.1:9222/devtools/page/A331E56CCB8615EB4FCB720425A82259\n{\"id\":2,\"result\":{\"frameId\":\"A331E56CCB8615EB4FCB720425A82259\",\"loaderId\":\"EF5AAD19F2F8BB27FAF55F94FFB27DF9\"}}\n```\n\n\n### Proxy TCP connections to WebSocket connections and back.\n\n```\n$ websocat --oneshot -b ws-l:127.0.0.1:1234 tcp:127.0.0.1:22&\n$ websocat --oneshot -b tcp-l:127.0.0.1:1236 ws://127.0.0.1:1234/&\n$ nc 127.0.0.1 1236\nSSH-2.0-OpenSSH_7.4p1 Debian-10+deb9u3\nqwertyu\nProtocol mismatch.\n```\n\n\n### Broadcast all messages between connected WebSocket clients\n\n```\nA$ websocat -t ws-l:127.0.0.1:1234 broadcast:mirror:\nB$ websocat ws://127.0.0.1:1234\nC$ websocat ws://127.0.0.1:1234\n```\n\n(if you like this one, you may actually want https://github.com/vi/wsbroad/ instead)\n\nSee [moreexamples.md](./moreexamples.md) for further examples.\n\n## Features\n\n* Connecting to and serving WebSockets from command line.\n* Executing external program and making it communicate to WebSocket using stdin/stdout.\n* Text and binary modes, converting between lines (or null-terminated records) and messages.\n* Inetd mode, UNIX sockets (including abstract namespaced on Linux).\n* Integration with Nginx using TCP or UNIX sockets.\n* Directly using unauthenticated SOCKS5 servers for connecting to WebSockets and listening WebSocket connection.\n* Auto-reconnect and connection-reuse modes.\n* Linux, Windows and Mac support, with [pre-built executables][releases].\n* Low-level WebSocket clients and servers with overridable underlying transport connection, e.g. calling external program to serve as a transport for websocat (for SSL, proxying, etc.).\n\n[releases]:https://github.com/vi/websocat/releases\n\n# Installation\n\nThere are multiple options for installing WebSocat. From easy to hard:\n\n## FreeBSD\n\n`pkg install websocat`\n\n## Debian / Ubuntu\n* Download a pre-build executable from [GitHub releases][releases].\n* Websocat is not yet officially packaged for Debian. Some older versions of Websocat may also have Debian package files available on Github releases.\n\n## macOS\n\n[Homebrew](https://brew.sh): `brew install websocat`\n\n[MacPorts](https://www.macports.org): `sudo port install websocat`\n\n## From source\n* Install the [Rust toolchain](https://rustup.rs/) and do `cargo install websocat`. If something fails with a `-sys` crate, try with `--no-default-features`;\n* Build Websocat from source code (see below), then move `target/release/websocat` somewhere to the PATH.\n\n## Pre-built binaries\nPre-built binaries for Linux (usual and musl), Windows, OS X and Android are available on the [releases page](https://github.com/vi/websocat/releases).\n\n\nBuilding from source code\n---\n\n1. Install the [Rust toolchain](https://rustup.rs/). Note that Websocat v1 (i.e. the current stable version) may fail to support too new Rust due to its old dependencies which can be broken by e.g. [this](https://github.com/rust-lang/rust/pull/78802).\n2. `cargo build --release`.\n3. Find the executable somewhere under `target/`, e.g. in `target/release/websocat`.\n\n### Rust versions\n\n\n|Websocat versions|Minimal Rust version|Maximal Rust version|\n|----|----|----|\n| 4.0.0-alpha1 | 1.84 | ?   |\n| 1.12+     | ?    | ?          |\n| 1.9 - 1.11| 1.46 | maybe 1.63 |\n| 1.6 - 1.8 | 1.34 | maybe 1.63  |\n| 1.3 - 1.5 | 1.31 | 1.47? |\n| 1.2       | 1.28 | 1.47? |\n| 1.0-1.1   | 1.23 | 1.47? |\n| 1.2       | ?    | ?     |\n\nNote that building with legacy Rust version (e.g. 1.46) may require manually copying `Cargo.lock.legacy` to `Cargo.lock` prior to the building.\n\nEarly non-async versions of Websocat should be buildable by even older rustc.  \n\nNote that old versions of Websocat may misbehave if built by Rust 1.48 or later due to https://github.com/rust-lang/rust/pull/71274/.\n\nIt may be not a good idea to build v1.x line of Websocat with Rust 1.64 due to [IP address representation refactor]. It may expose previously hidden undefined behaviour in legacy dependencies. (In practice, it seems to just work though - but a lot of time passed since I checked Websocat properly and in detail).\n\n[ipaddr]:https://github.com/rust-lang/rust/pull/78802\n\n\nSSL on Android\n---\n\nwebsocat's `wss://` may fail on Android. As a workaround, download certificate bundle, for example, from https://curl.haxx.se/ca/cacert.pem and specify it explicitly:\n\n    SSL_CERT_FILE=cacert.pem /data/local/tmp/websocat wss://echo.websocket.org\n\nOr just use `--insecure` option.\n\nDocumentation\n---\n\nBasic usage examples are provided at the top of this README and in `--help` message. More tricks are described in [moreexamples.md](./moreexamples.md).\n\nThere is a [list of all address types and overlays](doc.md).\n\n<details><summary>`websocat --help=long` output</summary>\n\n```\nwebsocat 1.13.0\nVitaly \"_Vi\" Shukela <vi0oss@gmail.com>\nCommand-line client for web sockets, like netcat/curl/socat for ws://.\n\nUSAGE:\n    websocat ws://URL | wss://URL               (simple client)\n    websocat -s port                            (simple server)\n    websocat [FLAGS] [OPTIONS] <addr1> <addr2>  (advanced mode)\n\nFLAGS:\n        --stdout-announce-listening-ports        [A] Print a line to stdout for each port being listened\n        --async-stdio\n            [A] On UNIX, set stdin and stdout to nonblocking mode instead of spawning a thread. This should improve\n            performance, but may break other programs running on the same console.\n        --compress-deflate\n            [A] Compress data coming to a WebSocket using deflate method. Affects only binary WebSocket messages.\n\n        --compress-gzip\n            [A] Compress data coming to a WebSocket using gzip method. Affects only binary WebSocket messages.\n\n        --compress-zlib\n            [A] Compress data coming to a WebSocket using zlib method. Affects only binary WebSocket messages.\n\n        --crypto-reverse\n            [A] Swap encryption and decryption operations in `crypto:` specifier - encrypt on read, decrypto on write.\n\n        --dump-spec                              [A] Instead of running, dump the specifiers representation to stdout\n    -e, --set-environment\n            Set WEBSOCAT_* environment variables when doing exec:/cmd:/sh-c:\n            Currently it's WEBSOCAT_URI and WEBSOCAT_CLIENT for\n            request URI and client address (if TCP)\n            Beware of ShellShock or similar security problems.\n    -E, --exit-on-eof                            Close a data transfer direction if the other one reached EOF\n        --foreachmsg-wait-read                   [A] Wait for reading to finish before closing foreachmsg:'s peer\n        --jsonrpc\n            Format messages you type as JSON RPC 2.0 method calls. First word becomes method name, the rest becomes\n            parameters, possibly automatically wrapped in [].\n        --jsonrpc-omit-jsonrpc                   [A] Omit `jsonrpc` field when using `--jsonrpc`, e.g. for Chromium\n        --just-generate-key                      [A] Just a Sec-WebSocket-Key value without running main Websocat\n        --lengthprefixed-little-endian\n            [A] Use little-endian framing headers instead of big-endian for `lengthprefixed:` overlay.\n\n        --lengthprefixed-skip-read-direction\n            [A] Only affect one direction of the `lengthprefixed:` overlay, bypass tranformation for the other one.\n\n        --lengthprefixed-skip-write-direction\n            [A] Only affect one direction of the `lengthprefixed:` overlay, bypass tranformation for the other one.\n\n        --linemode-strip-newlines\n            [A] Don't include trailing \\n or \\r\\n coming from streams in WebSocket messages\n\n    -0, --null-terminated                        Use \\0 instead of \\n for linemode\n        --no-line                                [A] Don't automatically insert line-to-message transformation\n        --no-exit-on-zeromsg\n            [A] Don't exit when encountered a zero message. Zero messages are used internally in Websocat, so it may\n            fail to close connection at all.\n        --no-fixups\n            [A] Don't perform automatic command-line fixups. May destabilize websocat operation. Use --dump-spec without\n            --no-fixups to discover what is being inserted automatically and read the full manual about Websocat\n            internal workings.\n        --no-async-stdio                         [A] Inhibit using stdin/stdout in a nonblocking way if it is not a tty\n    -1, --one-message                            Send and/or receive only one message. Use with --no-close and/or -u/-U.\n        --oneshot                                Serve only once. Not to be confused with -1 (--one-message)\n        --print-ping-rtts\n            Print measured round-trip-time to stderr after each received WebSocket pong.\n\n        --exec-exit-on-disconnect\n            [A] Make exec: or sh-c: or cmd: immediately exit when connection is closed, don't wait for termination.\n\n        --exec-sighup-on-stdin-close\n            [A] Make exec: or sh-c: or cmd: send SIGHUP on UNIX when input is closed.\n\n        --exec-sighup-on-zero-msg\n            [A] Make exec: or sh-c: or cmd: send SIGHUP on UNIX when facing incoming zero-length message.\n\n    -q                                           Suppress all diagnostic messages, except of startup errors\n        --reuser-send-zero-msg-on-disconnect\n            [A] Make reuse-raw: send a zero-length message to the peer when some clients disconnects.\n\n    -s, --server-mode                            Simple server mode: specify TCP port or addr:port as single argument\n    -S, --strict\n            strict line/message mode: drop too long messages instead of splitting them, drop incomplete lines.\n\n        --timestamp-monotonic                    [A] Use monotonic clock for `timestamp:` overlay\n    -k, --insecure                               Accept invalid certificates and hostnames while connecting to TLS\n        --udp-broadcast                          [A] Set SO_BROADCAST\n        --udp-multicast-loop                     [A] Set IP[V6]_MULTICAST_LOOP\n        --udp-oneshot                            [A] udp-listen: replies only one packet per client\n        --udp-reuseaddr\n            [A] Set SO_REUSEADDR for UDP socket. Listening TCP sockets are always reuseaddr.\n\n        --uncompress-deflate\n            [A] Uncompress data coming from a WebSocket using deflate method. Affects only binary WebSocket messages.\n\n        --uncompress-gzip\n            [A] Uncompress data coming from a WebSocket using deflate method. Affects only binary WebSocket messages.\n\n        --uncompress-zlib\n            [A] Uncompress data coming from a WebSocket using deflate method. Affects only binary WebSocket messages.\n\n    -u, --unidirectional                         Inhibit copying data in one direction\n    -U, --unidirectional-reverse\n            Inhibit copying data in the other direction (or maybe in both directions if combined with -u)\n\n        --accept-from-fd\n            [A] Do not call `socket(2)` in UNIX socket listener peer, start with `accept(2)` using specified file\n            descriptor number as argument instead of filename\n        --unlink                                 [A] Unlink listening UNIX socket before binding to it\n    -V, --version                                Prints version information\n    -v                                           Increase verbosity level to info or further\n    -b, --binary                                 Send message to WebSockets as binary messages\n    -n, --no-close                               Don't send Close message to websocket on EOF\n        --websocket-ignore-zeromsg\n            [A] Silently drop incoming zero-length WebSocket messages. They may cause connection close due to usage of\n            zero-len message as EOF flag inside Websocat.\n    -t, --text                                   Send message to WebSockets as text messages\n        --base64\n            Encode incoming binary WebSocket messages in one-line Base64 If `--binary-prefix` (see `--help=full`) is\n            set, outgoing WebSocket messages that start with the prefix are decoded from base64 prior to sending.\n        --base64-text\n            [A] Encode incoming text WebSocket messages in one-line Base64. I don't know whether it can be ever useful,\n            but it's for symmetry with `--base64`.\n\nOPTIONS:\n        --socks5 <auto_socks5>\n            Use specified address:port as a SOCKS5 proxy. Note that proxy authentication is not supported yet. Example:\n            --socks5 127.0.0.1:9050\n        --autoreconnect-delay-millis <autoreconnect_delay_millis>\n            [A] Delay before reconnect attempt for `autoreconnect:` overlay. [default: 20]\n\n        --basic-auth <basic_auth>\n            Add `Authorization: Basic` HTTP request header with this base64-encoded parameter\n\n        --queue-len <broadcast_queue_len>\n            [A] Number of pending queued messages for broadcast reuser [default: 16]\n\n    -B, --buffer-size <buffer_size>                                  Maximum message size, in bytes [default: 65536]\n        --byte-to-exit-on <byte_to_exit_on>\n            [A] Override the byte which byte_to_exit_on: overlay looks for [default: 28]\n\n        --client-pkcs12-der <client_pkcs12_der>                      [A] Client identity TLS certificate\n        --client-pkcs12-passwd <client_pkcs12_passwd>\n            [A] Password for --client-pkcs12-der pkcs12 archive. Required on Mac.\n\n        --close-reason <close_reason>\n            Close connection with a reason message. This option only takes effect if --close-status-code option is\n            provided as well.\n        --close-status-code <close_status_code>                      Close connection with a status code.\n        --crypto-key <crypto_key>\n            [A] Specify encryption/decryption key for `crypto:` specifier. Requires `base64:`, `file:` or `pwd:` prefix.\n\n    -H, --header <custom_headers>...\n            Add custom HTTP header to websocket client request. Separate header name and value with a colon and\n            optionally a single space. Can be used multiple times. Note that single -H may eat multiple further\n            arguments, leading to confusing errors. Specify headers at the end or with equal sign like -H='X: y'.\n        --server-header <custom_reply_headers>...\n            Add custom HTTP header to websocket upgrade reply. Separate header name and value with a colon and\n            optionally a single space. Can be used multiple times. Note that single -H may eat multiple further\n            arguments, leading to confusing errors.\n        --exec-args <exec_args>...\n            [A] Arguments for the `exec:` specifier. Must be the last option, everything after it gets into the exec\n            args list.\n        --header-to-env <headers_to_env>...\n            Forward specified incoming request header to H_* environment variable for `exec:`-like specifiers.\n\n    -h, --help <help>\n            See the help.\n            --help=short is the list of easy options and address types\n            --help=long lists all options and types (see [A] markers)\n            --help=doc also shows longer description and examples.\n        --inhibit-pongs <inhibit_pongs>\n            [A] Stop replying to incoming WebSocket pings after specified number of replies\n\n        --just-generate-accept <just_generate_accept>\n            [A] Just a Sec-WebSocket-Accept value based on supplied Sec-WebSocket-Key value without running main\n            Websocat\n        --lengthprefixed-nbytes <lengthprefixed_header_bytes>\n            [A] Use this number of length header bytes for `lengthprefixed:` overlay. [default: 4]\n\n        --max-messages <max_messages>\n            Maximum number of messages to copy in one direction.\n\n        --max-messages-rev <max_messages_rev>\n            Maximum number of messages to copy in the other direction.\n\n        --conncap <max_parallel_conns>\n            Maximum number of simultaneous connections for listening mode\n\n        --max-sent-pings <max_sent_pings>\n            [A] Stop sending pings after this number of sent pings\n\n        --max-ws-frame-length <max_ws_frame_length>\n            [A] Maximum size of incoming WebSocket frames, to prevent memory overflow [default: 104857600]\n\n        --max-ws-message-length <max_ws_message_length>\n            [A] Maximum size of incoming WebSocket messages (sans of one data frame), to prevent memory overflow\n            [default: 209715200]\n        --origin <origin>                                            Add Origin HTTP header to websocket client request\n        --pkcs12-der <pkcs12_der>\n            Pkcs12 archive needed to accept SSL connections, certificate and key.\n            A command to output it: openssl pkcs12 -export -out output.pkcs12 -inkey key.pem -in cert.pem\n            Use with -s (--server-mode) option or with manually specified TLS overlays.\n            See moreexamples.md for more info.\n        --pkcs12-passwd <pkcs12_passwd>\n            Password for --pkcs12-der pkcs12 archive. Required on Mac.\n\n    -p, --preamble <preamble>...\n            Prepend copied data with a specified string. Can be specified multiple times.\n\n    -P, --preamble-reverse <preamble_reverse>...\n            Prepend copied data with a specified string (reverse direction). Can be specified multiple times.\n\n        --prometheus <prometheus>\n            Expose Prometheus metrics on specified IP address and port in addition to running usual Websocat session\n\n        --request-header <request_headers>...\n            [A] Specify HTTP request headers for `http-request:` specifier.\n\n    -X, --request-method <request_method>                            [A] Method to use for `http-request:` specifier\n        --request-uri <request_uri>                                  [A] URI to use for `http-request:` specifier\n        --restrict-uri <restrict_uri>\n            When serving a websocket, only accept the given URI, like `/ws`\n            This liberates other URIs for things like serving static files or proxying.\n    -F, --static-file <serve_static_files>...\n            Serve a named static file for non-websocket connections.\n            Argument syntax: <URI>:<Content-Type>:<file-path>\n            Argument example: /index.html:text/html:index.html\n            Directories are not and will not be supported for security reasons.\n            Can be specified multiple times. Recommended to specify them at the end or with equal sign like `-F=...`,\n            otherwise this option may eat positional arguments\n        --socks5-bind-script <socks5_bind_script>\n            [A] Execute specified script in `socks5-bind:` mode when remote port number becomes known.\n\n        --socks5-destination <socks_destination>\n            [A] Examples: 1.2.3.4:5678  2600:::80  hostname:5678\n\n        --tls-domain <tls_domain>\n            [A] Specify domain for SNI or certificate verification when using tls-connect: overlay\n\n        --udp-multicast <udp_join_multicast_addr>...\n            [A] Issue IP[V6]_ADD_MEMBERSHIP for specified multicast address. Can be specified multiple times.\n\n        --udp-multicast-iface-v4 <udp_join_multicast_iface_v4>...\n            [A] IPv4 address of multicast network interface. Has to be either not specified or specified the same number\n            of times as multicast IPv4 addresses. Order matters.\n        --udp-multicast-iface-v6 <udp_join_multicast_iface_v6>...\n            [A] Index of network interface for IPv6 multicast. Has to be either not specified or specified the same\n            number of times as multicast IPv6 addresses. Order matters.\n        --udp-ttl <udp_ttl>                                          [A] Set IP_TTL, also IP_MULTICAST_TTL if applicable\n        --protocol <websocket_protocol>\n            Specify this Sec-WebSocket-Protocol: header when connecting\n\n        --server-protocol <websocket_reply_protocol>\n            Force this Sec-WebSocket-Protocol: header when accepting a connection\n\n        --websocket-version <websocket_version>                      Override the Sec-WebSocket-Version value\n        --binary-prefix <ws_binary_prefix>\n            [A] Prepend specified text to each received WebSocket binary message. Also strip this prefix from outgoing\n            messages, explicitly marking them as binary even if `--text` is specified\n        --ws-c-uri <ws_c_uri>\n            [A] URI to use for ws-c: overlay [default: ws://0.0.0.0/]\n\n        --ping-interval <ws_ping_interval>                           Send WebSocket pings each this number of seconds\n        --ping-timeout <ws_ping_timeout>\n            Drop WebSocket connection if Pong message not received for this number of seconds\n\n        --text-prefix <ws_text_prefix>\n            [A] Prepend specified text to each received WebSocket text message. Also strip this prefix from outgoing\n            messages, explicitly marking them as text even if `--binary` is specified\n\nARGS:\n    <addr1>    In simple mode, WebSocket URL to connect. In advanced mode first address (there are many kinds of\n               addresses) to use. See --help=types for info about address types. If this is an address for\n               listening, it will try serving multiple connections.\n    <addr2>    In advanced mode, second address to connect. If this is an address for listening, it will accept only\n               one connection.\n\n\nBasic examples:\n  Command-line websocket client:\n    websocat ws://ws.vi-server.org/mirror/\n    \n  WebSocket server\n    websocat -s 8080\n    \n  WebSocket-to-TCP proxy:\n    websocat --binary ws-l:127.0.0.1:8080 tcp:127.0.0.1:5678\n    \n\nFull list of address types:\n\tws://           \tInsecure (ws://) WebSocket client. Argument is host and URL.\n\twss://          \tSecure (wss://) WebSocket client. Argument is host and URL.\n\tws-listen:      \tWebSocket server. Argument is host and port to listen.\n\tinetd-ws:       \tWebSocket inetd server. [A]\n\tl-ws-unix:      \tWebSocket UNIX socket-based server. [A]\n\tl-ws-abstract:  \tWebSocket abstract-namespaced UNIX socket server. [A]\n\tws-lowlevel-client:\t[A] Low-level HTTP-independent WebSocket client connection without associated HTTP upgrade.\n\tws-lowlevel-server:\t[A] Low-level HTTP-independent WebSocket server connection without associated HTTP upgrade.\n\twss-listen:     \tListen for secure WebSocket connections on a TCP port\n\thttp:           \t[A] Issue HTTP request, receive a 1xx or 2xx reply, then pass\n\tasyncstdio:     \t[A] Set stdin and stdout to nonblocking mode, then use it as a communication counterpart. UNIX-only.\n\tinetd:          \tLike `asyncstdio:`, but intended for inetd(8) usage. [A]\n\ttcp:            \tConnect to specified TCP host and port. Argument is a socket address.\n\ttcp-listen:     \tListen TCP port on specified address.\n\tssl-listen:     \tListen for SSL connections on a TCP port\n\tsh-c:           \tStart specified command line using `sh -c` (even on Windows)\n\tcmd:            \tStart specified command line using `sh -c` or `cmd /C` (depending on platform)\n\texec:           \tExecute a program directly (without a subshell), providing array of arguments on Unix [A]\n\treadfile:       \tSynchronously read a file. Argument is a file path.\n\twritefile:      \tSynchronously truncate and write a file.\n\tappendfile:     \tSynchronously append a file.\n\tudp:            \tSend and receive packets to specified UDP socket, from random UDP port  \n\tudp-listen:     \tBind an UDP socket to specified host:port, receive packet\n\topen-async:     \tOpen file for read and write and use it like a socket. [A]\n\topen-fd:        \tUse specified file descriptor like a socket. [A]\n\tthreadedstdio:  \t[A] Stdin/stdout, spawning a thread (threaded version).\n\t-               \tRead input from console, print to console. Uses threaded implementation even on UNIX unless requested by `--async-stdio` CLI option.\n\tunix:           \tConnect to UNIX socket. Argument is filesystem path. [A]\n\tunix-listen:    \tListen for connections on a specified UNIX socket [A]\n\tunix-dgram:     \tSend packets to one path, receive from the other. [A]\n\tabstract:       \tConnect to UNIX abstract-namespaced socket. Argument is some string used as address. [A]\n\tabstract-listen:\tListen for connections on a specified abstract UNIX socket [A]\n\tabstract-dgram: \tSend packets to one address, receive from the other. [A]\n\tmirror:         \tSimply copy output to input. No arguments needed.\n\tliteralreply:   \tReply with a specified string for each input packet.\n\tclogged:        \tDo nothing. Don't read or write any bytes. Keep connections in \"hung\" state. [A]\n\tliteral:        \tOutput a string, discard input.\n\tassert:         \tCheck the input.  [A]\n\tassert2:        \tCheck the input. [A]\n\tseqpacket:      \tConnect to AF_UNIX SOCK_SEQPACKET socket. Argument is a filesystem path. [A]\n\tseqpacket-listen:\tListen for connections on a specified AF_UNIX SOCK_SEQPACKET socket [A]\n\trandom:         \tGenerate random bytes when being read from, discard written bytes.\nFull list of overlays:\n\tws-upgrade:     \tWebSocket upgrader / raw server. Specify your own protocol instead of usual TCP. [A]\n\thttp-request:   \t[A] Issue HTTP request, receive a 1xx or 2xx reply, then pass\n\thttp-post-sse:  \t[A] Accept HTTP/1 request. Then, if it is GET,\n\tssl-connect:    \tOverlay to add TLS encryption atop of existing connection [A]\n\tssl-accept:     \tAccept an TLS connection using arbitrary backing stream. [A]\n\treuse-raw:      \tReuse subspecifier for serving multiple clients: unpredictable mode. [A]\n\tbroadcast:      \tReuse this connection for serving multiple clients, sending replies to all clients.\n\tautoreconnect:  \tRe-establish underlying connection on any error or EOF\n\tws-c:           \tLow-level WebSocket connector. Argument is a some another address. [A]\n\tmsg2line:       \tLine filter: Turns messages from packet stream into lines of byte stream. [A]\n\tline2msg:       \tLine filter: turn lines from byte stream into messages as delimited by '\\\\n' or '\\\\0' [A]\n\tlengthprefixed: \tTurn stream of bytes to/from data packets with length-prefixed framing.  [A]\n\tforeachmsg:     \tExecute something for each incoming message.\n\tlog:            \tLog each buffer as it pass though the underlying connector.\n\tjsonrpc:        \t[A] Turns messages like `abc 1,2` into `{\"jsonrpc\":\"2.0\",\"id\":412, \"method\":\"abc\", \"params\":[1,2]}`.\n\ttimestamp:      \t[A] Prepend timestamp to each incoming message.\n\tsocks5-connect: \tSOCKS5 proxy client (raw) [A]\n\tsocks5-bind:    \tSOCKS5 proxy client (raw, bind command) [A]\n\tcrypto:         \t[A] Encrypts written messages and decrypts (and verifies) read messages with a static key, using ChaCha20-Poly1305 algorithm.\n\tprometheus:     \t[A] Account connections, messages, bytes and other data and expose Prometheus metrics on a separate port.\n\texit_on_specific_byte:\t[A] Turn specific byte into a EOF, allowing user to escape interactive Websocat session\n\twaitfordata:    \tWait for some data to pending being written before starting connecting. [A]\n```\n</details>\n\n\nSome notes\n---\n\n* IPv6 is supported, surround your IP in square brackets or use it as is, depending on context.\n* Web socket usage is not obligatory, you can use any specs on both sides.\n* Typically one line in binary stream correspond to one WebSocket text message. This is adjustable with options.\n\nLimitations\n---\n\n* It only connects (or serves) HTTP/1. [RFC 8441](https://www.rfc-editor.org/rfc/rfc8441) or [RFC 9220](https://www.rfc-editor.org/rfc/rfc9220.html) are not currently supported.\n* It is not convenient when text and binary WebSocket messages are mixed. This affects `mirror:` specifier, making it a bit different from ws://echo.websocket.org. There are `--binary-prefix`, `--text-prefix` and `--base64` options to handle mixture of binary and text.\n* Current version of Websocat don't receive notification about closed sockets. This makes serving without `-E` or `-u` options or in backpressure scenarios prone to socket leak.\n* Readline is not integrated. Users are advices to wrap websocat using [`rlwrap`](https://linux.die.net/man/1/rlwrap) tool for more convenient CLI.\n* Build process of current version of Websocat is not properly automated and is fragile.\n* Main version (v1) is based on obsolete dependency versions that trigger security warnings and may become tricky to build. There is [new version (v4)](https://github.com/vi/websocat/tree/websocat4), but is not yet considered stable and [has many missing features](https://github.com/vi/websocat/issues/276).\n\nBindings\n---\n\n* Node.js: [`seamapi/node-websocat`](https://github.com/seamapi/node-websocat)\n\nSee also\n---\n\n* [wstunnel](https://github.com/erebe/wstunnel)\n* [wscat](https://github.com/websockets/wscat)\n* [websocketd](https://github.com/joewalnes/websocketd)\n* [wsd](https://github.com/alexanderGugel/wsd)\n"
  },
  {
    "path": "doc.md",
    "content": "\n# Websocat Reference (in progress)\n\nWebsocat has many command-line options and special format for positional arguments.\n\nThere are three main modes of websocat invocation:\n\n* Simple client mode: `websocat wss://your.server/url`\n* Simple server mode: `websocat -s 127.0.0.1:8080`\n* Advanced [socat][1]-like mode: `websocat -t ws-l:127.0.0.1:8080 mirror:`\n\nUltimately in any of those modes websocat creates two connections and exchanges data between them.\nIf one of the connections is bytestream-oriented (for example the terminal stdin/stdout or a TCP connection), but the other is message-oriented (for example, a WebSocket or UDP) then websocat operates in lines: each line correspond to a message. Details of this are configurable by various options.\n\n`ws-l:` or `mirror:` above are examples of address types. With the exception of special cases like WebSocket URL `ws://1.2.3.4/` or stdio `-`, websocat's positional argument is defined by this rule:\n\n```\n<specifier> ::= ( <overlay> \":\" )* <addrtype> \":\" [address]\n```\n\nSome address types may be \"aliases\" to other address types or combinations of overlays and address types.\n\n[1]:http://www.dest-unreach.org/socat/doc/socat.html\n\n# `--help=long`\n\n\"Advanced\" options and flags are denoted by `[A]` marker.\n\n\n```\n\nwebsocat 1.14.0\nVitaly \"_Vi\" Shukela <vi0oss@gmail.com>\nCommand-line client for web sockets, like netcat/curl/socat for ws://.\n\nUSAGE:\n    websocat ws://URL | wss://URL               (simple client)\n    websocat -s port                            (simple server)\n    websocat [FLAGS] [OPTIONS] <addr1> <addr2>  (advanced mode)\n\nFLAGS:\n        --stdout-announce-listening-ports        [A] Print a line to stdout for each port being listened\n        --async-stdio\n            [A] On UNIX, set stdin and stdout to nonblocking mode instead of spawning a thread. This should improve\n            performance, but may break other programs running on the same console.\n        --compress-deflate\n            [A] Compress data coming to a WebSocket using deflate method. Affects only binary WebSocket messages.\n\n        --compress-gzip\n            [A] Compress data coming to a WebSocket using gzip method. Affects only binary WebSocket messages.\n\n        --compress-zlib\n            [A] Compress data coming to a WebSocket using zlib method. Affects only binary WebSocket messages.\n\n        --crypto-reverse\n            [A] Swap encryption and decryption operations in `crypto:` specifier - encrypt on read, decrypto on write.\n\n        --dump-spec                              [A] Instead of running, dump the specifiers representation to stdout\n    -e, --set-environment\n            Set WEBSOCAT_* environment variables when doing exec:/cmd:/sh-c:\n            Currently it's WEBSOCAT_URI and WEBSOCAT_CLIENT for\n            request URI and client address (if TCP)\n            Beware of ShellShock or similar security problems.\n    -E, --exit-on-eof                            Close a data transfer direction if the other one reached EOF\n        --foreachmsg-wait-read                   [A] Wait for reading to finish before closing foreachmsg:'s peer\n        --jsonrpc\n            Format messages you type as JSON RPC 2.0 method calls. First word becomes method name, the rest becomes\n            parameters, possibly automatically wrapped in [].\n        --jsonrpc-omit-jsonrpc                   [A] Omit `jsonrpc` field when using `--jsonrpc`, e.g. for Chromium\n        --just-generate-key                      [A] Just a Sec-WebSocket-Key value without running main Websocat\n        --lengthprefixed-little-endian\n            [A] Use little-endian framing headers instead of big-endian for `lengthprefixed:` overlay.\n\n        --lengthprefixed-skip-read-direction\n            [A] Only affect one direction of the `lengthprefixed:` overlay, bypass tranformation for the other one.\n\n        --lengthprefixed-skip-write-direction\n            [A] Only affect one direction of the `lengthprefixed:` overlay, bypass tranformation for the other one.\n\n        --linemode-strip-newlines\n            [A] Don't include trailing \\n or \\r\\n coming from streams in WebSocket messages\n\n    -0, --null-terminated                        Use \\0 instead of \\n for linemode\n        --no-line                                [A] Don't automatically insert line-to-message transformation\n        --no-exit-on-zeromsg\n            [A] Don't exit when encountered a zero message. Zero messages are used internally in Websocat, so it may\n            fail to close connection at all.\n        --no-fixups\n            [A] Don't perform automatic command-line fixups. May destabilize websocat operation. Use --dump-spec without\n            --no-fixups to discover what is being inserted automatically and read the full manual about Websocat\n            internal workings.\n        --no-async-stdio                         [A] Inhibit using stdin/stdout in a nonblocking way if it is not a tty\n    -1, --one-message                            Send and/or receive only one message. Use with --no-close and/or -u/-U.\n        --oneshot                                Serve only once. Not to be confused with -1 (--one-message)\n        --print-ping-rtts\n            Print measured round-trip-time to stderr after each received WebSocket pong.\n\n        --exec-exit-on-disconnect\n            [A] Make exec: or sh-c: or cmd: immediately exit when connection is closed, don't wait for termination.\n\n        --exec-sighup-on-stdin-close\n            [A] Make exec: or sh-c: or cmd: send SIGHUP on UNIX when input is closed.\n\n        --exec-sighup-on-zero-msg\n            [A] Make exec: or sh-c: or cmd: send SIGHUP on UNIX when facing incoming zero-length message.\n\n    -q                                           Suppress all diagnostic messages, except of startup errors\n        --reuser-send-zero-msg-on-disconnect\n            [A] Make reuse-raw: send a zero-length message to the peer when some clients disconnects.\n\n    -s, --server-mode                            Simple server mode: specify TCP port or addr:port as single argument\n    -S, --strict\n            strict line/message mode: drop too long messages instead of splitting them, drop incomplete lines.\n\n        --timestamp-monotonic                    [A] Use monotonic clock for `timestamp:` overlay\n    -k, --insecure                               Accept invalid certificates and hostnames while connecting to TLS\n        --udp-broadcast                          [A] Set SO_BROADCAST\n        --udp-multicast-loop                     [A] Set IP[V6]_MULTICAST_LOOP\n        --udp-oneshot                            [A] udp-listen: replies only one packet per client\n        --udp-reuseaddr\n            [A] Set SO_REUSEADDR for UDP socket. Listening TCP sockets are always reuseaddr.\n\n        --uncompress-deflate\n            [A] Uncompress data coming from a WebSocket using deflate method. Affects only binary WebSocket messages.\n\n        --uncompress-gzip\n            [A] Uncompress data coming from a WebSocket using deflate method. Affects only binary WebSocket messages.\n\n        --uncompress-zlib\n            [A] Uncompress data coming from a WebSocket using deflate method. Affects only binary WebSocket messages.\n\n    -u, --unidirectional                         Inhibit copying data in one direction\n    -U, --unidirectional-reverse\n            Inhibit copying data in the other direction (or maybe in both directions if combined with -u)\n\n        --accept-from-fd\n            [A] Do not call `socket(2)` in UNIX socket listener peer, start with `accept(2)` using specified file\n            descriptor number as argument instead of filename\n        --unlink                                 [A] Unlink listening UNIX socket before binding to it\n    -V, --version                                Prints version information\n    -v                                           Increase verbosity level to info or further\n    -b, --binary                                 Send message to WebSockets as binary messages\n    -n, --no-close                               Don't send Close message to websocket on EOF\n        --websocket-ignore-zeromsg\n            [A] Silently drop incoming zero-length WebSocket messages. They may cause connection close due to usage of\n            zero-len message as EOF flag inside Websocat.\n    -t, --text                                   Send message to WebSockets as text messages\n        --base64\n            Encode incoming binary WebSocket messages in one-line Base64 If `--binary-prefix` (see `--help=full`) is\n            set, outgoing WebSocket messages that start with the prefix are decoded from base64 prior to sending.\n        --base64-text\n            [A] Encode incoming text WebSocket messages in one-line Base64. I don't know whether it can be ever useful,\n            but it's for symmetry with `--base64`.\n\nOPTIONS:\n        --socks5 <auto_socks5>\n            Use specified address:port as a SOCKS5 proxy. Example: --socks5 127.0.0.1:9050\n\n        --autoreconnect-delay-millis <autoreconnect_delay_millis>\n            [A] Delay before reconnect attempt for `autoreconnect:` overlay. [default: 20]\n\n        --basic-auth <basic_auth>\n            Add `Authorization: Basic` HTTP request header with this base64-encoded parameter. Also available as\n            `WEBSOCAT_BASIC_AUTH` environment variable\n        --basic-auth-file <basic_auth_file>\n            Add `Authorization: Basic` HTTP request header base64-encoded content of the specified file\n\n        --queue-len <broadcast_queue_len>\n            [A] Number of pending queued messages for broadcast reuser [default: 16]\n\n    -B, --buffer-size <buffer_size>                                  Maximum message size, in bytes [default: 65536]\n        --byte-to-exit-on <byte_to_exit_on>\n            [A] Override the byte which byte_to_exit_on: overlay looks for [default: 28]\n\n        --client-pkcs12-der <client_pkcs12_der>                      [A] Client identity TLS certificate\n        --client-pkcs12-passwd <client_pkcs12_passwd>\n            [A] Password for --client-pkcs12-der pkcs12 archive. Required on Mac.\n\n        --close-reason <close_reason>\n            Close connection with a reason message. This option only takes effect if --close-status-code option is\n            provided as well.\n        --close-status-code <close_status_code>                      Close connection with a status code.\n        --crypto-key <crypto_key>\n            [A] Specify encryption/decryption key for `crypto:` specifier. Requires `base64:`, `file:` or `pwd:` prefix.\n\n    -H, --header <custom_headers>...\n            Add custom HTTP header to websocket client request. Separate header name and value with a colon and\n            optionally a single space. Can be used multiple times. Note that single -H may eat multiple further\n            arguments, leading to confusing errors. Specify headers at the end or with equal sign like -H='X: y'.\n        --server-header <custom_reply_headers>...\n            Add custom HTTP header to websocket upgrade reply. Separate header name and value with a colon and\n            optionally a single space. Can be used multiple times. Note that single -H may eat multiple further\n            arguments, leading to confusing errors.\n        --exec-args <exec_args>...\n            [A] Arguments for the `exec:` specifier. Must be the last option, everything after it gets into the exec\n            args list.\n        --header-to-env <headers_to_env>...\n            Forward specified incoming request header to H_* environment variable for `exec:`-like specifiers.\n\n    -h, --help <help>\n            See the help.\n            --help=short is the list of easy options and address types\n            --help=long lists all options and types (see [A] markers)\n            --help=doc also shows longer description and examples.\n        --inhibit-pongs <inhibit_pongs>\n            [A] Stop replying to incoming WebSocket pings after specified number of replies\n\n        --just-generate-accept <just_generate_accept>\n            [A] Just a Sec-WebSocket-Accept value based on supplied Sec-WebSocket-Key value without running main\n            Websocat\n        --lengthprefixed-nbytes <lengthprefixed_header_bytes>\n            [A] Use this number of length header bytes for `lengthprefixed:` overlay. [default: 4]\n\n        --max-messages <max_messages>\n            Maximum number of messages to copy in one direction.\n\n        --max-messages-rev <max_messages_rev>\n            Maximum number of messages to copy in the other direction.\n\n        --conncap <max_parallel_conns>\n            Maximum number of simultaneous connections for listening mode\n\n        --max-sent-pings <max_sent_pings>\n            [A] Stop sending pings after this number of sent pings\n\n        --max-ws-frame-length <max_ws_frame_length>\n            [A] Maximum size of incoming WebSocket frames, to prevent memory overflow [default: 104857600]\n\n        --max-ws-message-length <max_ws_message_length>\n            [A] Maximum size of incoming WebSocket messages (sans of one data frame), to prevent memory overflow\n            [default: 209715200]\n        --origin <origin>                                            Add Origin HTTP header to websocket client request\n        --pkcs12-der <pkcs12_der>\n            Pkcs12 archive needed to accept SSL connections, certificate and key.\n            A command to output it: openssl pkcs12 -export -out output.pkcs12 -inkey key.pem -in cert.pem\n            Use with -s (--server-mode) option or with manually specified TLS overlays.\n            See moreexamples.md for more info.\n        --pkcs12-passwd <pkcs12_passwd>\n            Password for --pkcs12-der pkcs12 archive. Required on Mac.\n\n    -p, --preamble <preamble>...\n            Prepend copied data with a specified string. Can be specified multiple times.\n\n    -P, --preamble-reverse <preamble_reverse>...\n            Prepend copied data with a specified string (reverse direction). Can be specified multiple times.\n\n        --prometheus <prometheus>\n            Expose Prometheus metrics on specified IP address and port in addition to running usual Websocat session\n\n        --request-header <request_headers>...\n            [A] Specify HTTP request headers for `http-request:` specifier.\n\n    -X, --request-method <request_method>                            [A] Method to use for `http-request:` specifier\n        --request-uri <request_uri>                                  [A] URI to use for `http-request:` specifier\n        --restrict-uri <restrict_uri>\n            When serving a websocket, only accept the given URI, like `/ws`\n            This liberates other URIs for things like serving static files or proxying.\n    -F, --static-file <serve_static_files>...\n            Serve a named static file for non-websocket connections.\n            Argument syntax: <URI>:<Content-Type>:<file-path>\n            Argument example: /index.html:text/html:index.html\n            Directories are not and will not be supported for security reasons.\n            Can be specified multiple times. Recommended to specify them at the end or with equal sign like `-F=...`,\n            otherwise this option may eat positional arguments\n        --socks5-bind-script <socks5_bind_script>\n            [A] Execute specified script in `socks5-bind:` mode when remote port number becomes known.\n\n        --socks5-user-pass <socks5_user_pass>\n            [A] Specify username:password for SOCKS5 proxy. If not specified, the default is to use no authentication.\n\n        --socks5-destination <socks_destination>\n            [A] Examples: 1.2.3.4:5678  2600:::80  hostname:5678\n\n        --tls-domain <tls_domain>\n            [A] Specify domain for SNI or certificate verification when using tls-connect: overlay\n\n        --udp-multicast <udp_join_multicast_addr>...\n            [A] Issue IP[V6]_ADD_MEMBERSHIP for specified multicast address. Can be specified multiple times.\n\n        --udp-multicast-iface-v4 <udp_join_multicast_iface_v4>...\n            [A] IPv4 address of multicast network interface. Has to be either not specified or specified the same number\n            of times as multicast IPv4 addresses. Order matters.\n        --udp-multicast-iface-v6 <udp_join_multicast_iface_v6>...\n            [A] Index of network interface for IPv6 multicast. Has to be either not specified or specified the same\n            number of times as multicast IPv6 addresses. Order matters.\n        --udp-ttl <udp_ttl>                                          [A] Set IP_TTL, also IP_MULTICAST_TTL if applicable\n        --ua <useragent>\n            Set `User-Agent` request header to this value. Similar to setting it with `-H`.\n\n        --protocol <websocket_protocol>\n            Specify this Sec-WebSocket-Protocol: header when connecting\n\n        --server-protocol <websocket_reply_protocol>\n            Force this Sec-WebSocket-Protocol: header when accepting a connection\n\n        --websocket-version <websocket_version>                      Override the Sec-WebSocket-Version value\n        --binary-prefix <ws_binary_prefix>\n            [A] Prepend specified text to each received WebSocket binary message. Also strip this prefix from outgoing\n            messages, explicitly marking them as binary even if `--text` is specified\n        --ws-c-uri <ws_c_uri>\n            [A] URI to use for ws-c: overlay [default: ws://0.0.0.0/]\n\n        --ping-interval <ws_ping_interval>                           Send WebSocket pings each this number of seconds\n        --ping-timeout <ws_ping_timeout>\n            Drop WebSocket connection if Pong message not received for this number of seconds\n\n        --text-prefix <ws_text_prefix>\n            [A] Prepend specified text to each received WebSocket text message. Also strip this prefix from outgoing\n            messages, explicitly marking them as text even if `--binary` is specified\n\nARGS:\n    <addr1>    In simple mode, WebSocket URL to connect. In advanced mode first address (there are many kinds of\n               addresses) to use. See --help=types for info about address types. If this is an address for\n               listening, it will try serving multiple connections.\n    <addr2>    In advanced mode, second address to connect. If this is an address for listening, it will accept only\n               one connection.\n\n\nBasic examples:\n  Command-line websocket client:\n    websocat ws://ws.vi-server.org/mirror/\n    \n  WebSocket server\n    websocat -s 8080\n    \n  WebSocket-to-TCP proxy:\n    websocat --binary ws-l:127.0.0.1:8080 tcp:127.0.0.1:5678\n    \n\n```\n\n# Full list of address types\n\n\"Advanced\" address types are denoted by `[A]` marker.\n\n\n### `ws://`\n\nInternal name for --dump-spec: WsClient\n\n\nInsecure (ws://) WebSocket client. Argument is host and URL.\n\nExample: connect to public WebSocket loopback and copy binary chunks from stdin to the websocket.\n\n    websocat - ws://echo.websocket.org/\n\n\n### `wss://`\n\nInternal name for --dump-spec: WsClientSecure\n\n\nSecure (wss://) WebSocket client. Argument is host and URL.\n\nExample: forward TCP port 4554 to a websocket\n\n    websocat tcp-l:127.0.0.1:4554 wss://127.0.0.1/some_websocket\n\n### `ws-listen:`\n\nAliases: `ws-l:`, `l-ws:`, `listen-ws:`  \nInternal name for --dump-spec: WsTcpServer\n\n\nWebSocket server. Argument is host and port to listen.\n\nExample: Dump all incoming websocket data to console\n\n    websocat ws-l:127.0.0.1:8808 -\n\nExample: the same, but more verbose:\n\n    websocat ws-l:tcp-l:127.0.0.1:8808 reuse:-\n\n\n### `inetd-ws:`\n\nAliases: `ws-inetd:`  \nInternal name for --dump-spec: WsInetdServer\n\n\nWebSocket inetd server. [A]\n\nTODO: transfer the example here\n\n\n### `l-ws-unix:`\n\nInternal name for --dump-spec: WsUnixServer\n\n\nWebSocket UNIX socket-based server. [A]\n\n\n### `l-ws-abstract:`\n\nInternal name for --dump-spec: WsAbstractUnixServer\n\n\nWebSocket abstract-namespaced UNIX socket server. [A]\n\n\n### `ws-lowlevel-client:`\n\nAliases: `ws-ll-client:`, `ws-ll-c:`  \nInternal name for --dump-spec: WsLlClient\n\n\n[A] Low-level HTTP-independent WebSocket client connection without associated HTTP upgrade.\n\nExample: TODO\n\n\n### `ws-lowlevel-server:`\n\nAliases: `ws-ll-server:`, `ws-ll-s:`  \nInternal name for --dump-spec: WsLlServer\n\n\n[A] Low-level HTTP-independent WebSocket server connection without associated HTTP upgrade.\n\nExample: TODO\n\n\n### `wss-listen:`\n\nAliases: `wss-l:`, `l-wss:`, `wss-listen:`  \nInternal name for --dump-spec: WssListen\n\n\nListen for secure WebSocket connections on a TCP port\n\nExample: wss:// echo server + client for testing\n\n    websocat -E -t --pkcs12-der=q.pkcs12 wss-listen:127.0.0.1:1234 mirror:\n    websocat --ws-c-uri=wss://localhost/ -t - ws-c:cmd:'socat - ssl:127.0.0.1:1234,verify=0'\n\nSee [moreexamples.md](./moreexamples.md) for info about generation of `q.pkcs12`.\n\n\n### `http:`\n\nInternal name for --dump-spec: Http\n\n\n[A] Issue HTTP request, receive a 1xx or 2xx reply, then pass\nthe torch to outer peer, if any - highlevel version.\n\nContent you write becomes body, content you read is body that server has sent.\n\nURI is specified inline.\n\nExample:\n\n    websocat  -b - http://example.com < /dev/null\n\n\n### `asyncstdio:`\n\nInternal name for --dump-spec: AsyncStdio\n\n\n[A] Set stdin and stdout to nonblocking mode, then use it as a communication counterpart. UNIX-only.\nMay cause problems with programs running at the same terminal. This specifier backs the `--async-stdio` CLI option. \n\nTypically this specifier can be specified only one time.\n    \nExample: simulate `cat(1)`. This is an exception from \"only one time\" rule above:\n\n    websocat - -\n\nExample: SSH transport\n\n    ssh -c ProxyCommand='websocat asyncstdio: ws://myserver/mywebsocket' user@myserver\n\n\n### `inetd:`\n\nInternal name for --dump-spec: Inetd\n\n\nLike `asyncstdio:`, but intended for inetd(8) usage. [A]\n\nAutomatically enables `-q` (`--quiet`) mode.\n\n`inetd-ws:` - is of `ws-l:inetd:`\n\nExample of inetd.conf line that makes it listen for websocket\nconnections on port 1234 and redirect the data to local SSH server.\n\n    1234 stream tcp nowait myuser  /opt/websocat websocat inetd-ws: tcp:127.0.0.1:22\n\n\n### `tcp:`\n\nAliases: `tcp-connect:`, `connect-tcp:`, `tcp-c:`, `c-tcp:`  \nInternal name for --dump-spec: TcpConnect\n\n\nConnect to specified TCP host and port. Argument is a socket address.\n\nExample: simulate netcat netcat\n\n    websocat - tcp:127.0.0.1:22\n\nExample: redirect websocket connections to local SSH server over IPv6\n\n    websocat ws-l:0.0.0.0:8084 tcp:[::1]:22\n\n\n### `tcp-listen:`\n\nAliases: `listen-tcp:`, `tcp-l:`, `l-tcp:`  \nInternal name for --dump-spec: TcpListen\n\n\nListen TCP port on specified address.\n    \nExample: echo server\n\n    websocat tcp-l:0.0.0.0:1441 mirror:\n    \nExample: redirect TCP to a websocket\n\n    websocat tcp-l:0.0.0.0:8088 ws://echo.websocket.org\n\n\n### `ssl-listen:`\n\nAliases: `ssl-l:`, `tls-l:`, `tls-listen:`, `l-ssl:`, `listen-ssl:`, `listen-tls:`, `listen-tls:`  \nInternal name for --dump-spec: TlsListen\n\n\nListen for SSL connections on a TCP port\n\nExample: Non-websocket SSL echo server\n\n    websocat -E -b --pkcs12-der=q.pkcs12 ssl-listen:127.0.0.1:1234 mirror:\n    socat - ssl:127.0.0.1:1234,verify=0\n\n\n### `sh-c:`\n\nInternal name for --dump-spec: ShC\n\n\nStart specified command line using `sh -c` (even on Windows)\n  \nExample: serve a counter\n\n    websocat -U ws-l:127.0.0.1:8008 sh-c:'for i in 0 1 2 3 4 5 6 7 8 9 10; do echo $i; sleep 1; done'\n  \nExample: unauthenticated shell\n\n    websocat --exit-on-eof ws-l:127.0.0.1:5667 sh-c:'bash -i 2>&1'\n\n\n### `cmd:`\n\nInternal name for --dump-spec: Cmd\n\n\nStart specified command line using `sh -c` or `cmd /C` (depending on platform)\n\nOtherwise should be the the same as `sh-c:` (see examples from there).\n\n\n### `exec:`\n\nInternal name for --dump-spec: Exec\n\n\nExecute a program directly (without a subshell), providing array of arguments on Unix [A]\n\nExample: Serve current date\n\n  websocat -U ws-l:127.0.0.1:5667 exec:date\n  \nExample: pinger\n\n  websocat -U ws-l:127.0.0.1:5667 exec:ping --exec-args 127.0.0.1 -c 1\n  \n\n\n### `readfile:`\n\nInternal name for --dump-spec: ReadFile\n\n\nSynchronously read a file. Argument is a file path.\n\nBlocking on operations with the file pauses the whole process\n\nExample: Serve the file once per connection, ignore all replies.\n\n    websocat ws-l:127.0.0.1:8000 readfile:hello.json\n\n\n\n### `writefile:`\n\nInternal name for --dump-spec: WriteFile\n\n\n\nSynchronously truncate and write a file.\n\nBlocking on operations with the file pauses the whole process\n\nExample:\n\n    websocat ws-l:127.0.0.1:8000 writefile:data.txt\n\n\n\n### `appendfile:`\n\nInternal name for --dump-spec: AppendFile\n\n\n\nSynchronously append a file.\n\nBlocking on operations with the file pauses the whole process\n\nExample: Logging all incoming data from WebSocket clients to one file\n\n    websocat -u ws-l:127.0.0.1:8000 reuse:appendfile:log.txt\n\n\n### `udp:`\n\nAliases: `udp-connect:`, `connect-udp:`, `udp-c:`, `c-udp:`  \nInternal name for --dump-spec: UdpConnect\n\n\nSend and receive packets to specified UDP socket, from random UDP port  \n\n\n### `udp-listen:`\n\nAliases: `listen-udp:`, `udp-l:`, `l-udp:`  \nInternal name for --dump-spec: UdpListen\n\n\nBind an UDP socket to specified host:port, receive packet\nfrom any remote UDP socket, send replies to recently observed\nremote UDP socket.\n\nNote that it is not a multiconnect specifier like e.g. `tcp-listen`:\nentire lifecycle of the UDP socket is the same connection.\n\nFile a feature request on Github if you want proper DNS-like request-reply UDP mode here.\n\n\n### `open-async:`\n\nInternal name for --dump-spec: OpenAsync\n\n\nOpen file for read and write and use it like a socket. [A]\nNot for regular files, see readfile/writefile instead.\n  \nExample: Serve big blobs of random data to clients\n\n    websocat -U ws-l:127.0.0.1:8088 open-async:/dev/urandom\n\n\n\n### `open-fd:`\n\nInternal name for --dump-spec: OpenFdAsync\n\n\nUse specified file descriptor like a socket. [A]\n\nExample: Serve random data to clients v2\n\n    websocat -U ws-l:127.0.0.1:8088 reuse:open-fd:55   55< /dev/urandom\n\n\n### `threadedstdio:`\n\nInternal name for --dump-spec: ThreadedStdio\n\n\n[A] Stdin/stdout, spawning a thread (threaded version).\n\nLike `-`, but forces threaded mode instead of async mode\n\nUse when standard input is not `epoll(7)`-able or you want to avoid setting it to nonblocking mode.\n\n\n### `-`\n\nAliases: `stdio:`  \nInternal name for --dump-spec: Stdio\n\n\nRead input from console, print to console. Uses threaded implementation even on UNIX unless requested by `--async-stdio` CLI option.\n\nTypically this specifier can be specified only one time.\n    \nExample: simulate `cat(1)`. This is an exception from \"only one time\" rule above:\n\n    websocat - -\n\nExample: SSH transport\n\n    ssh -c ProxyCommand='websocat - ws://myserver/mywebsocket' user@myserver\n\n\n### `unix:`\n\nAliases: `unix-connect:`, `connect-unix:`, `unix-c:`, `c-unix:`  \nInternal name for --dump-spec: UnixConnect\n\n\nConnect to UNIX socket. Argument is filesystem path. [A]\n\nExample: forward connections from websockets to a UNIX stream socket\n\n    websocat ws-l:127.0.0.1:8088 unix:the_socket\n\n\n### `unix-listen:`\n\nAliases: `listen-unix:`, `unix-l:`, `l-unix:`  \nInternal name for --dump-spec: UnixListen\n\n\nListen for connections on a specified UNIX socket [A]\n\nExample: forward connections from a UNIX socket to a WebSocket\n\n    websocat --unlink unix-l:the_socket ws://127.0.0.1:8089\n\nExample: Accept forwarded WebSocket connections from Nginx\n\n    umask 0000\n    websocat --unlink -b -E ws-u:unix-l:/tmp/wstest tcp:[::]:22\n\nNginx config:\n\n    location /ws {\n        proxy_read_timeout 7d;\n        proxy_send_timeout 7d;\n        #proxy_pass http://localhost:3012;\n        proxy_pass http://unix:/tmp/wstest;\n        proxy_http_version 1.1;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection \\\"upgrade\\\";\n    }\n\nThis configuration allows to make Nginx responsible for\nSSL and also it can choose which connections to forward\nto websocat based on URLs.\n\nObviously, Nginx can also redirect to TCP-listening\nwebsocat just as well - UNIX sockets are not a requirement for this feature.\n\nSee `moreexamples.md` for SystemD usage (untested).\n\nTODO: --chmod option?\n\n\n### `unix-dgram:`\n\nInternal name for --dump-spec: UnixDgram\n\n\nSend packets to one path, receive from the other. [A]\nA socket for sending must be already opened.\n\nI don't know if this mode has any use, it is here just for completeness.\n\nExample:\n\n    socat unix-recv:./sender -&\n    websocat - unix-dgram:./receiver:./sender\n\n\n### `abstract:`\n\nAliases: `abstract-connect:`, `connect-abstract:`, `abstract-c:`, `c-abstract:`  \nInternal name for --dump-spec: AbstractConnect\n\n\nConnect to UNIX abstract-namespaced socket. Argument is some string used as address. [A]\n\nToo long addresses may be silently chopped off.\n\nExample: forward connections from websockets to an abstract stream socket\n\n    websocat ws-l:127.0.0.1:8088 abstract:the_socket\n\nNote that abstract-namespaced Linux sockets may not be normally supported by Rust,\nso non-prebuilt versions may have problems with them.\n\n\n### `abstract-listen:`\n\nAliases: `listen-abstract:`, `abstract-l:`, `l-abstract:`  \nInternal name for --dump-spec: AbstractListen\n\n\nListen for connections on a specified abstract UNIX socket [A]\n\nExample: forward connections from an abstract UNIX socket to a WebSocket\n\n    websocat abstract-l:the_socket ws://127.0.0.1:8089\n\nNote that abstract-namespaced Linux sockets may not be normally supported by Rust,\nso non-prebuilt versions may have problems with them.\n\n\n### `abstract-dgram:`\n\nInternal name for --dump-spec: AbstractDgram\n\n\nSend packets to one address, receive from the other. [A]\nA socket for sending must be already opened.\n\nI don't know if this mode has any use, it is here just for completeness.\n\nExample (untested):\n\n    websocat - abstract-dgram:receiver_addr:sender_addr\n\nNote that abstract-namespaced Linux sockets may not be normally supported by Rust,\nso non-prebuilt versions may have problems with them. In particular, this mode\nmay fail to work without `workaround1` Cargo feature.\n\n\n### `mirror:`\n\nInternal name for --dump-spec: Mirror\n\n\nSimply copy output to input. No arguments needed.\n\nExample: emulate echo.websocket.org\n\n    websocat -t ws-l:127.0.0.1:1234 mirror:\n\n\n### `literalreply:`\n\nInternal name for --dump-spec: LiteralReply\n\n\nReply with a specified string for each input packet.\n\nExample:\n\n    websocat ws-l:0.0.0.0:1234 literalreply:'{\"status\":\"OK\"}'\n\n\n### `clogged:`\n\nInternal name for --dump-spec: Clogged\n\n\nDo nothing. Don't read or write any bytes. Keep connections in \"hung\" state. [A]\n\n\n### `literal:`\n\nInternal name for --dump-spec: Literal\n\n\nOutput a string, discard input.\n\nExample:\n\n    websocat ws-l:127.0.0.1:8080 literal:'{ \"hello\":\"world\"} '\n\n\n### `assert:`\n\nInternal name for --dump-spec: Assert\n\n\nCheck the input.  [A]\n\nRead entire input and panic the program if the input is not equal\nto the specified string. Used in tests.\n\n\n### `assert2:`\n\nInternal name for --dump-spec: Assert2\n\n\nCheck the input. [A]\n\nRead entire input and emit an error if the input is not equal\nto the specified string.\n\n\n### `seqpacket:`\n\nAliases: `seqpacket-connect:`, `connect-seqpacket:`, `seqpacket-c:`, `c-seqpacket:`  \nInternal name for --dump-spec: SeqpacketConnect\n\n\nConnect to AF_UNIX SOCK_SEQPACKET socket. Argument is a filesystem path. [A]\n\nStart the path with `@` character to make it connect to abstract-namespaced socket instead.\n\nToo long paths are silently truncated.\n\nExample: forward connections from websockets to a UNIX seqpacket abstract socket\n\n    websocat ws-l:127.0.0.1:1234 seqpacket:@test\n\n\n### `seqpacket-listen:`\n\nAliases: `listen-seqpacket:`, `seqpacket-l:`, `l-seqpacket:`  \nInternal name for --dump-spec: SeqpacketListen\n\n\nListen for connections on a specified AF_UNIX SOCK_SEQPACKET socket [A]\n\nStart the path with `@` character to make it connect to abstract-namespaced socket instead.\n\nToo long (>=108 bytes) paths are silently truncated.\n\nExample: forward connections from a UNIX seqpacket socket to a WebSocket\n\n    websocat --unlink seqpacket-l:the_socket ws://127.0.0.1:8089\n\n\n### `random:`\n\nInternal name for --dump-spec: Random\n\n\nGenerate random bytes when being read from, discard written bytes.\n\n    websocat -b random: ws://127.0.0.1/flood\n\n\n\n\n\n# Full list of overlays\n\n\"Advanced\" overlays denoted by `[A]` marker.\n\n\n### `ws-upgrade:`\n\nAliases: `upgrade-ws:`, `ws-u:`, `u-ws:`  \nInternal name for --dump-spec: WsServer\n\n\nWebSocket upgrader / raw server. Specify your own protocol instead of usual TCP. [A]\n\nAll other WebSocket server modes actually use this overlay under the hood.\n\nExample: serve incoming connection from socat\n\n    socat tcp-l:1234,fork,reuseaddr exec:'websocat -t ws-u\\:stdio\\: mirror\\:'\n\n\n### `http-request:`\n\nInternal name for --dump-spec: HttpRequest\n\n\n[A] Issue HTTP request, receive a 1xx or 2xx reply, then pass\nthe torch to outer peer, if any - lowlevel version.\n\nContent you write becomes body, content you read is body that server has sent.\n\nURI is specified using a separate command-line parameter\n\nExample:\n\n    websocat -Ub - http-request:tcp:example.com:80 --request-uri=http://example.com/ --request-header 'Connection: close'\n\n\n### `http-post-sse:`\n\nInternal name for --dump-spec: HttpPostSse\n\n\n[A] Accept HTTP/1 request. Then, if it is GET,\nunidirectionally return incoming messages as server-sent events (SSE).\n\nIf it is POST then, also unidirectionally, write body upstream.\n\nExample - turn SSE+POST pair into a client WebSocket connection:\n\n    websocat -E -t http-post-sse:tcp-l:127.0.0.1:8080 reuse:ws://127.0.0.1:80/websock\n\n`curl -dQQQ http://127.0.0.1:8080/` would send into it and \n`curl -N http://127.0.0.1:8080/` would recv from it.\n\n\n### `ssl-connect:`\n\nAliases: `ssl-c:`, `ssl:`, `tls:`, `tls-connect:`, `tls-c:`, `c-ssl:`, `connect-ssl:`, `c-tls:`, `connect-tls:`  \nInternal name for --dump-spec: TlsConnect\n\n\nOverlay to add TLS encryption atop of existing connection [A]\n\nExample: manually connect to a secure websocket\n\n    websocat -t - ws-c:tls-c:tcp:174.129.224.73:1080 --ws-c-uri ws://echo.websocket.org --tls-domain echo.websocket.org\n\nFor a user-friendly solution, see --socks5 command-line option\n\n\n### `ssl-accept:`\n\nAliases: `ssl-a:`, `tls-a:`, `tls-accept:`, `a-ssl:`, `accept-ssl:`, `accept-tls:`, `accept-tls:`  \nInternal name for --dump-spec: TlsAccept\n\n\nAccept an TLS connection using arbitrary backing stream. [A]\n\nExample: The same as in TlsListenClass's example, but with manual acceptor\n\n    websocat -E -b --pkcs12-der=q.pkcs12 tls-a:tcp-l:127.0.0.1:1234 mirror:\n\n\n### `reuse-raw:`\n\nAliases: `raw-reuse:`  \nInternal name for --dump-spec: Reuser\n\n\nReuse subspecifier for serving multiple clients: unpredictable mode. [A]\n\nBetter used with --unidirectional, otherwise replies get directed to\nrandom connected client.\n\nExample: Forward multiple parallel WebSocket connections to a single persistent TCP connection\n\n    websocat -u ws-l:0.0.0.0:8800 reuse:tcp:127.0.0.1:4567\n\nExample (unreliable): don't disconnect SSH when websocket reconnects\n\n    websocat ws-l:[::]:8088 reuse:tcp:127.0.0.1:22\n\n\n### `broadcast:`\n\nAliases: `reuse:`, `reuse-broadcast:`, `broadcast-reuse:`  \nInternal name for --dump-spec: BroadcastReuser\n\n\nReuse this connection for serving multiple clients, sending replies to all clients.\n\nMessages from any connected client get directed to inner connection,\nreplies from the inner connection get duplicated across all connected\nclients (and are dropped if there are none).\n\nIf WebSocket client is too slow for accepting incoming data,\nmessages get accumulated up to the configurable --broadcast-buffer, then dropped.\n\nExample: Simple data exchange between connected WebSocket clients\n\n    websocat -E ws-l:0.0.0.0:8800 reuse-broadcast:mirror:\n\n\n### `autoreconnect:`\n\nInternal name for --dump-spec: AutoReconnect\n\n\nRe-establish underlying connection on any error or EOF\n\nExample: keep connecting to the port or spin 100% CPU trying if it is closed.\n\n    websocat - autoreconnect:tcp:127.0.0.1:5445\n    \nExample: keep remote logging connection open (or flood the host if port is closed):\n\n    websocat -u ws-l:0.0.0.0:8080 reuse:autoreconnect:tcp:192.168.0.3:1025\n  \nTODO: implement delays between reconnect attempts\n\n\n### `ws-c:`\n\nAliases: `c-ws:`, `ws-connect:`, `connect-ws:`  \nInternal name for --dump-spec: WsConnect\n\n\nLow-level WebSocket connector. Argument is a some another address. [A]\n\nURL and Host: header being sent are independent from the underlying connection.\n\nExample: connect to echo server in more explicit way\n\n    websocat --ws-c-uri=ws://echo.websocket.org/ - ws-c:tcp:174.129.224.73:80\n\nExample: connect to echo server, observing WebSocket TCP packet exchange\n\n    websocat --ws-c-uri=ws://echo.websocket.org/ - ws-c:cmd:\"socat -v -x - tcp:174.129.224.73:80\"\n\n\n\n### `msg2line:`\n\nInternal name for --dump-spec: Message2Line\n\n\nLine filter: Turns messages from packet stream into lines of byte stream. [A]\n\nEnsure each message (a chunk from one read call from underlying connection)\ncontains no inner newlines (or zero bytes) and terminates with one newline.\n\nReverse of the `line2msg:`.\n\nUnless --null-terminated, replaces both newlines (\\x0A) and carriage returns (\\x0D) with spaces (\\x20) for each read.\n\nDoes not affect writing at all. Use this specifier on both ends to get bi-directional behaviour.\n\nAutomatically inserted by --line option on top of the stack containing a websocket.\n\nExample: TODO\n\n\n### `line2msg:`\n\nInternal name for --dump-spec: Line2Message\n\n\nLine filter: turn lines from byte stream into messages as delimited by '\\\\n' or '\\\\0' [A]\n\nEnsure that each message (a successful read call) is obtained from a line [A]\ncoming from underlying specifier, buffering up or splitting content as needed.\n\nReverse of the `msg2line:`.\n\nDoes not affect writing at all. Use this specifier on both ends to get bi-directional behaviour.\n\nAutomatically inserted by --line option at the top of the stack opposite to websocket-containing stack.\n\nExample: TODO\n\n\n### `lengthprefixed:`\n\nInternal name for --dump-spec: LengthPrefixed\n\n\nTurn stream of bytes to/from data packets with length-prefixed framing.  [A]\n\nYou can choose the number of header bytes (1 to 8) and endianness. Default is 4 bytes big endian.\n\nThis affects both reading and writing - attach this overlay to stream specifier to turn it into a packet-orineted specifier.\n\nMind the buffer size (-B). All packets should fit in there.\n\nExamples:\n\n    websocat -u -b udp-l:127.0.0.1:1234 lengthprefixed:writefile:test.dat\n\n    websocat -u -b lengthprefixed:readfile:test.dat udp:127.0.0.1:1235\n\nThis would save incoming UDP packets to a file, then replay the datagrams back to UDP socket\n\n    websocat -b lengthprefixed:- ws://127.0.0.1:1234/ --binary-prefix=B --text-prefix=T\n\nThis allows to mix and match text and binary WebSocket messages to and from stdio without the base64 overhead.\n\n\n### `foreachmsg:`\n\nInternal name for --dump-spec: Foreachmsg\n\n\nExecute something for each incoming message.\n\nSomewhat the reverse of the `autoreconnect:`.\n\nExample:\n\n    websocat -t -u ws://server/listen_for_updates foreachmsg:writefile:status.txt\n\nThis keeps only recent incoming message in file and discards earlier messages.\n\n\n### `log:`\n\nInternal name for --dump-spec: Log\n\n\nLog each buffer as it pass though the underlying connector.\n\nIf you increase the logging level, you will also see hex buffers.\n\nExample: view WebSocket handshake and traffic on the way to echo.websocket.org\n\n    websocat -t - ws-c:log:tcp:127.0.0.1:1080 --ws-c-uri ws://echo.websocket.org\n\n\n\n### `jsonrpc:`\n\nInternal name for --dump-spec: JsonRpc\n\n\n[A] Turns messages like `abc 1,2` into `{\"jsonrpc\":\"2.0\",\"id\":412, \"method\":\"abc\", \"params\":[1,2]}`.\n\nFor simpler manual testing of websocket-based JSON-RPC services\n\nExample: TODO\n\n\n### `timestamp:`\n\nInternal name for --dump-spec: Timestamp\n\n\n[A] Prepend timestamp to each incoming message.\n\nExample: TODO\n\n\n### `socks5-connect:`\n\nInternal name for --dump-spec: SocksProxy\n\n\nSOCKS5 proxy client (raw) [A]\n\nExample: connect to a websocket using local `ssh -D` proxy\n\n    websocat -t - ws-c:socks5-connect:tcp:127.0.0.1:1080 --socks5-destination echo.websocket.org:80 --ws-c-uri ws://echo.websocket.org\n\nFor a user-friendly solution, see --socks5 command-line option\n\n\n### `socks5-bind:`\n\nInternal name for --dump-spec: SocksBind\n\n\nSOCKS5 proxy client (raw, bind command) [A]\n\nExample: bind to a websocket using some remote SOCKS server\n\n    websocat -v -t ws-u:socks5-bind:tcp:132.148.129.183:14124 - --socks5-destination 255.255.255.255:65535\n\nNote that port is typically unpredictable. Use --socks5-bind-script option to know the port.\nSee an example in moreexamples.md for more thorough example.\n\n\n### `crypto:`\n\nInternal name for --dump-spec: Crypto\n\n\n[A] Encrypts written messages and decrypts (and verifies) read messages with a static key, using ChaCha20-Poly1305 algorithm.\n\nDo not not use in stream mode - packet boundaries are significant.\n\nNote that attacker may duplicate, drop or reorder messages, including between different Websocat sessions with the same key.\n\nEach encrypted message is 12 bytes bigger than original message.\n\nAssociated --crypto-key option accepts the following prefixes:\n\n- `file:` prefix means that Websocat should read 32-byte file and use it as a key.\n- `base64:` prefix means the rest of the value is base64-encoded 32-byte buffer\n- `pwd:` means Websocat should use argon2 derivation from the specified password as a key\n\nUse `--crypto-reverse` option to swap encryption and decryption.\n\nNote that `crypto:` specifier is absent in usual Websocat builds.\nYou may need to build Websocat from source code with `--features=crypto_peer` for it to be available.\n\n\n### `prometheus:`\n\nAliases: `metrics:`  \nInternal name for --dump-spec: Prometheus\n\n\n[A] Account connections, messages, bytes and other data and expose Prometheus metrics on a separate port.\n\nNot included by default, build a crate with `--features=prometheus_peer` to have it.\nYou can also use `--features=prometheus_peer,prometheus/process` to have additional metrics.\n\n\n### `exit_on_specific_byte:`\n\nInternal name for --dump-spec: ExitOnSpecificByte\n\n\n[A] Turn specific byte into a EOF, allowing user to escape interactive Websocat session\nwhen terminal is set to raw mode. Works only bytes read from the overlay, not on the written bytes.\n\nDefault byte is 1C which is typically triggered by Ctrl+\\.\n\nExample: `(stty raw -echo; websocat -b exit_on_specific_byte:stdio tcp:127.0.0.1:23; stty sane)`\n\n\n\n### `drop_on_backpressure:`\n\nInternal name for --dump-spec: DropOnBackpressure\n\n\n[A] Prevent writing from ever blocking, drop writes instead.\n\nDoes not affect reading part.\nwhen terminal is set to raw mode. Works only bytes read from the overlay, not on the written bytes.\n\nDefault byte is 1C which is typically triggered by Ctrl+\\.\n\nExample (attachable log observer):\n\n    some_program | websocat -b -u asyncstdio: drop_on_backpressure:autoreconnect:ws-l:127.0.0.1:1234\n\n\n\n### `waitfordata:`\n\nAliases: `wait-for-data:`  \nInternal name for --dump-spec: WaitForData\n\n\nWait for some data to pending being written before starting connecting. [A]\n\nExample: Connect to the TCP server on the left side immediately, but connect to\nthe TCP server on the right side only after some data gets written by the first connection\n\n\n    websocat -b tcp:127.0.0.1:1234 waitfordata:tcp:127.0.0.1:1235\n\nExample: Connect to first WebSocket server, wait for some incoming WebSocket message, then\nconnect to the second WebSocket server and start exchanging text and binary WebSocket messages\nbetween them.\n\n    websocat -b --binary-prefix=b --text-prefix=t ws://127.0.0.1:1234 waitfordata:ws://127.0.0.1:1235/\n\n\n\n  \n### Address types or specifiers to be implemented later:\n\n`sctp:`, `speedlimit:`, `quic:`\n\n### Final example\n\nFinal example just for fun: wacky mode\n\n    websocat ws-c:ws-l:ws-c:- tcp:127.0.0.1:5678\n    \nConnect to a websocket using stdin/stdout as a transport,\nthen accept a websocket connection over the previous websocket used as a transport,\nthen connect to a websocket using previous step as a transport,\nthen forward resulting connection to the TCP port.\n\n(Exercise to the reader: manage to make it actually connect to 5678).\n\n"
  },
  {
    "path": "misc/prebuilt_release_settings.sh",
    "content": "targetsettings() {\n  EXTRA_CARGO_FLAGS=\"--features=vendored_openssl,openssl-probe\"\n  case \"$1\" in\n     i686-unknown-linux-musl)\n          EXTRA_CARGO_FLAGS=\"--no-default-features --features=signal_handler,tokio-process,unix_stdio,compression\"\n     ;;\n     arm-unknown-linux-musleabi)\n          EXTRA_CARGO_FLAGS=\"--no-default-features --features=signal_handler,tokio-process,unix_stdio,compression\"\n     ;;\n     loongarch64-unknown-linux-musl)\n          EXTRA_CARGO_FLAGS=\"--no-default-features --features=signal_handler,tokio-process,unix_stdio,compression\"\n     ;;\n     riscv64gc-unknown-linux-musl)\n          EXTRA_CARGO_FLAGS=\"--no-default-features --features=signal_handler,tokio-process,unix_stdio,compression\"\n     ;;\n     wasm32-wasip1)\n          SKIP=1\n     ;;\n  esac\n}\n"
  },
  {
    "path": "moreexamples.md",
    "content": "More examples to avoid bloating up README or specifier-specific docs.\n\n\n# SSL (TLS) and wss://\n\n## Connecting to wss:// without checking certificate\n\nWebsocat has `-k` option to turn off checking of SSL certificate. As alternative (or when using older versions of Websocat) you can use external programs to provide SSL for websocat.\nWith `socat`:\n\n```\n$ websocat -t --ws-c-uri=wss://echo.websocket.org/ - ws-c:cmd:'socat - ssl:echo.websocket.org:443,verify=0'\nsadf\nsadf\ndsafdsaf\ndsafdsaf\n```\n\nWith `openssl s_client`, also showing the log output:\n\n```\n$ websocat -v -t --ws-c-uri=wss://echo.websocket.org/ - ws-c:cmd:'openssl s_client -connect echo.websocket.org:443 -quiet' \n INFO 2018-08-30T15:45:31Z: websocat::lints: Auto-inserting the line mode\n INFO 2018-08-30T15:45:31Z: websocat::sessionserve: Serving Line2Message(Stdio) to Message2Line(WsConnect(Cmd(\"openssl s_client -connect echo.websocket.org:443 -quiet\"))) with Options { websocket_text_mode: true, websocket_protocol: None, udp_oneshot_mode: false, unidirectional: false, unidirectional_reverse: false, exit_on_eof: false, oneshot: false, unlink_unix_socket: false, exec_args: [], ws_c_uri: \"wss://echo.websocket.org/\", linemode_strip_newlines: false, linemode_strict: false, origin: None, custom_headers: [], websocket_version: None, websocket_dont_close: false, one_message: false, no_auto_linemode: false, buffer_size: 65536, broadcast_queue_len: 16, read_debt_handling: Warn, linemode_zero_terminated: false, restrict_uri: None, serve_static_files: [], exec_set_env: false, reuser_send_zero_msg_on_disconnect: false, process_zero_sighup: false, process_exit_sighup: false, socks_destination: None, auto_socks5: None, socks5_bind_script: None, tls_domain: None }\n INFO 2018-08-30T15:45:31Z: websocat::stdio_peer: get_stdio_peer (async)\n INFO 2018-08-30T15:45:31Z: websocat::stdio_peer: Setting stdin to nonblocking mode\n INFO 2018-08-30T15:45:31Z: websocat::stdio_peer: Installing signal handler\n INFO 2018-08-30T15:45:31Z: websocat::ws_client_peer: get_ws_client_peer_wrapped\ndepth=2 C = US, ST = Arizona, L = Scottsdale, O = \"GoDaddy.com, Inc.\", CN = Go Daddy Root Certificate Authority - G2\nverify return:1\ndepth=1 C = US, ST = Arizona, L = Scottsdale, O = \"GoDaddy.com, Inc.\", OU = http://certs.godaddy.com/repository/, CN = Go Daddy Secure Certificate Authority - G2\nverify return:1\ndepth=0 OU = Domain Control Validated, CN = *.websocket.org\nverify return:1\n INFO 2018-08-30T15:45:31Z: websocat::ws_client_peer: Connected to ws\n123\n123\nqwer\nqwer\n INFO 2018-08-30T15:45:35Z: websocat::sessionserve: Forward finished\n INFO 2018-08-30T15:45:35Z: websocat::sessionserve: Forward shutdown finished\n INFO 2018-08-30T15:45:35Z: websocat::sessionserve: Reverse finished\n INFO 2018-08-30T15:45:35Z: websocat::sessionserve: Reverse shutdown finished\n INFO 2018-08-30T15:45:35Z: websocat::sessionserve: Finished\n INFO 2018-08-30T15:45:35Z: websocat::stdio_peer: Restoring blocking status for stdin\n INFO 2018-08-30T15:45:35Z: websocat::stdio_peer: Restoring blocking status for stdin\n```\n\nThis approach can also be used in Websocat builds that do not support SSL.\n\n## Listening wss:// for development purposes\n\n```\n$ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365\nGenerating a 4096 bit RSA private key\n..........++\n.........................++\nwriting new private key to 'key.pem'\nEnter PEM pass phrase:1234\nVerifying - Enter PEM pass phrase:1234\n-----\nYou are about to be asked to enter information that will be incorporated\ninto your certificate request.\nWhat you are about to enter is what is called a Distinguished Name or a DN.\nThere are quite a few fields but you can leave some blank\nFor some fields there will be a default value,\nIf you enter '.', the field will be left blank.\n-----\nCountry Name (2 letter code) [AU]:\nState or Province Name (full name) [Some-State]:\nLocality Name (eg, city) []:\nOrganization Name (eg, company) [Internet Widgits Pty Ltd]:\nOrganizational Unit Name (eg, section) []:\nCommon Name (e.g. server FQDN or YOUR name) []:\nEmail Address []:\n\n$ openssl pkcs12 -export -out q.pkcs12 -inkey key.pem -in cert.pem\nEnter pass phrase for key.pem:1234\nEnter Export Password:<empty>\nVerifying - Enter Export Password:<empty>\n\n$ websocat --pkcs12-der=q.pkcs12 -s 1234\nListening on wss://127.0.0.1:1234/\n```\n\nThere is a pre-generated certificate `test.pkcs12` included in Git.\n\nWorkaround method for creating a `wss://` server:\n\n```\nsocat openssl-listen:1234,cert=cert.pem,key=key.pem,verify=0,fork,reuseaddr system:\"websocat -t inetd-ws\\\\: open-fd\\\\:2\"\n```\n\n# Proxy servers\n\n## Connect to a WebSocket using a SOCKS5 proxy\n\nThere is internal SOCKS5 client now, but sometimes external client is better:\n\n    websocat -v -t - --ws-c-uri=ws://echo.websocket.org ws-c:cmd:'SOCKS5_PASSWORD=a connect-proxy -S a@127.0.0.1:9050 echo.websocket.org 80'\n\n## Connect to a WebSocket using HTTP proxy\n\n    websocat -v -t - --ws-c-uri=ws://echo.websocket.org ws-c:cmd:'connect-proxy -H 127.0.0.1:9051 echo.websocket.org 80'\n\n\n## Listen WebSocket on SOCKS5 server side and connect to it\n\n```\ncat > port_obtained << \\EOF\n#!/bin/sh\necho Remote port opened: $1\nwebsocat -t -1 literal:\"Roundtrip using SOCKS server\" ws://132.148.129.183:$1/\nEOF\n\nchmod +x port_obtained\n\nwebsocat -E -t ws-u:socks5-bind:tcp:132.148.129.183:14124 - --socks5-destination 255.255.255.255:65535 --socks5-bind-script ./port_obtained\nRemote port opened: 53467\nRoundtrip using SOCKS server\n\nwebsocat -v -E -t ws-u:socks5-bind:tcp:132.148.129.183:14124 - --socks5-destination 255.255.255.255:65535 --socks5-bind-script ./port_obtained \n INFO 2018-08-29T22:04:42Z: websocat::lints: Auto-inserting the line mode\n INFO 2018-08-29T22:04:42Z: websocat::sessionserve: Serving Message2Line(WsServer(SocksBind(TcpConnect(V4(132.148.129.183:14124))))) to Line2Message(Stdio) with Options { websocket_text_mode: true, websocket_protocol: None, udp_oneshot_mode: false, unidirectional: false, unidirectional_reverse: false, exit_on_eof: true, oneshot: false, unlink_unix_socket: false, exec_args: [], ws_c_uri: \"ws://0.0.0.0/\", linemode_strip_newlines: false, linemode_strict: false, origin: None, custom_headers: [], websocket_version: None, websocket_dont_close: false, one_message: false, no_auto_linemode: false, buffer_size: 65536, broadcast_queue_len: 16, read_debt_handling: Warn, linemode_zero_terminated: false, restrict_uri: None, serve_static_files: [], exec_set_env: false, reuser_send_zero_msg_on_disconnect: false, process_zero_sighup: false, process_exit_sighup: false, socks_destination: Some(SocksSocketAddr { host: Ip(V4(255.255.255.255)), port: 65535 }), auto_socks5: None, socks5_bind_script: Some(\"./port_obtained\") }\n INFO 2018-08-29T22:04:43Z: websocat::net_peer: Connected to TCP\n INFO 2018-08-29T22:04:46Z: websocat::proxy_peer: SOCKS5 connect/bind: SocksSocketAddr { host: Ip(V4(0.0.0.0)), port: 34020 }\nRemote port opened: 34020\n INFO 2018-08-29T22:04:46Z: websocat::proxy_peer: SOCKS5 remote connected: SocksSocketAddr { host: Ip(V4(104.131.203.210)), port: 58836 }\n INFO 2018-08-29T22:04:47Z: websocat::ws_server_peer: Incoming connection to websocket: /\n INFO 2018-08-29T22:04:47Z: websocat::ws_server_peer: Upgraded\n INFO 2018-08-29T22:04:47Z: websocat::stdio_peer: get_stdio_peer (async)\n INFO 2018-08-29T22:04:47Z: websocat::stdio_peer: Setting stdin to nonblocking mode\n INFO 2018-08-29T22:04:47Z: websocat::stdio_peer: Installing signal handler\nRoundtrip using SOCKS server\n INFO 2018-08-29T22:04:47Z: websocat::sessionserve: Forward finished\n INFO 2018-08-29T22:04:47Z: websocat::sessionserve: Forward shutdown finished\n INFO 2018-08-29T22:04:47Z: websocat::sessionserve: One of directions finished\n INFO 2018-08-29T22:04:47Z: websocat::stdio_peer: Restoring blocking status for stdin\n INFO 2018-08-29T22:04:47Z: websocat::stdio_peer: Restoring blocking status for stdin\n```\n\n# Persistent client connection\n\nSuppose there is WebSocket server which replies exactly one WebSocket text message for each received WebSocket request.\n\nYou want to have a persistent WebSocket client connection to that server and issue multiple requests from a script.\n\nYou can do something like this:\n\n```\n$ websocat -t -E tcp-l:127.0.0.1:1234  reuse-raw:ws://echo.websocket.org --max-messages-rev 1&\n[1] 864\n\n$ WS_PID=$!\n\n$ echo 'Hello 1' | nc 127.0.0.1 1234\nHello 1\n\n$ echo 'World 2' | nc 127.0.0.1 1234\nWorld 2\n\n$ kill $WS_PID\n```\n\nThis scheme is unfortunately unreliable: if client is disconnected before it can receive a message,\nthe message can get delivered to next connected client.\n\n\n# Configuring Nginx to forward Websocket connections\n\nUsual `proxy_pass` is not enough.\n\nYou need something like this:\n\n```\nlocation /mywebsocket {\n    proxy_read_timeout 1d;\n    proxy_send_timeout 1d;\n    proxy_pass http://localhost:8123;\n    #proxy_pass http://unix:/tmp/unixsocket_websocat;\n    proxy_http_version 1.1;\n    proxy_set_header Upgrade $http_upgrade;\n    proxy_set_header Connection \"upgrade\";\n}\n```\n\n# Debugging connection issues by dumping traffic.\n\nIf you want to log information that is passing though Websocat to console, you can use `log:` filter (overlay).\n\nThis allows inspecting traffic at various levels. For Websocat versions earlier than 1.7, you can use `ws-c:cmd:` trick instead.\n\nExample sessions:\n\n## At message level\n\n```\n$ websocat  -t - --ws-c-uri=wss://echo.websocket.org log:ws-c:ssl:tcp:echo.websocket.org:443\n[WARN  websocat::ssl_peer] Connected to TLS without proper verification of certificate. Use --tls-domain option.\nasdf\nWRITE 5 \"asdf\\n\"\nREAD 5 \"asdf\\n\"\nasdf\n12345\nWRITE 6 \"12345\\n\"\nREAD 6 \"12345\\n\"\n12345\n```\n\n## At HTTP and Websocket protocol level\n\n```\n$ websocat  -t - --ws-c-uri=wss://echo.websocket.org ws-c:log:ssl:tcp:echo.websocket.org:443\n[WARN  websocat::ssl_peer] Connected to TLS without proper verification of certificate. Use --tls-domain option.\nWRITE 157 \"GET / HTTP/1.1\\r\\nHost: echo.websocket.org\\r\\nConnection: Upgrade\\r\\nUpgrade: websocket\\r\\nSec-WebSocket-Version: 13\\r\\nSec-WebSocket-Key: lnQ/ReYmkEBvBVajdASTHg==\\r\\n\\r\\n\"\nREAD 201 \"HTTP/1.1 101 Web Socket Protocol Handshake\\r\\nConnection: Upgrade\\r\\nDate: Sun, 21 Feb 2021 20:59:59 GMT\\r\\nSec-WebSocket-Accept: uZ5OAMgIFtQE5WRsqRRlA+DM0UI=\\r\\nServer: Kaazing Gateway\\r\\nUpgrade: websocket\\r\\n\\r\\n\"\nABCDE\nWRITE 12 \"\\x81\\x86\\xd6\\xa90\\x97\\x97\\xebs\\xd3\\x93\\xa3\"\nREAD 8 \"\\x81\\x06ABCDE\\n\"\nABCDE\n56789\nWRITE 12 \"\\x81\\x86\\x8aH\\xe8b\\xbf~\\xdfZ\\xb3B\"\nREAD 8 \"\\x81\\x0656789\\n\"\n56789\nWRITE 6 \"\\x88\\x80u\\xbfoz\"\nREAD 2 \"\\x88\\x00\"\n```\n\n## At TCP stream level\n\n```\n$ websocat  -t - --ws-c-uri=wss://echo.websocket.org --tls-domain=echo.websocket.org ws-c:ssl:log:tcp:echo.websocket.org:443\nWRITE 517 \"\\x16\\x03\\x01\\x02\\x00\\x01\\x00\\x01\\xfc\\x03\\x03\\x0e(r\\xec\\xbbXr\\xacb\\xe2\\x0b+\\x0f\\xdc\\xfa\\x17\\xb7R`\\x1e\\xda\\xb42e\\xd7\\xf2\\xdd\\xd24O;\\xd9 Y\\t2\\xe9kI\\xa5I\\xd4\\xee)p6\\xd6\\xbf\\x8dE:`\\xe8]\\x7fVN\\x9e\\x10\\xe4\\x7f8\\xa2:\\x98\\x00>\\x13\\x02\\x13\\x03\\x13\\x01\\xc0,\\xc00\\x00\\x9f\\xcc\\xa9\\xcc\\xa8\\xcc\\xaa\\xc0+\\xc0/\\x00\\x9e\\xc0$\\xc0(\\x00k\\xc0#\\xc0\\'\\x00g\\xc0\\n\\xc0\\x14\\x009\\xc0\\t\\xc0\\x13\\x003\\x00\\x9d\\x00\\x9c\\x00=\\x00<\\x005\\x00/\\x00\\xff\\x01\\x00\\x01u\\x00\\x00\\x00\\x17\\x00\\x15\\x00\\x00\\x12echo.websocket.org\\x00\\x0b\\x00\\x04\\x03\\x00\\x01\\x02\\x00\\n\\x00\\x0c\\x00\\n\\x00\\x1d\\x00\\x17\\x00\\x1e\\x00\\x19\\x00\\x18\\x00#\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\r\\x000\\x00.\\x04\\x03\\x05\\x03\\x06\\x03\\x08\\x07\\x08\\x08\\x08\\t\\x08\\n\\x08\\x0b\\x08\\x04\\x08\\x05\\x08\\x06\\x04\\x01\\x05\\x01\\x06\\x01\\x03\\x03\\x02\\x03\\x03\\x01\\x02\\x01\\x03\\x02\\x02\\x02\\x04\\x02\\x05\\x02\\x06\\x02\\x00+\\x00\\t\\x08\\x03\\x04\\x03\\x03\\x03\\x02\\x03\\x01\\x00-\\x00\\x02\\x01\\x01\\x003\\x00&\\x00$\\x00\\x1d\\x00 \\xa0t\\xa8\\x9d\\xd8t\\x06E\\x94\\xba+\\xa7\\xcf\\x90**W\\x8dS\\xd1\\xf4\\xd4\\xf7\\x06\\xda\\xa4B\\x9e\\xeb\\xa4\\xf3\\x10\\x00\\x15\\x00\\xc1\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\nREAD 5 \"\\x16\\x03\\x03\\n\\x00\"\nREAD 2560 \"\\x02\\x00\\x00M\\x03\\x03`2\\xca\\x95\\x17H\\xaa\\x9f\\x7fz[O>\\x0e\\xc8x\\xd9\\x8d\\xe71\\xac\\xffJ\\xb8\\xd8\\x8d\\xe4LB2\\xee\\x80 `2\\xca\\x95\\x93;2\\xdc\\x04@;T\\xc23Ei\\x8ax\\xfa\\xb8y\\x12\\'\\x05(\\xce8|^Fv\\x97\\x00/\\x00\\x00\\x05\\xff\\x01\\x00\\x01\\x00\\x0b\\x00\\t\\xa7\\x00\\t\\xa4\\x00\\x0550\\x82\\x0510\\x82\\x04\\x19\\xa0\\x03\\x02\\x01\\x02\\x02\\x12\\x04\\x00\\xd5\\xa4Naq\\xe3S\\xeb\\xc7\\xc8\\x82\\xb3g\\xc5\\xdeq0\\r\\x06\\t*\\x86H\\x86\\xf7\\r\\x01\\x01\\x0b\\x05\\x00021\\x0b0\\t\\x06\\x03U\\x04\\x06\\x13\\x02US1\\x160\\x14\\x06\\x03U\\x04\\n\\x13\\rLet\\'s Encrypt1\\x0b0\\t\\x06\\x03U\\x04\\x03\\x13\\x02R30\\x1e\\x17\\r210104172430Z\\x17\\r210404172430Z0\\x181\\x160\\x14\\x06\\x03U\\x04\\x03\\x13\\rwebsocket.org0\\x82\\x01\\\"0\\r\\x06\\t*\\x86H\\x86\\xf7\\r\\x01\\x01\\x01\\x05\\x00\\x03\\x82\\x01\\x0f\\x000\\x82\\x01\\n\\x02\\x82\\x01\\x01\\x00\\xa56O\\xbfj\\xb6\\'\\x8a<#\\xcf\\xce=\\x1d\\xb4\\x1d\\x0fI\\xffW\\\\\\xd9\\xdb\\xack\\xd3\\xed;<?\\xb9K\\xfcf;\\xec\\x98-\\r\\xa0KG\\xd5\\xec\\x9c\\xdeIY\\x89\\x97\\x83\\xfc\\xc7A7\\x89\\xa9\\xf41\\x0b\\xe6g3\\n\\xb9nH\\x87c\\xe3K\\x88\\xae\\x88\\x97\\x93#\\x9d\\x19f\\x8b\\xbf\\xe6\\xb7\\x9d\\x87\\xc9\\xee\\x94\\x94\\xaflz\\xeb_P\\x1eG\\xeb\\x88Q\\n\\x9f\\x95VnR1\\xed\\x1a/\\xee\\xe5\\x87\\x1a\\xec\\x9c\\xcd\\xff!\\x04\\xb8\\x8c\\x9b\\x9c&]\\xb8\\xe4=\\xb1\\xad\\xb0R\\x83~\\xa9,\\xb4ct\\x17\\xb6\\x9f\\x82\\xda\\xf6\\xae&\\xed\\xdf\\xfe\\x91\\x851\\x91\\xafG8\\x14R\\xf2\\xf8e9\\xd8\\x0bTsc\\xa6\\x98\\x9dBi%\\xfd\\xaa\\xa7\\\"\\x17{=\\x90a\\x12os\\x8c|\\x94M\\xeeD\\xc7v\\xb6\\x04\\xa1\\xd6\\xda\\x9d_\\xb1\\xd0\\xb5\\xb1;\\x08\\xd72\\x89+\\xa9W\\xd7\\x9e\\xf0\\x86D\\xe8\\xbf\\x81\\x0b\\xf7\\xa9nca|\\xd4SK}\\x02\\xdf7\\xa7\\xa3\\xd5MD\\x80b\\x19\\xa5\\xb2(\\x9c*\\x87\\xe0\\x9b\\x9b\\xf6\\xe3\\x02\\x03\\x01\\x00\\x01\\xa3\\x82\\x02Y0\\x82\\x02U0\\x0e\\x06\\x03U\\x1d\\x0f\\x01\\x01\\xff\\x04\\x04\\x03\\x02\\x05\\xa00\\x1d\\x06\\x03U\\x1d%\\x04\\x160\\x14\\x06\\x08+\\x06\\x01\\x05\\x05\\x07\\x03\\x01\\x06\\x08+\\x06\\x01\\x05\\x05\\x07\\x03\\x020\\x0c\\x06\\x03U\\x1d\\x13\\x01\\x01\\xff\\x04\\x020\\x000\\x1d\\x06\\x03U\\x1d\\x0e\\x04\\x16\\x04\\x14\\x04c\\xfdH\\xa3m\\xbf\\x01B%\\x98\\x95\\x9cP\\xb6\\x10\\x96\\xc6i\\x1c0\\x1f\\x06\\x03U\\x1d#\\x04\\x180\\x16\\x80\\x14\\x14.\\xb3\\x17\\xb7XV\\xcb\\xaeP\\t@\\xe6\\x1f\\xaf\\x9d\\x8b\\x14\\xc2\\xc60U\\x06\\x08+\\x06\\x01\\x05\\x05\\x07\\x01\\x01\\x04I0G0!\\x06\\x08+\\x06\\x01\\x05\\x05\\x070\\x01\\x86\\x15http://r3.o.lencr.org0\\\"\\x06\\x08+\\x06\\x01\\x05\\x05\\x070\\x02\\x86\\x16http://r3.i.lencr.org/0)\\x06\\x03U\\x1d\\x11\\x04\\\"0 \\x82\\x0f*.websocket.org\\x82\\rwebsocket.org0L\\x06\\x03U\\x1d \\x04E0C0\\x08\\x06\\x06g\\x81\\x0c\\x01\\x02\\x0107\\x06\\x0b+\\x06\\x01\\x04\\x01\\x82\\xdf\\x13\\x01\\x01\\x010(0&\\x06\\x08+\\x06\\x01\\x05\\x05\\x07\\x02\\x01\\x16\\x1ahttp://cps.letsencrypt.org0\\x82\\x01\\x04\\x06\\n+\\x06\\x01\\x04\\x01\\xd6y\\x02\\x04\\x02\\x04\\x81\\xf5\\x04\\x81\\xf2\\x00\\xf0\\x00v\\x00\\x94 \\xbc\\x1e\\x8e\\xd5\\x8dl\\x88s\\x1f\\x82\\x8b\\\",\\r\\xd1\\xdaM^lO\\x94=a\\xdbN/XM\\xa2\\xc2\\x00\\x00\\x01v\\xce\\xa4\\xb7\\xce\\x00\\x00\\x04\\x03\\x00G0E\\x02!\\x00\\x9df\\x0b\\xa0\\xc2?\\x02\\x0c;V1\\x04B\\xf8\\xbc\\x80\\xf3ToUi\\x98i\\xfb\\x08\\xb1\\x1d2\\xed\\xd7\\n=\\x02 b 2b\\x8d\\x1b\\xac2\\xf5-:\\xad\\x8f\\r\\xaa\\xc7\\xccR\\xc3\\xba\\xd5kz#g.\\x85c\\xb2\\xf1\\x9b\\x86\\x00v\\x00\\xf6\\\\\\x94/\\xd1w0\\\"\\x14T\\x18\\x080\\x94V\\x8e\\xe3M\\x13\\x193\\xbf\\xdf\\x0c/ \\x0b\\xccN\\xf1d\\xe3\\x00\\x00\\x01v\\xce\\xa4\\xb7\\xc8\\x00\\x00\\x04\\x03\\x00G0E\\x02 o\\xb2V\\x9fH\\xf6\\xfe\\x88\\x08diV\\x9d\\x91\\x06Gj\\x02\\xad(\\xaa\\x1f\\xfa\\xdd\\x13\\xe6\\xe2Z\\r\\x8fD\\xae\\x02!\\x00\\x88\\xf3\\xfd\\xd6\\x04\\xef\\x03\\\"\\x8c=\\xb3dI\\x19\\xdd\\xa8\\xf2P\\xca#*\\t\\xdb\\x95B9\\xee\\xd6\\x8a\\xc1\\t[0\\r\\x06\\t*\\x86H\\x86\\xf7\\r\\x01\\x01\\x0b\\x05\\x00\\x03\\x82\\x01\\x01\\x00W\\xde]\\xac\\xc8;=\\xc1\\xf7\\x84#\\x08\\\\!W\\xe6\\xe6O\\x9a\\x81\\x86\\xefRg~\\xff\\xc1\\xf5\\x04q\\x89\\xb1\\x96\\xc9c\\t%l\\xc0\\xe9\\xee\\xaba\\x84o\\xc2u<o\\xaf(\\xad:\\x14\\x94:\\x97\\x89\\x12?\\x9f\\x80n\\x11\\xa87\\x9e\\xdbw\\xf8\\xde\\xde\\x9c\\x1e\\xc3\\xe3\\xdb\\xa3.!R\\xa2\\x8eM\\xb2z\\xbc\\xb8\\xc9\\x04\\x9b\\xc8Q\\xff\\x8e\\xea\\x8c\\x813\\x9e\\xc8WOP\\xdc\\x9e\\xbe5P\\xc8\\xdeq\\xdf\\xc9\\x0ek\\xa9\\x8f\\xfc\\xff\\xee\\xd5\\xcf\\x95U\\xd0\\x0f\\x1a]\\xa2\\xb0(y\\xaa\\xb0(&N^~\\xd8\\x7f`\\'\\x85\\xeff\\xc1\\xce\\x9f1\\xaf\\xe1U\\xe3\\x89t\\x14VE\\xa3\\xf1]\\xa7\\xf9K\\xbeV\\x99\\xbb\\x1c\\xc4\\xe2|\\xf5Pg\\x14\\xde\\xf7\\xeb\\\"Z(\\xa1D=#1\\xcc\\x1f\\xa9\\xad=\\xbe\\xb2\\x01j\\x02\\xc9\\xd0\\x96\\n3\\xb9(b>\\xc4?7\\x8d)\\x1bOf\\xf4\\xfd\\xefw\\xdb\\xbb\\xf6\\x9cN\\xf4-\\x1a.8Th\\xa0q\\xfe\\xf5\\xb0\\xcf\\x9f/\\x14\\xb4\\xbb \\xb8\\xfd\\xd24Yg?}\\xd5L\\xde\\x07\\x00\\x04i0\\x82\\x04e0\\x82\\x03M\\xa0\\x03\\x02\\x01\\x02\\x02\\x10@\\x01u\\x04\\x83\\x14\\xa4\\xc8!\\x8c\\x84\\xa9\\x0c\\x16\\xcd\\xdf0\\r\\x06\\t*\\x86H\\x86\\xf7\\r\\x01\\x01\\x0b\\x05\\x000?1$0\\\"\\x06\\x03U\\x04\\n\\x13\\x1bDigital Signature Trust Co.1\\x170\\x15\\x06\\x03U\\x04\\x03\\x13\\x0eDST Root CA X30\\x1e\\x17\\r201007192140Z\\x17\\r210929192140Z021\\x0b0\\t\\x06\\x03U\\x04\\x06\\x13\\x02US1\\x160\\x14\\x06\\x03U\\x04\\n\\x13\\rLet\\'s Encrypt1\\x0b0\\t\\x06\\x03U\\x04\\x03\\x13\\x02R30\\x82\\x01\\\"0\\r\\x06\\t*\\x86H\\x86\\xf7\\r\\x01\\x01\\x01\\x05\\x00\\x03\\x82\\x01\\x0f\\x000\\x82\\x01\\n\\x02\\x82\\x01\\x01\\x00\\xbb\\x02\\x15(\\xcc\\xf6\\xa0\\x94\\xd3\\x0f\\x12\\xec\\x8dU\\x92\\xc3\\xf8\\x82\\xf1\\x99\\xa6zB\\x88\\xa7]&\\xaa\\xb5+\\xb9\\xc5L\\xb1\\xaf\\x8ek\\xf9u\\xc8\\xa3\\xd7\\x0fG\\x94\\x14U5W\\x8c\\x9e\\xa8\\xa29\\x19\\xf5\\x82<B\\xa9Nn\\xf5;\\xc3.\\xdb\\x8d\\xc0\\xb0\\\\\\xf3Y8\\xe7\\xed\\xcfi\\xf0Z\\x0b\\x1b\\xbe\\xc0\\x94$%\\x87\\xfa7q\\xb3\\x13\\xe7\\x1c\\xac\\xe1\\x9b\\xef\\xdb\\xe4;ERE\\x96\\xa9\\xc1S\\xce4\\xc8R\\xee\\xb5\\xae\\xed\\x8f\\xde`p\\xe2\\xa5T\\xab\\xb6m\\x0e\\x97\\xa5@4k+\\xd3\\xbcf\\xebf4|\\xfak\\x8b\\x8fW)\\x99\\xf80\\x17]\\xbaro\\xfb\\x81\\xc5\\xad\\xd2\\x86X=\\x17\\xc7\\xe7\\t\\xbb\\xf1+\\xf7\\x86\\xdc\\xc1\\xdaq]\\xd4F\\xe3\\xcc\\xad%\\xc1\\x88\\xbc`guf\\xb3\\xf1\\x18\\xf7\\xa2\\\\\\xe6S\\xff:\\x88\\xb6G\\xa5\\xff\\x13\\x18\\xea\\x98\\tw?\\x9dS\\xf9\\xcf\\x01\\xe5\\xf5\\xa6p\\x17\\x14\\xafc\\xa4\\xff\\x99\\xb3\\x93\\x9d\\xdcS\\xa7\\x06\\xfeH\\x85\\x1d\\xa1i\\xae%u\\xbb\\x13\\xccR\\x03\\xf5\\xedQ\\xa1\\x8b\\xdb\\x15\\x02\\x03\\x01\\x00\\x01\\xa3\\x82\\x01h0\\x82\\x01d0\\x12\\x06\\x03U\\x1d\\x13\\x01\\x01\\xff\\x04\\x080\\x06\\x01\\x01\\xff\\x02\\x01\\x000\\x0e\\x06\\x03U\\x1d\\x0f\\x01\\x01\\xff\\x04\\x04\\x03\\x02\\x01\\x860K\\x06\\x08+\\x06\\x01\\x05\\x05\\x07\\x01\\x01\\x04?0=0;\\x06\\x08+\\x06\\x01\\x05\\x05\\x070\\x02\\x86/http://apps.identrust.com/roots/dstrootcax3.p7c0\\x1f\\x06\\x03U\\x1d#\\x04\\x180\\x16\\x80\\x14\\xc4\\xa7\\xb1\\xa4{,q\\xfa\\xdb\\xe1K\\x90u\\xff\\xc4\\x15`\\x85\\x89\\x100T\\x06\\x03U\\x1d \\x04M0K0\\x08\\x06\\x06g\\x81\\x0c\\x01\\x02\\x010?\\x06\\x0b+\\x06\\x01\\x04\\x01\\x82\\xdf\\x13\\x01\\x01\\x01000.\\x06\\x08+\\x06\\x01\\x05\\x05\\x07\\x02\\x01\\x16\\\"http://cps.root-x1.letsencrypt.org0<\\x06\\x03U\\x1d\\x1f\\x0450301\\xa0/\\xa0-\\x86+http://crl.identrust.com/DSTROOTCAX3CRL.crl0\\x1d\\x06\\x03U\\x1d\\x0e\\x04\\x16\\x04\\x14\\x14.\\xb3\\x17\\xb7XV\\xcb\\xaeP\\t@\\xe6\\x1f\\xaf\\x9d\\x8b\\x14\\xc2\\xc60\\x1d\\x06\\x03U\\x1d%\\x04\\x160\\x14\\x06\\x08+\\x06\\x01\\x05\\x05\\x07\\x03\\x01\\x06\\x08+\\x06\\x01\\x05\\x05\\x07\\x03\\x020\\r\\x06\\t*\\x86H\\x86\\xf7\\r\\x01\\x01\\x0b\\x05\\x00\\x03\\x82\\x01\\x01\\x00\\xd9L\\xe0\\xc9\\xf5\\x84\\x8871\\xdb\\xbb\\x13\\xe2\\xb3\\xfc\\x8bkb\\x12lX\\xb7I~<\\x02\\xb7\\xa8\\x1f(a\\xeb\\xce\\xe0.s\\xefI\\x07z5\\x84\\x1f\\x1d\\xadh\\xf0\\xd8\\xfeV\\x81/m\\x7fX\\xa6n56\\x10\\x1cs\\xc3\\xe5\\xbdm^\\x01\\xd7nr\\xfb*\\xa0\\xb8\\xd3Wd\\xe5[\\xc2i\\xd4\\xd0\\xb2\\xf7|K\\xc3\\x17\\x8e\\x88rs\\xdc\\xfd\\xfcm\\xbd\\xe3\\xc9\\x0b\\x8ea:\\x16X}t6+U\\x80=\\xc7c\\xbe\\x84C\\xc69\\xa1\\x0ekW\\x9e?)\\xc1\\x80\\xf6\\xb2\\xbdG\\xcb\\xaa0l\\xb72\\xe1YT\\x0b\\x18\\t\\x17^cl\\xfb\\x96g<\\x1cs\\x0c\\x93\\x8b\\xc6\\x11v$\\x86\\xde@\\x07\\x07\\xe4}-f\\xb5%\\xa3\\x96X\\xc8\\xea\\x80\\xee\\xcfi;\\x96\\xfc\\xe6\\x8d\\xc03\\xf3\\x89\\xf8)-\\x14\\x14-~\\xf0ap\\x95]\\xf7\\x0b\\xe5\\xc0\\xfb$\\xfa\\xec\\x8e\\xcba\\xc8\\xeecq(\\xa8,\\x05;w\\xef\\x9b^\\x03d\\xf0Q\\xd1\\xe4\\x85S\\\\\\xb0\\x02\\x97\\xd4~\\xc64\\xd2\\xce\\x10\\x00\\xe4\\xb1\\xdf:\\xc2\\xea\\x17\\xbe\\x0e\\x00\\x00\\x00\"\nWRITE 342 \"\\x16\\x03\\x03\\x01\\x06\\x10\\x00\\x01\\x02\\x01\\x00s\\xaa\\x90\\xf7\\x88\\xba\\xdcI\\xe0\\x81\\xcb\\xf4\\x93\\x1cbt#+^\\x13?N\\xc2\\x18\\xaf\\xab\\xb7\\xc7\\xf5\\xc6\\x8e\\xcaD\\xf9U\\xaak\\x96E\\xa6\\xeeH&F\\x0f\\x12\\x03\\xcf)}\\xb8\\xbc\\x9e\\xe2j\\x98\\xaa\\x1b\\\"L&D\\xac\\xef\\x7fe\\x0fu\\xf1B\\xf1\\x8b>0P\\xd067\\xdfBM\\xf0\\xd7r\\x13`\\x8e\\x88=\\xd0\\x9a\\xf8\\xfe\\x0c\\x01~\\xb8\\x87I\\xa5\\xcc\\x15s\\x95\\\"\\xa1\\xdb>3m4\\x92j&\\xa6_r\\x96\\xa0v\\xfa\\x12\\x14WH\\xfd\\xa6\\xa5\\x8dY\\xfa\\xd6(\\xa3\\xafT\\x18\\xca8\\xb8E\\xc2a\\xb7l\\x1e\\xd75\\x80_\\xb3\\x14}S\\xa2\\x1d\\x98\\xc4y\\x80W\\xf6\\xb3&\\xdf9\\x84O9~\\xbfd\\xd2\\xd7/\\xb7U\\xe1~\\xcb6\\x8a\\xf2\\x07\\x07\\xc1\\xb3\\xf1L\\x14\\xa3\\x0c\\xb2\\xf0(\\x99\\xa3;\\xff[\\x1d\\xa3p\\xe2\\xe9\\x9cZ5\\xaa\\xe3\\xf0\\xa4\\xb28\\xac\\x82e\\xb9\\x82\\xfcz\\xbf0\\x1f\\xce\\x9f\\x9d\\xcb\\x94\\x7f\\xc7\\xb8\\x8fH$\\xdcj+\\xfe\\xb7z\\x9b\\xe0\\xf0\\xd4\\xdct\\xa7\\xe7W\\x92A\\xa1e8\\xc7\\x14\\x03\\x03\\x00\\x01\\x01\\x16\\x03\\x03\\x00@\\xbb\\x00\\xba\\x14\\xcc\\xa1\\xa2@w mP=\\xd3T\\xaf\\\"\\xe6M!\\xa8\\xe8A\\xbb[\\xafx\\xbeDU}\\xe1/\\x8b\\x0e\\x8b\\x8a\\xe6\\x82\\x06\\xe5bHy\\xbcq{ \\xdb\\xb3o\\xae\\xb1\\x03\\x9e\\xa6\\xcf\\x19\\xee\\xf9\\x9b\\xb3@0\"\nREAD 5 \"\\x14\\x03\\x03\\x00\\x01\"\nREAD 1 \"\\x01\"\nREAD 5 \"\\x16\\x03\\x03\\x00@\"\nREAD 64 \"\\\\0\\xa6\\xfb\\x9fcks\\xe2o\\xd7L\\xc2\\xf7@\\x0f\\xaf\\xc8\\xb3\\xd9F\\xb0\\x1b\\xa3\\xe7\\xc6\\xfb\\xb4\\x12\\xf1\\xac8\\x9d0\\x89\\x96\\x89H\\xde\\xda\\n\\xaaN\\x90@\\xa9<#\\xe9_\\xa1\\xcex\\x11\\xe2\\x0f\\x87\\x1c\\x87 \\xbf\\'9\\xa4\"\nWRITE 213 \"\\x17\\x03\\x03\\x00\\xd0\\x8b\\xf1G\\xdcB\\x83\\xbf\\x0f\\xb0\\xf4\\x97YXk\\xe7ow\\xd3\\x81\\x89\\xa0\\xa1\\x8bG\\x99\\xeb\\xe3\\xfa\\xda\\xd4QW=\\x1d\\x06\\xadn#)L\\x08\\xa6\\x10\\xa4\\x03O\\xdf\\xd5\\xe2a\\xda\\xce\\x9b\\xe9f\\xdf\\xf4\\x14UzW\\xd8\\xc8\\x1c\\xa2\\xbf\\x00\\xc5\\x9c}\\x7fW;\\x9b\\xe8\\x9f\\xb0\\xaa\\x8c\\xd0\\xf9\\xe0\\xf7\\xf4\\\"\\xb6\\xb0\\nY\\xff\\x05\\x0bz\\xbc\\x9c\\x81\\xd0\\xf5\\xee\\x0bK\\xd4Q\\xd5l\\x81E\\xfb\\xdcW\\x12\\xe4\\xaa\\xe7\\xaf\\x1c\\x7f.\\xb1\\xd78\\x11\\xe6\\xfd\\xb6\\xb9\\xc5\\xd1\\xec\\x83\\xe5\\x16\\xb2P\\xbd\\x97\\xb5E\\x95\\'\\xf2\\xf8B\\xee\\x01S\\xf61&@-\\xa0\\x95e,H\\x07\\xb1\\x9c\\xba`\\xd9\\xf3\\x9b\\xe8\\xba\\x8c\\xaaqt_\\xe2t&f\\xd3\\xba\\xc3W\\xcc\\xb7;\\xa7\\xc9A\\x16\\x11$e\\xfd\\x1e\\x8eKF\\x0cO+\\x84\\x1e\\xc7\\x85\\x1b\\xf2\\\"Pou\\xda\"\nREAD 5 \"\\x17\\x03\\x03\\x00\\xf0\"\nREAD 240 \"3\\x14\\xe7g\\x12\\xc2I4\\x96\\x8f\\r$\\xb4\\xa2(\\xf2b\\xf5]\\x82\\xa77\\x88wJy\\x84\\xf4\\x8e\\xaf\\xa6\\x04\\x8d\\xed\\x7f\\xd6q\\x80o\\'0\\x1e\\x85\\xa1\\xf8\\x9buk\\xc2\\x9bx>\\xb4S\\xe3\\xa4\\xa2\\x88\\x8a\\xff\\xac\\x06\\x81\\xc4\\x98g,\\xb2\\xa9\\'\\xe2C=\\x90\\x9b\\xc4\\xd4\\x7f\\xa3\\xcffneE\\xee\\x86\\xae\\xd3\\x17\\xf3 (\\xb7\\xf8W\\xcd(\\x16\\x82s2\\xe7\\x03y81\\xe0r\\xa0C\\x82\\xedK\\x93u\\x87x,\\xf3\\xca\\xc7\\x84\\xa1\\xf5x \\x06Q5\\xe7\\x00%\\xb4\\x84\\xfaw\\x1f^R\\xc9o\\xb4\\x96\\xf0\\xa7\\x01\\xc5h\\x04.\\xc8Q\\xcfw\\t\\x8e\\xa8\\xf9\\x98\\x9a~q\\xb9/\\x9f.u\\x023Q8\\x95\\x8b\\x7f~\\xccx\\xa9_,\\xc7*<.\\x0e\\'jH\\x05\\x8b\\xe8\\xc2\\xee\\xd8\\xa4\\x12\\xe1\\xb6\\xd7FL\\xe7\\r\\xc1\\x07*\\xcb+\\xf0\\xc2Pq\\xed7\\t\\x1e\\xa4,\\x05\\xd0\\x0e\\xba\\xf1ABtL*\\xba\\xbb\\xe7~\\x1e\\xefO\\x85\\xbd\\xe6\\x84S\"\nqwert\nWRITE 69 \"\\x17\\x03\\x03\\x00@\\n\\x8c\\\\\\xa8b\\xae\\x05N\\xb9O\\x81\\xaej\\x9eM\\xf7$\\xf9\\x8f\\xc8\\xefz!\\x89.\\xfe\\x19\\x12`\\xbf{fJS\\xd5\\x15\\x84\\x0e\\xa5\\xf7\\\"4\\xa1\\xda[\\x95\\xe2#&\\xe8\\xd3\\x96\\xb8]\\xa4+~\\xb2Cc\\xfc6\\xb2|\"\nREAD 5 \"\\x17\\x03\\x03\\x000\"\nREAD 48 \"\\xb8uj&\\xda\\xed\\xf9\\xbc\\xbfn\\x9eS`\\xa3\\x10q\\x10[\\x05}\\xef\\xd6\\xbf\\t\\xea\\x14f\\xf9c\\xe6$\\xd1R:\\xdc\\xcc\\x88[\\x97\\xca\\x12\\x9f\\xc1pk\\x0e\\xf6o\"\nqwert\nzxcvbn\nWRITE 69 \"\\x17\\x03\\x03\\x00@\\xe5\\xf4\\x99xS\\xf6\\xad\\xf4\\x89?\\xbd\\x99=`a\\xab\\x8e\\xed\\xd83\\xa9-\\xabU{\\xfa~\\xd9h\\xf8\\x1a\\xfaS\\xfdD-\\xc4\\x84=:\\xae;\\x0b\\xccRHT\\xef\\x04E\\x927\\xbf~\\x0e:\\xb0\\x16\\x01\\xf5\\xd1\\x0f\\x1e\\xc9\"\nREAD 5 \"\\x17\\x03\\x03\\x000\"\nREAD 48 \"\\xa4\\x9b=\\x85\\xe3\\x8c\\x0c-*\\x08(Qu\\xd8\\x93\\xbde,c\\x1a\\x9c\\\"\\xc9o\\xa7\\xb5\\xe9eH6\\xa1\\xed\\xb9\\xcb+\\x13>nu^\\x1c\\x9d\\x1b2\\xc0\\x98\\x1d(\"\nzxcvbn\nWRITE 53 \"\\x17\\x03\\x03\\x000\\x17\\x19\\xf4>\\xddSG8\\xdd\\xcd\\x00\\xf2\\xf58\\x15n\\xbaY\\xbaU\\xf0H\\x8b\\t}\\xa5\\xaa\\xfbXy\\xc7f\\xc2r\\x9e\\x94dO\\xdc\\xaf\\xad\\xcc\\xcd\\x16\\x87\\xdd\\x19\\xb9\"\nREAD 5 \"\\x17\\x03\\x03\\x000\"\nREAD 48 \"j#\\x9d\\x17B\\x89\\xee\\x92\\x90\\xcaH6\\xf7PQe|p\\xed\\xf8,=\\x0f+\\x8a\\x10)\\xcf\\x06\\xb1\\x06\\xde\\x9eA>B\\xb7g\\xde\\xce\\xcc\\xfd\\x88\\x1c\\xf1-\\x96\\x00\"\n```\n\n# listening websockets from systemd\n\nSystemd units for WebSocket-to-localhost-SSH redirector.\n\n## `Accept=yes` mode\n\nEach client connection gets separate Websocat process.\n\n`/etc/systemd/system/qqq.socket`\n\n```\n[Unit]\nDescription=\"websocat\"\n\n[Socket]\nListenStream=/run/qqq.socket\n#ListenStream=127.0.0.1:1234 # also works for TCP\nAccept=yes\n\n[Install]\nWantedBy=sockets.target\n```\n\n`/etc/systemd/system/qqq@.service` (note the important `@` character).\n\n```\n[Unit]\nDescription=\"websocat\"\nRequires=qqq.socket\n\n[Service]\nType=simple\nExecStart=/opt/websocat -E -b ws-inetd: tcp:127.0.0.1:22\nStandardInput=socket\nNonBlocking=true\n\n[Install]\nWantedBy=multi-user.target\n```\n\n## `Accept=no` mode\n\nWebsocat is socket-activated by systemd and keeps on listening for more connections\n\nRequires new enough Websocat version with `--accept-from-fd` option.\n\n`/etc/systemd/system/qqq.socket`\n\n```\n[Unit]\nDescription=\"websocat\"\n\n[Socket]\nListenStream=/run/qqq.socket\n# does _not_ work with TCP socket currently\nAccept=no\n\n[Install]\nWantedBy=sockets.target\n```\n\n\n`/etc/systemd/system/qqq.service` (note the absence of  `@`).\n\n```\n[Unit]\nDescription=\"websocat\"\nRequires=qqq.socket\n\n[Service]\nExecStart=/opt/websocat -E -b --accept-from-fd l-ws-unix:3 tcp:127.0.0.1:22\n\n[Install]\nWantedBy=multi-user.target\n```\n\nwith `SocketUser=www-data` it can be combined with Nginx setup above.\n\nExample SSH client command: `ssh root@localhost -o 'ProxyCommand=/opt/websocat -E -b - ws-c:unix:/run/qqq.socket'`\n\n\n# Specifying distinct host names for resolving IP address, `Host: ` header and TLS.\n\nExample command line:\n\n    websocat -t - --ws-c-uri=wss://domain-for-host-header/ ws-c:tls:tcp:domain-or-ip-address-for-resolving:443 --tls-domain domain-for-checking-certificate\n\nOr without TLS:\n\n    websocat -t - --ws-c-uri=wss://domain-for-host-header/ ws-c:tcp:domain-or-ip-address-for-resolving:80\n"
  },
  {
    "path": "src/all_peers.rs",
    "content": "// This is an X-Macro.\n#[macro_export]\nmacro_rules! list_of_all_specifier_classes {\n    ($your_macro:ident) => {\n        $your_macro!($crate::ws_client_peer::WsClientClass);\n        #[cfg(feature = \"ssl\")]\n        $your_macro!($crate::ws_client_peer::WsClientSecureClass);\n        $your_macro!($crate::ws_server_peer::WsTcpServerClass);\n        $your_macro!($crate::ws_server_peer::WsInetdServerClass);\n        $your_macro!($crate::ws_server_peer::WsUnixServerClass);\n        $your_macro!($crate::ws_server_peer::WsAbstractUnixServerClass);\n        $your_macro!($crate::ws_server_peer::WsServerClass);\n        $your_macro!($crate::ws_lowlevel_peer::WsLlClientClass);\n        $your_macro!($crate::ws_lowlevel_peer::WsLlServerClass);\n\n        #[cfg(feature = \"ssl\")]\n        $your_macro!($crate::ssl_peer::WssListenClass);\n\n        $your_macro!($crate::http_peer::HttpRequestClass);\n        $your_macro!($crate::http_peer::HttpClass);\n        $your_macro!($crate::http_peer::HttpPostSseClass);\n        \n\n        #[cfg(all(unix, feature = \"unix_stdio\"))]\n        $your_macro!($crate::stdio_peer::AsyncStdioClass);\n        #[cfg(all(unix, feature = \"unix_stdio\"))]\n        $your_macro!($crate::stdio_peer::InetdClass);\n        #[cfg(not(all(unix, feature = \"unix_stdio\")))]\n        $your_macro!($crate::stdio_threaded_peer::InetdClass);\n\n        $your_macro!($crate::net_peer::TcpConnectClass);\n        $your_macro!($crate::net_peer::TcpListenClass);\n\n        #[cfg(feature = \"ssl\")]\n        $your_macro!($crate::ssl_peer::TlsConnectClass);\n        #[cfg(feature = \"ssl\")]\n        $your_macro!($crate::ssl_peer::TlsAcceptClass);\n        #[cfg(feature = \"ssl\")]\n        $your_macro!($crate::ssl_peer::TlsListenClass);\n\n        #[cfg(feature = \"tokio-process\")]\n        $your_macro!($crate::process_peer::ShCClass);\n        #[cfg(feature = \"tokio-process\")]\n        $your_macro!($crate::process_peer::CmdClass);\n        #[cfg(feature = \"tokio-process\")]\n        $your_macro!($crate::process_peer::ExecClass);\n\n        $your_macro!($crate::file_peer::ReadFileClass);\n        $your_macro!($crate::file_peer::WriteFileClass);\n        $your_macro!($crate::file_peer::AppendFileClass);\n\n        $your_macro!($crate::primitive_reuse_peer::ReuserClass);\n        $your_macro!($crate::broadcast_reuse_peer::BroadcastReuserClass);\n        $your_macro!($crate::reconnect_peer::AutoReconnectClass);\n\n        $your_macro!($crate::ws_client_peer::WsConnectClass);\n\n        $your_macro!($crate::net_peer::UdpConnectClass);\n        $your_macro!($crate::net_peer::UdpListenClass);\n\n        #[cfg(all(unix, feature = \"unix_stdio\"))]\n        $your_macro!($crate::stdio_peer::OpenAsyncClass);\n        #[cfg(all(unix, feature = \"unix_stdio\"))]\n        $your_macro!($crate::stdio_peer::OpenFdAsyncClass);\n\n        $your_macro!($crate::stdio_threaded_peer::ThreadedStdioClass);\n        $your_macro!($crate::stdio_threaded_peer::StdioClass);\n\n        #[cfg(unix)]\n        $your_macro!($crate::unix_peer::UnixConnectClass);\n        #[cfg(unix)]\n        $your_macro!($crate::unix_peer::UnixListenClass);\n        #[cfg(unix)]\n        $your_macro!($crate::unix_peer::UnixDgramClass);\n        #[cfg(unix)]\n        $your_macro!($crate::unix_peer::AbstractConnectClass);\n        #[cfg(unix)]\n        $your_macro!($crate::unix_peer::AbstractListenClass);\n        #[cfg(unix)]\n        $your_macro!($crate::unix_peer::AbstractDgramClass);\n\n        #[cfg(all(windows,feature = \"windows_named_pipes\"))]\n        $your_macro!($crate::windows_np_peer::NamedPipeConnectClass);\n\n        $your_macro!($crate::line_peer::Message2LineClass);\n        $your_macro!($crate::line_peer::Line2MessageClass);\n        $your_macro!($crate::lengthprefixed_peer::LengthPrefixedClass);\n        $your_macro!($crate::foreachmsg_peer::ForeachmsgClass);\n        $your_macro!($crate::mirror_peer::MirrorClass);\n        $your_macro!($crate::mirror_peer::LiteralReplyClass);\n        $your_macro!($crate::trivial_peer::CloggedClass);\n        $your_macro!($crate::trivial_peer::LiteralClass);\n        $your_macro!($crate::trivial_peer::AssertClass);\n        $your_macro!($crate::trivial_peer::Assert2Class);\n\n        $your_macro!($crate::trivial_peer::LogClass);\n\n        #[cfg(all(target_os = \"linux\", feature = \"seqpacket\"))]\n        $your_macro!($crate::unix_peer::unix_seqpacket_peer::SeqpacketConnectClass);\n        #[cfg(all(target_os = \"linux\", feature = \"seqpacket\"))]\n        $your_macro!($crate::unix_peer::unix_seqpacket_peer::SeqpacketListenClass);\n\n        $your_macro!($crate::jsonrpc_peer::JsonRpcClass);\n        $your_macro!($crate::timestamp_peer::TimestampClass);\n\n        $your_macro!($crate::socks5_peer::SocksProxyClass);\n        $your_macro!($crate::socks5_peer::SocksBindClass);\n\n        #[cfg(feature = \"crypto_peer\")]\n        $your_macro!($crate::crypto_peer::CryptoClass);\n\n        $your_macro!($crate::trivial_peer::RandomClass);\n\n        #[cfg(feature = \"prometheus_peer\")]\n        $your_macro!($crate::prometheus_peer::PrometheusClass);\n\n        $your_macro!($crate::trivial_peer::ExitOnSpecificByteClass);\n        $your_macro!($crate::trivial_peer::DropOnBackpressureClass);\n\n        $your_macro!($crate::reconnect_peer::WaitForDataClass);\n    };\n}\n"
  },
  {
    "path": "src/broadcast_reuse_peer.rs",
    "content": "extern crate futures;\nextern crate tokio_io;\n\nuse futures::future::ok;\nuse std::cell::RefCell;\nuse std::rc::Rc;\n\nuse super::{brokenpipe, simple_err, wouldblock, BoxedNewPeerFuture, Peer};\n\nuse std::io::{Error as IoError, Read, Write};\nuse tokio_io::{AsyncRead, AsyncWrite};\n\nuse super::{once, ConstructParams, PeerConstructor, Specifier};\nuse futures::Async;\nuse futures::AsyncSink;\nuse futures::Future;\nuse futures::Sink;\nuse futures::Stream;\nuse crate::spawn_hack;\nuse std::ops::DerefMut;\n\nuse futures::unsync::mpsc;\n\ndeclare_slab_token!(BroadcastClientIndex);\nuse slab_typesafe::Slab;\n\n#[derive(Debug)]\npub struct BroadcastReuser(pub Rc<dyn Specifier>);\nimpl Specifier for BroadcastReuser {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        let mut reuser = p.global(GlobalState::default).clone();\n        let bs = p.program_options.buffer_size;\n        let ql = p.program_options.broadcast_queue_len;\n        let l2r = p.left_to_right.clone();\n        let inner = || self.0.construct(p).get_only_first_conn(l2r);\n        once(connection_reuser(&mut reuser, inner, bs, ql))\n    }\n    specifier_boilerplate!(singleconnect has_subspec globalstate);\n    self_0_is_subspecifier!(...);\n}\n\nspecifier_class!(\n    name = BroadcastReuserClass,\n    target = BroadcastReuser,\n    prefixes = [\n        \"broadcast:\",\n        \"reuse:\",\n        \"reuse-broadcast:\",\n        \"broadcast-reuse:\"\n    ],\n    arg_handling = subspec,\n    overlay = true,\n    MessageBoundaryStatusDependsOnInnerType,\n    SingleConnect,\n    help = r#\"\nReuse this connection for serving multiple clients, sending replies to all clients.\n\nMessages from any connected client get directed to inner connection,\nreplies from the inner connection get duplicated across all connected\nclients (and are dropped if there are none).\n\nIf WebSocket client is too slow for accepting incoming data,\nmessages get accumulated up to the configurable --broadcast-buffer, then dropped.\n\nExample: Simple data exchange between connected WebSocket clients\n\n    websocat -E ws-l:0.0.0.0:8800 reuse-broadcast:mirror:\n\"#\n);\n\ntype SailingBuffer = Rc<Vec<u8>>;\ntype Clients = Slab<BroadcastClientIndex, mpsc::Sender<SailingBuffer>>;\n\npub struct Broadcaster {\n    inner_peer: Peer,\n    clients: Clients,\n}\npub type HBroadCaster = Rc<RefCell<Option<Broadcaster>>>;\n\npub type GlobalState = HBroadCaster;\n\nstruct PeerHandleW(HBroadCaster);\nstruct PeerHandleR(\n    HBroadCaster,\n    mpsc::Receiver<SailingBuffer>,\n    BroadcastClientIndex,\n);\nstruct InnerPeerReader(HBroadCaster, Vec<u8>);\n\nimpl Future for InnerPeerReader {\n    type Item = ();\n    type Error = ();\n    fn poll(&mut self) -> futures::Poll<(), ()> {\n        loop {\n            let mut meb = self.0.borrow_mut();\n            let me = meb.as_mut().expect(\"Assertion failed 16293\");\n            match me.inner_peer.0.read(&mut self.1[..]) {\n                Ok(0) => {\n                    info!(\"Underlying peer finished\");\n                    return Ok(futures::Async::Ready(()));\n                }\n                Ok(n) => {\n                    if me.clients.is_empty() {\n                        info!(\"Dropping broadcast due to no clients being connected\");\n                        continue;\n                    };\n                    let sb = Rc::new(self.1[0..n].to_vec());\n                    for (_, client) in me.clients.iter_mut() {\n                        match client.start_send(sb.clone()) {\n                            Ok(AsyncSink::Ready) => match client.poll_complete() {\n                                Ok(Async::Ready(())) => {}\n                                Ok(Async::NotReady) => {\n                                    warn!(\"A client's sink is NotReady for poll_complete\");\n                                }\n                                Err(e) => {\n                                    warn!(\"A client's sink is in error state: {}\", e);\n                                }\n                            },\n                            Ok(AsyncSink::NotReady(_)) => {\n                                warn!(\"A client's sink is NotReady for start_send\");\n                            }\n                            Err(e) => {\n                                warn!(\"A client's sink is in error state: {}\", e);\n                            }\n                        };\n                    }\n                }\n                Err(e) => {\n                    if e.kind() == ::std::io::ErrorKind::WouldBlock {\n                        return Ok(Async::NotReady);\n                    }\n                    error!(\"Inner peer read failed: {}\", e);\n                    return Err(());\n                }\n            }\n        }\n    }\n}\n\nimpl Drop for PeerHandleR {\n    fn drop(&mut self) {\n        self.0\n            .borrow_mut()\n            .as_mut()\n            .expect(\"Assertion failed 16292\")\n            .clients\n            .remove(self.2);\n    }\n}\n\nimpl Read for PeerHandleR {\n    fn read(&mut self, b: &mut [u8]) -> Result<usize, IoError> {\n        loop {\n            return match self.1.poll() {\n                Ok(Async::Ready(Some(v))) => {\n                    if v.len() > b.len() {\n                        error!(\"Too big message dropped\");\n                        continue;\n                    }\n                    b[0..(v.len())].copy_from_slice(&v[..]);\n                    Ok(v.len())\n                }\n                Ok(Async::Ready(None)) => brokenpipe(),\n                Ok(Async::NotReady) => wouldblock(),\n                Err(()) => Err(simple_err(\"Something unexpected\".into())),\n            };\n        }\n\n        /*if let &mut Some(ref mut x) = self.0.borrow_mut().deref_mut() {\n            x.inner_peer.0.read(b) // To be changed\n        } else {\n            unreachable!()\n        }*/\n    }\n}\nimpl AsyncRead for PeerHandleR {}\n\nimpl Write for PeerHandleW {\n    fn write(&mut self, b: &[u8]) -> Result<usize, IoError> {\n        if let Some(ref mut x) = *self.0.borrow_mut().deref_mut() {\n            x.inner_peer.1.write(b)\n        } else {\n            unreachable!()\n        }\n    }\n    fn flush(&mut self) -> Result<(), IoError> {\n        if let Some(ref mut x) = *self.0.borrow_mut().deref_mut() {\n            x.inner_peer.1.flush()\n        } else {\n            unreachable!()\n        }\n    }\n}\nimpl AsyncWrite for PeerHandleW {\n    fn shutdown(&mut self) -> futures::Poll<(), IoError> {\n        if let Some(ref mut _x) = *self.0.borrow_mut().deref_mut() {\n            // Ignore shutdown attempts\n            Ok(futures::Async::Ready(()))\n        //_x.1.shutdown()\n        } else {\n            unreachable!()\n        }\n    }\n}\n\nfn makeclient(ps: HBroadCaster, queue_len: usize) -> Peer {\n    let (send, recv) = mpsc::channel(queue_len);\n    let k = ps\n        .borrow_mut()\n        .as_mut()\n        .expect(\"Assertion failed 16291\")\n        .clients\n        .insert(send);\n    let ph1 = PeerHandleR(ps.clone(), recv, k);\n    let ph2 = PeerHandleW(ps);\n    Peer::new(ph1, ph2, None /* TODO */)\n}\n\npub fn connection_reuser<F: FnOnce() -> BoxedNewPeerFuture>(\n    s: &mut GlobalState,\n    inner_peer: F,\n    buffer_size: usize,\n    queue_len: usize,\n) -> BoxedNewPeerFuture {\n    let need_init = s.borrow().is_none();\n\n    let rc = s.clone();\n    if need_init {\n        info!(\"Initializing\");\n        Box::new(inner_peer().and_then(move |inner| {\n            {\n                let mut b = rc.borrow_mut();\n                let x: &mut Option<Broadcaster> = b.deref_mut();\n                *x = Some(Broadcaster {\n                    inner_peer: inner,\n                    clients: Clients::new(),\n                });\n                spawn_hack(InnerPeerReader(rc.clone(), vec![0; buffer_size]));\n            }\n\n            let ps: HBroadCaster = rc.clone();\n            ok(makeclient(ps, queue_len))\n        })) as BoxedNewPeerFuture\n    } else {\n        info!(\"Reusing\");\n        let ps: HBroadCaster = rc.clone();\n        Box::new(ok(makeclient(ps, queue_len))) as BoxedNewPeerFuture\n    }\n}\n"
  },
  {
    "path": "src/crypto_peer.rs",
    "content": "use argon2::Argon2;\nuse futures::Async;\nuse futures::future::ok;\n\nuse std::rc::Rc;\n\nuse super::{BoxedNewPeerFuture, Peer};\nuse super::{ConstructParams, PeerConstructor, Specifier};\n\nuse std::io::{Read, Write};\nuse tokio_io::{AsyncRead, AsyncWrite};\n\nuse std::io::Error as IoError;\n\nuse chacha20poly1305::ChaCha20Poly1305;\nuse chacha20poly1305::Nonce;\nuse chacha20poly1305::aead::NewAead;\nuse chacha20poly1305::aead::Aead;\nuse rand::RngCore;\n\n#[derive(Debug)]\npub struct Crypto<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for Crypto<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(cp.clone());\n        let mut key = [0u8; 32];\n        if let Some(k) = cp.program_options.crypto_key {\n            key = k;\n        } else {\n            log::error!(\"You are using `crypto:` without `--crypto-key`. This uses a hard coded key and is insecure.\")\n        }\n        inner.map(move |p, _| crypto_peer(p, key, cp.program_options.crypto_reverse))\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = CryptoClass,\n    target = Crypto,\n    prefixes = [\"crypto:\"],\n    arg_handling = subspec,\n    overlay = true,\n    MessageOriented,\n    MulticonnectnessDependsOnInnerType,\n    help = r#\"\n[A] Encrypts written messages and decrypts (and verifies) read messages with a static key, using ChaCha20-Poly1305 algorithm.\n\nDo not not use in stream mode - packet boundaries are significant.\n\nNote that attacker may duplicate, drop or reorder messages, including between different Websocat sessions with the same key.\n\nEach encrypted message is 12 bytes bigger than original message.\n\nAssociated --crypto-key option accepts the following prefixes:\n\n- `file:` prefix means that Websocat should read 32-byte file and use it as a key.\n- `base64:` prefix means the rest of the value is base64-encoded 32-byte buffer\n- `pwd:` means Websocat should use argon2 derivation from the specified password as a key\n\nUse `--crypto-reverse` option to swap encryption and decryption.\n\nNote that `crypto:` specifier is absent in usual Websocat builds.\nYou may need to build Websocat from source code with `--features=crypto_peer` for it to be available.\n\"#\n);\n\n#[derive(Clone, Copy)]\nenum Mode {\n    Encrypt,\n    Decrypt,\n}\n\npub fn crypto_peer(inner_peer: Peer, key: [u8; 32], reverse: bool) -> BoxedNewPeerFuture {\n    let (mode_r, mode_w) = if reverse {\n        (Mode::Encrypt, Mode::Decrypt)\n    } else {\n        (Mode::Decrypt, Mode::Encrypt)\n    };\n    let crypto = ChaCha20Poly1305::new(chacha20poly1305::Key::from_slice(&key));\n    let filtered_r = CryptoWrapperR(inner_peer.0, crypto.clone(), mode_r);\n    let filtered_w = CryptoWrapperW(inner_peer.1, crypto, mode_w);\n    let thepeer = Peer::new(filtered_r, filtered_w, inner_peer.2);\n    Box::new(ok(thepeer)) as BoxedNewPeerFuture\n}\nstruct CryptoWrapperR(Box<dyn AsyncRead>, ChaCha20Poly1305, Mode);\n\nimpl Read for CryptoWrapperR {\n    fn read(&mut self, b: &mut [u8]) -> Result<usize, IoError> {\n        let mut l = b.len();\n\n        assert!(l > 12);\n\n        if matches!(self.2, Mode::Encrypt) {\n            l -= 12;\n        }\n\n        let n = match self.0.read(&mut b[..l]) {\n            Ok(x) => x,\n            Err(e) => return Err(e),\n        };\n\n        if n == 0 { return Ok(0) }\n\n        let data = process_data(&b[..n], &self.1, self.2)?;\n\n        let m = data.len();\n        b[..m].copy_from_slice(&data[..m]);\n\n        Ok(m)\n    }\n}\nimpl AsyncRead for CryptoWrapperR {}\n\nstruct CryptoWrapperW(Box<dyn AsyncWrite>, ChaCha20Poly1305, Mode);\n\nimpl Write for CryptoWrapperW {\n    fn write(&mut self, b: &[u8]) -> Result<usize, IoError> {\n        let l = b.len();\n\n        let data = process_data(b, &self.1, self.2)?;\n\n        let n = match self.0.write(&data[..]) {\n            Ok(x) => x,\n            Err(e) => return Err(e),\n        };\n\n        if n != data.len() {\n            log::error!(\"Short write when using `crypto:` specifier\");\n        }\n\n        Ok(l)\n    }\n\n    fn flush(&mut self) -> std::io::Result<()> {\n        self.0.flush()\n    }\n}\nimpl AsyncWrite for CryptoWrapperW {\n    fn shutdown(&mut self) -> std::result::Result<Async<()>, std::io::Error> {\n        self.0.shutdown()\n    }\n}\n\nfn process_data(buf: &[u8], crypto: &ChaCha20Poly1305, mode: Mode) -> Result<Vec<u8>, IoError>  {\n    let l = buf.len();\n    match mode {\n        Mode::Encrypt => {\n            let mut nonce = [0u8; 12];\n            rand::thread_rng().fill_bytes(&mut nonce[..]);\n            let mut data: Vec<u8> = crypto\n                .encrypt(Nonce::from_slice(&nonce), &buf[..])\n                .unwrap();\n            data.extend_from_slice(&nonce[..]);\n            Ok(data)\n        }\n        Mode::Decrypt => {\n            if l < 12 {\n                log::error!(\"Insufficient packet length for `crypto:` specifier's decryption\");\n                return Err(std::io::ErrorKind::Other.into()); \n            }\n            let mut nonce = [0u8; 12];\n            nonce.copy_from_slice(&buf[l-12..l]);\n            match crypto.decrypt(Nonce::from_slice(&nonce), &buf[..(l-12)]) {\n                Ok(x) => Ok(x),\n                Err(_) => {\n                    log::error!(\"crypto: decryption failed\");\n                    return Err(std::io::ErrorKind::Other.into())\n                }\n            }\n        }\n    }\n}\n\npub fn interpret_opt(x: &str) -> crate::Result<[u8; 32]> {\n    let mut key = [0u8; 32];\n    if x.starts_with(\"base64:\") {\n        let mut buf = Vec::with_capacity(32);\n        base64::decode_config_buf(&x[7..], base64::STANDARD, &mut buf)?;\n        if buf.len() != 32 {\n            log::error!(\"Expected 32 bytes, got {} bytes\", buf.len());\n            return Err(\"Non 32-byte buffer specified\".into());\n        }\n        key.copy_from_slice(&buf[..]);\n\n    } else if x.starts_with(\"file:\") {\n        let buf = std::fs::read(&x[5..])?;\n        if buf.len() != 32 {\n            log::error!(\"Expected 32 bytes, got {} bytes\", buf.len());\n            return Err(\"Non 32-byte buffer specified\".into());\n        }\n        key.copy_from_slice(&buf[..])\n    } else if x.starts_with(\"pwd:\") {\n        let argon2 = Argon2::default();\n        const SALT : &'static [u8] = &[0x81, 0x65, 0x0c, 0xc7, 0x09, 0x76, 0xc1, 0x12, 0x6b, 0x5b, 0x5f, 0x04,\n        0x08, 0x61, 0xf6, 0x1b, 0xd6, 0xab, 0x88, 0xa2, 0xee, 0x67, 0x47, 0xc1,\n        0xbe, 0x12, 0xd7, 0xd7, 0x2d, 0xb8, 0x39, 0xcf];\n        argon2.hash_password_into(x[4..].as_bytes(),SALT,&mut key[..]).unwrap();\n    } else {\n        return Err(\"--crypto-key's value must start with `base64:`, `file:` or `pwd:`\".into());\n    }\n    Ok(key)\n}\n"
  },
  {
    "path": "src/file_peer.rs",
    "content": "use futures;\nuse futures::Async;\nuse std;\nuse std::io::Result as IoResult;\nuse std::io::{Read, Write};\nuse std::path::{Path, PathBuf};\nuse tokio_io::{AsyncRead, AsyncWrite};\n\nuse std::fs::{File, OpenOptions};\nuse std::rc::Rc;\n\nuse super::{BoxedNewPeerFuture, Peer, Result};\n\nuse super::{once, ConstructParams, PeerConstructor, Specifier};\n\n#[derive(Clone, Debug)]\npub struct ReadFile(pub PathBuf);\nimpl Specifier for ReadFile {\n    fn construct(&self, _: ConstructParams) -> PeerConstructor {\n        fn gp(p: &Path) -> Result<Peer> {\n            let f = File::open(p)?;\n            Ok(Peer::new(ReadFileWrapper(f), super::trivial_peer::DevNull, None))\n        }\n        once(Box::new(futures::future::result(gp(&self.0))) as BoxedNewPeerFuture)\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\nspecifier_class!(\n    name = ReadFileClass,\n    target = ReadFile,\n    prefixes = [\"readfile:\"],\n    arg_handling = into,\n    overlay = false,\n    StreamOriented,\n    SingleConnect,\n    help = r#\"\nSynchronously read a file. Argument is a file path.\n\nBlocking on operations with the file pauses the whole process\n\nExample: Serve the file once per connection, ignore all replies.\n\n    websocat ws-l:127.0.0.1:8000 readfile:hello.json\n\n\"#\n);\n\n#[derive(Clone, Debug)]\npub struct WriteFile(pub PathBuf);\nimpl Specifier for WriteFile {\n    fn construct(&self, _: ConstructParams) -> PeerConstructor {\n        fn gp(p: &Path) -> Result<Peer> {\n            let f = File::create(p)?;\n            Ok(Peer::new(super::trivial_peer::DevNull, WriteFileWrapper(f), None))\n        }\n        once(Box::new(futures::future::result(gp(&self.0))) as BoxedNewPeerFuture)\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\nspecifier_class!(\n    name = WriteFileClass,\n    target = WriteFile,\n    prefixes = [\"writefile:\"],\n    arg_handling = into,\n    overlay = false,\n    StreamOriented,\n    SingleConnect,\n    help = r#\"\n\nSynchronously truncate and write a file.\n\nBlocking on operations with the file pauses the whole process\n\nExample:\n\n    websocat ws-l:127.0.0.1:8000 writefile:data.txt\n\n\"#\n);\n\n#[derive(Clone, Debug)]\npub struct AppendFile(pub PathBuf);\nimpl Specifier for AppendFile {\n    fn construct(&self, _: ConstructParams) -> PeerConstructor {\n        fn gp(p: &Path) -> Result<Peer> {\n            let f = OpenOptions::new().create(true).append(true).open(p)?;\n            Ok(Peer::new(super::trivial_peer::DevNull, WriteFileWrapper(f), None))\n        }\n        once(Box::new(futures::future::result(gp(&self.0))) as BoxedNewPeerFuture)\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\nspecifier_class!(\n    name = AppendFileClass,\n    target = AppendFile,\n    prefixes = [\"appendfile:\"],\n    arg_handling = into,\n    overlay = false,\n    StreamOriented,\n    SingleConnect,\n    help = r#\"\n\nSynchronously append a file.\n\nBlocking on operations with the file pauses the whole process\n\nExample: Logging all incoming data from WebSocket clients to one file\n\n    websocat -u ws-l:127.0.0.1:8000 reuse:appendfile:log.txt\n\"#\n);\n\npub struct ReadFileWrapper(pub File);\n\nimpl AsyncRead for ReadFileWrapper {}\nimpl Read for ReadFileWrapper {\n    fn read(&mut self, buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {\n        self.0.read(buf)\n    }\n}\n\nstruct WriteFileWrapper(File);\n\nimpl AsyncWrite for WriteFileWrapper {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        Ok(Async::Ready(()))\n    }\n}\nimpl Write for WriteFileWrapper {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        self.0.write(buf)\n    }\n    fn flush(&mut self) -> IoResult<()> {\n        self.0.flush()\n    }\n}\n"
  },
  {
    "path": "src/foreachmsg_peer.rs",
    "content": "use futures::future::ok;\n\nuse std::rc::Rc;\n\nuse super::{BoxedNewPeerFuture, Peer};\nuse super::{ConstructParams, PeerConstructor, Specifier};\n\nuse std::cell::RefCell;\n\nuse std::io::{Error as IoError, Read, Write};\nuse tokio_io::{AsyncRead, AsyncWrite};\n\nuse super::{once, simple_err, wouldblock};\nuse futures::{Async, Future, Poll};\n\n#[derive(Debug)]\npub struct Foreachmsg(pub Rc<dyn Specifier>);\nimpl Specifier for Foreachmsg {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        once(foreachmsg_peer(self.0.clone(), cp))\n    }\n    specifier_boilerplate!(singleconnect noglobalstate has_subspec);\n    self_0_is_subspecifier!(...);\n}\nspecifier_class!(\n    name = ForeachmsgClass,\n    target = Foreachmsg,\n    prefixes = [\"foreachmsg:\"],\n    arg_handling = subspec,\n    overlay = true,\n    MessageBoundaryStatusDependsOnInnerType,\n    SingleConnect,\n    help = r#\"\nExecute something for each incoming message.\n\nSomewhat the reverse of the `autoreconnect:`.\n\nExample:\n\n    websocat -t -u ws://server/listen_for_updates foreachmsg:writefile:status.txt\n\nThis keeps only recent incoming message in file and discards earlier messages.\n\"#\n);\n\n#[derive(Default)]\nstruct State2 {\n    already_warned: bool,\n}\n\n#[derive(Clone)]\nenum Phase {\n    Idle,\n    WriteDebt(Vec<u8>),\n    Flushing,\n    Closing,\n    WaitingForReadToFinish,\n}\n\nstruct State {\n    s: Rc<dyn Specifier>,\n    p: Option<Peer>,\n    n: Option<BoxedNewPeerFuture>,\n    cp: ConstructParams,\n    aux: State2,\n    ph: Phase,\n    finished_reading: bool,\n    read_waiter_tx: Option<futures::sync::oneshot::Sender<()>>,\n    read_waiter_rx: Option<futures::sync::oneshot::Receiver<()>>,\n    wait_for_new_peer_tx: Option<futures::sync::oneshot::Sender<()>>,\n    wait_for_new_peer_rx: Option<futures::sync::oneshot::Receiver<()>>,\n    need_wait_for_reading: bool,\n}\n\n/// This implementation's poll is to be reused many times, both after returning item and error\nimpl State {\n    //type Item = &'mut Peer;\n    //type Error = Box<::std::error::Error>;\n\n    fn poll(&mut self) -> Poll<&mut Peer, Box<dyn (::std::error::Error)>> {\n        let pp = &mut self.p;\n        let nn = &mut self.n;\n\n        let aux = &mut self.aux;\n\n        loop {\n            let cp = self.cp.clone();\n            if let Some(ref mut p) = *pp {\n                return Ok(Async::Ready(p));\n            }\n\n            // Peer is not present: trying to create a new one\n\n            if let Some(mut bnpf) = nn.take() {\n                match bnpf.poll() {\n                    Ok(Async::Ready(p)) => {\n                        *pp = Some(p);\n                        if let Some(tx) = self.wait_for_new_peer_tx.take() {\n                            let _ = tx.send(());\n                        }\n                        continue;\n                    }\n                    Ok(Async::NotReady) => {\n                        *nn = Some(bnpf);\n                        return Ok(Async::NotReady);\n                    }\n                    Err(_x) => {\n                        // Stop on error:\n                        //return Err(_x);\n\n                        // Just reconnect again on error\n\n                        if !aux.already_warned {\n                            aux.already_warned = true;\n                            error!(\"Reconnecting failed. Trying again in tight endless loop.\");\n                        }\n                    }\n                }\n            }\n            let l2r = cp.left_to_right.clone();\n            let pc: PeerConstructor = self.s.construct(cp);\n            *nn = Some(pc.get_only_first_conn(l2r));\n            self.finished_reading = false;\n            self.ph = Phase::Idle;\n            self.read_waiter_tx = None;\n            self.read_waiter_rx = None;\n        }\n    }\n}\n\n#[derive(Clone)]\nstruct PeerHandle(Rc<RefCell<State>>);\n\nmacro_rules! getpeer {\n    ($state:ident -> $p:ident) => {\n        let $p: &mut Peer = match $state.poll() {\n            Ok(Async::Ready(p)) => p,\n            Ok(Async::NotReady) => return wouldblock(),\n            Err(e) => {\n                return Err(simple_err(format!(\"{}\", e)));\n            }\n        };\n    };\n}\n\nimpl State {\n    fn reconnect(&mut self) {\n        info!(\"Reconnect\");\n        self.p = None;\n        self.ph = Phase::Idle;\n        self.finished_reading = false;\n        self.read_waiter_tx = None;\n        self.read_waiter_rx = None;\n    }\n}\n\nimpl Read for PeerHandle {\n    fn read(&mut self, b: &mut [u8]) -> Result<usize, IoError> {\n        let mut state = self.0.borrow_mut();\n        loop {\n            if let Some(w) = state.wait_for_new_peer_rx.as_mut() {\n                match w.poll() {\n                    Ok(Async::NotReady) => return wouldblock(),\n                    _ => {\n                        state.wait_for_new_peer_rx = None;\n                    }\n                }\n            }\n            let p : &mut Peer = match state.poll() {\n                Ok(Async::Ready(p)) => p,\n                Ok(Async::NotReady) => return wouldblock(),\n                Err(e) => {\n                    return Err(simple_err(format!(\"{}\", e)));\n                }\n            };\n            #[allow(unused_assignments)]\n            let mut finished_but_loop_around = false;\n            match p.0.read(b) {\n                Ok(0) => { \n                    state.finished_reading = true;\n                    if state.need_wait_for_reading {\n                        finished_but_loop_around = true;\n                    } else {\n                        return Ok(0);\n                    }\n                }\n                Err(e) => {\n                    if e.kind() == ::std::io::ErrorKind::WouldBlock {\n                        return Err(e);\n                    }\n                    state.finished_reading = true;\n                    warn!(\"{}\", e);\n\n                    if state.need_wait_for_reading {\n                        // Get a new peer to read from\n                        finished_but_loop_around = true;\n                    } else {\n                        return Err(e);\n                    }\n                }\n                Ok(x) => {\n                    return Ok(x);\n                }\n            }\n            if finished_but_loop_around {\n                state.finished_reading = true;\n                let (tx,rx) = futures::sync::oneshot::channel();\n                state.wait_for_new_peer_tx = Some(tx);\n                state.wait_for_new_peer_rx = Some(rx);\n                if let Some(rw) = state.read_waiter_tx.take() {\n                    let _ = rw.send(());\n                }\n            }\n        }\n    }\n}\nimpl AsyncRead for PeerHandle {}\n\nimpl Write for PeerHandle {\n    fn write(&mut self, b: &[u8]) -> Result<usize, IoError> {\n        let mut state = self.0.borrow_mut();\n        \n        let mut do_reconnect = false;\n        let mut finished = false;\n        loop {\n            if do_reconnect {\n                state.reconnect();\n                do_reconnect = false;\n            } else if finished {\n                state.p = None;\n                state.ph = Phase::Idle;\n                return Ok(b.len());\n            } else {\n                let mut ph = state.ph.clone();\n                {\n                    getpeer!(state -> p);\n\n                    match ph {\n                        Phase::Idle => {\n                            match p.1.write(b) {\n                                Ok(0) => { \n                                    info!(\"End-of-file write?\");\n                                    return Ok(0);\n                                }\n                                Err(e) => {\n                                    if e.kind() == ::std::io::ErrorKind::WouldBlock {\n                                        return Err(e);\n                                    }\n                                    warn!(\"{}\", e);\n                                    return Err(e);\n                                }\n                                Ok(x) if x == b.len() => {\n                                    debug!(\"Full write\");\n                                    // A successful write. Flushing and closing the peer.\n                                    ph = Phase::Flushing;\n                                },\n                                Ok(x) => {\n                                    debug!(\"Partial write of {} bytes\", x);\n                                    // A partial write. Creating write debt.\n                                    let debt = b[x..b.len()].to_vec();\n                                    ph = Phase::WriteDebt(debt);\n                                }\n                            }\n                        },\n                        Phase::WriteDebt(d) => {\n                            match p.1.write(&d[..]) {\n                                Ok(0) => { \n                                    info!(\"End-of-file write v2?\");\n                                    return Ok(0);\n                                }\n                                Err(e) => {\n                                    if e.kind() == ::std::io::ErrorKind::WouldBlock {\n                                        return Err(e);\n                                    }\n                                    warn!(\"{}\", e);\n                                    return Err(e);\n                                }\n                                Ok(x) if x == d.len() => {\n                                    debug!(\"Closing the debt\");\n                                    // A successful write. Flushing and closing the peer.\n                                    ph = Phase::Flushing;\n                                },\n                                Ok(x) => {\n                                    debug!(\"Partial write of {} debt bytes\", x);\n                                    // A partial write. Retaining the write debt.\n                                    let debt = d[x..d.len()].to_vec();\n                                    ph = Phase::WriteDebt(debt);\n                                }\n                            }\n                        },\n                        Phase::Flushing => {\n                            match p.1.flush() {\n                                Err(e) => {\n                                    if e.kind() == ::std::io::ErrorKind::WouldBlock {\n                                        return Err(e);\n                                    }\n                                    warn!(\"{}\", e);\n                                    return Err(e);\n                                }\n                                Ok(()) => {\n                                    debug!(\"Flushed\");\n                                    ph = Phase::Closing;\n                                }\n                            }\n                        },\n                        Phase::Closing => {\n                            match p.1.shutdown() {\n                                Err(e) => {\n                                    if e.kind() == ::std::io::ErrorKind::WouldBlock {\n                                        return Err(e);\n                                    }\n                                    warn!(\"{}\", e);\n                                    return Err(e);\n                                },\n                                Ok(Async::NotReady) => {\n                                    return wouldblock();\n                                },\n                                Ok(Async::Ready(())) => {\n                                    if state.need_wait_for_reading {\n                                        if state.finished_reading {\n                                            debug!(\"Closed and reading is also done\");\n                                            finished=true;\n                                        } else {\n                                            debug!(\"Closed, but need to wait for other direction to finish\");\n                                            ph = Phase::WaitingForReadToFinish;\n                                            let (tx,rx) = futures::sync::oneshot::channel();\n                                            state.read_waiter_tx = Some(tx);\n                                            state.read_waiter_rx = Some(rx);\n                                        }\n                                    } else {\n                                        debug!(\"Closed\");\n                                        finished=true;\n                                    }\n                                }\n                            }\n                        },\n                        Phase::WaitingForReadToFinish => {\n                            match state.read_waiter_rx.as_mut().unwrap().poll() {\n                                Ok(Async::NotReady) => {\n                                    return wouldblock();\n                                }\n                                _ => {\n                                    debug!(\"Waited for read to finish\");\n                                    finished=true;\n                                }\n                            }\n                        }\n                    }\n                }\n                state.ph = ph;\n            }\n        }\n    }\n    fn flush(&mut self) -> Result<(), IoError> {\n        // No-op here: we flush and close after each write\n        Ok(())\n    }\n}\nimpl AsyncWrite for PeerHandle {\n    fn shutdown(&mut self) -> futures::Poll<(), IoError> {\n        // No-op here: we flush and close after each write\n        Ok(Async::Ready(()))\n    }\n}\n\npub fn foreachmsg_peer(s: Rc<dyn Specifier>, cp: ConstructParams) -> BoxedNewPeerFuture {\n    let need_wait_for_reading = cp.program_options.foreachmsg_wait_reads;\n    let s = Rc::new(RefCell::new(State {\n        cp,\n        s,\n        p: None,\n        n: None,\n        aux: Default::default(),\n        ph: Phase::Idle,\n        finished_reading: false,\n        read_waiter_tx: None,\n        read_waiter_rx: None,\n        wait_for_new_peer_rx: None,\n        wait_for_new_peer_tx: None,\n        need_wait_for_reading,\n    }));\n    let ph1 = PeerHandle(s.clone());\n    let ph2 = PeerHandle(s);\n    let peer = Peer::new(ph1, ph2, None /* we handle hups ourselves */);\n    Box::new(ok(peer)) as BoxedNewPeerFuture\n}\n"
  },
  {
    "path": "src/help.rs",
    "content": "use super::{Opt, SpecifierClass, StructOpt};\n\nfn spechelp(sc: &dyn SpecifierClass, overlays: bool, advanced: bool) {\n    if !advanced && sc.help().contains(\"[A]\") {\n        return;\n    }\n    if overlays ^ sc.is_overlay() {\n        return;\n    }\n\n    let first_prefix = sc.get_prefixes()[0];\n\n    let mut first_help_line = None;\n    for l in sc.help().lines() {\n        if !l.trim().is_empty() {\n            first_help_line = Some(l);\n            break;\n        }\n    }\n    if let Some(fhl) = first_help_line {\n        println!(\"\\t{:16}\\t{}\", first_prefix, fhl);\n    }\n}\n\n// https://github.com/rust-lang/rust/issues/51942\n#[allow(clippy::nonminimal_bool)]\npub fn shorthelp() {\n    //use std::io::Write;\n    use std::io::{BufRead, BufReader};\n    let mut b = vec![];\n    if Opt::clap().write_help(&mut b).is_err() {\n        eprintln!(\"Error displaying the help message\");\n    }\n    let mut lines_to_display = vec![];\n    let mut do_display = true;\n    BufReader::new(&b[..]).lines().for_each(|l| {\n        if let Ok(l) = l {\n            {\n                let lt = l.trim();\n                let new_paragraph_start = false || lt.starts_with('-') || l.is_empty();\n                if lt.starts_with(\"--help\") {\n                    // Allowed to output [A] regardless\n                    do_display = true;\n                } else if l.contains(\"[A]\") {\n                    do_display = false;\n                    if l.trim().starts_with(\"[A]\") {\n                        // Also retroactively retract the previous line\n                        let nl = lines_to_display.len() - 1;\n                        lines_to_display.truncate(nl);\n                    }\n                } else if new_paragraph_start {\n                    do_display = true;\n                };\n            }\n            let mut additional_line = None;\n\n            if l == \"FLAGS:\" {\n                additional_line = Some(\"    (some flags are hidden, see --help=long)\".to_string());\n            };\n            if l == \"OPTIONS:\" {\n                additional_line =\n                    Some(\"    (some options are hidden, see --help=long)\".to_string());\n            };\n\n            if do_display {\n                lines_to_display.push(l);\n                if let Some(x) = additional_line {\n                    lines_to_display.push(x);\n                }\n            };\n        }\n    });\n    for l in lines_to_display {\n        println!(\"{}\", l);\n    }\n\n    println!(\"\\nPartial list of address types:\");\n\n    macro_rules! my {\n        ($x:expr) => {\n            spechelp(&$x, false, false);\n        };\n    }\n    list_of_all_specifier_classes!(my);\n\n    println!(\"Partial list of overlays:\");\n\n    macro_rules! my {\n        ($x:expr) => {\n            spechelp(&$x, true, false);\n        };\n    }\n    list_of_all_specifier_classes!(my);\n\n    println!(\"See more address types with the --help=long option.\");\n    println!(\"See short examples and --dump-spec names for most address types and overlays with --help=doc option\");\n}\n\npub fn longhelp() {\n    //let q = Opt::from_iter(vec![\"-\"]);\n    let mut a = Opt::clap();\n\n    let _ = a.print_help();\n\n    println!(\"\\n\\nFull list of address types:\");\n\n    macro_rules! my {\n        ($x:expr) => {\n            spechelp(&$x, false, true);\n        };\n    }\n    list_of_all_specifier_classes!(my);\n\n    println!(\"Full list of overlays:\");\n\n    macro_rules! my {\n        ($x:expr) => {\n            spechelp(&$x, true, true);\n        };\n    }\n    list_of_all_specifier_classes!(my);\n}\n\nfn specdoc(sc: &dyn SpecifierClass, overlays: bool) {\n    if sc.is_overlay() ^ overlays {\n        return;\n    }\n\n    let first_prefix = sc.get_prefixes()[0];\n    let spec_name = sc.get_name().replace(\"Class\", \"\");\n\n    let other_prefixes = sc.get_prefixes()[1..]\n        .iter()\n        .map(|x| format!(\"`{}`\", x))\n        .collect::<Vec<_>>()\n        .join(\", \");\n\n    println!(r#\"### `{}`\"#, first_prefix);\n    println!();\n    if !other_prefixes.is_empty() {\n        println!(\"Aliases: {}  \", other_prefixes);\n    }\n    println!(\"Internal name for --dump-spec: {}\", spec_name);\n    println!();\n\n    let help = \n        sc\n        .help()\n        //.lines()\n        //.map(|x|format!(\"    {}\",x))\n        //.collect::<Vec<_>>()\n        //.join(\"\\n\")\n        ;\n    println!(\"{}\\n\", help);\n}\n\npub fn dochelp() {\n    println!(r#\"\n# Websocat Reference (in progress)\n\nWebsocat has many command-line options and special format for positional arguments.\n\nThere are three main modes of websocat invocation:\n\n* Simple client mode: `websocat wss://your.server/url`\n* Simple server mode: `websocat -s 127.0.0.1:8080`\n* Advanced [socat][1]-like mode: `websocat -t ws-l:127.0.0.1:8080 mirror:`\n\nUltimately in any of those modes websocat creates two connections and exchanges data between them.\nIf one of the connections is bytestream-oriented (for example the terminal stdin/stdout or a TCP connection), but the other is message-oriented (for example, a WebSocket or UDP) then websocat operates in lines: each line correspond to a message. Details of this are configurable by various options.\n\n`ws-l:` or `mirror:` above are examples of address types. With the exception of special cases like WebSocket URL `ws://1.2.3.4/` or stdio `-`, websocat's positional argument is defined by this rule:\n\n```\n<specifier> ::= ( <overlay> \":\" )* <addrtype> \":\" [address]\n```\n\nSome address types may be \"aliases\" to other address types or combinations of overlays and address types.\n\n[1]:http://www.dest-unreach.org/socat/doc/socat.html\n\n# `--help=long`\n\n\"Advanced\" options and flags are denoted by `[A]` marker.\n\n\n```\n\"#);\n\n    let mut a = Opt::clap();\n\n    let _ = a.print_help();\n\n    println!(\n        r#\"\n\n```\n\n# Full list of address types\n\n\"Advanced\" address types are denoted by `[A]` marker.\n\n\"#\n    );\n\n    macro_rules! my {\n        ($x:expr) => {\n            specdoc(&$x, false);\n        };\n    }\n    list_of_all_specifier_classes!(my);\n\n    println!(\n        r#\"\n\n# Full list of overlays\n\n\"Advanced\" overlays denoted by `[A]` marker.\n\n\"#\n    );\n\n    macro_rules! my {\n        ($x:expr) => {\n            specdoc(&$x, true);\n        };\n    }\n    list_of_all_specifier_classes!(my);\n\n    println!(\n        r#\"\n  \n### Address types or specifiers to be implemented later:\n\n`sctp:`, `speedlimit:`, `quic:`\n\n### Final example\n\nFinal example just for fun: wacky mode\n\n    websocat ws-c:ws-l:ws-c:- tcp:127.0.0.1:5678\n    \nConnect to a websocket using stdin/stdout as a transport,\nthen accept a websocket connection over the previous websocket used as a transport,\nthen connect to a websocket using previous step as a transport,\nthen forward resulting connection to the TCP port.\n\n(Exercise to the reader: manage to make it actually connect to 5678).\n\"#\n    );\n}\n"
  },
  {
    "path": "src/http_peer.rs",
    "content": "\n#![allow(unused)]\n#![allow(clippy::needless_pass_by_value,clippy::cast_lossless,clippy::identity_op)]\nuse futures::future::{err, ok, Future};\n\nuse std::rc::Rc;\n\nuse super::{box_up_err, peer_strerr, BoxedNewPeerFuture, Peer};\nuse super::{ConstructParams, L2rUser, PeerConstructor, Specifier};\nuse tokio_io::io::{read_exact, write_all};\nuse tokio_io::{AsyncRead,AsyncWrite};\n\nuse std::io::Write;\nuse std::net::{IpAddr, Ipv4Addr};\n\nuse std::ffi::OsString;\n\nextern crate http_bytes;\nuse http_bytes::http;\n\nuse http_bytes::{Request,Response};\nuse crate::http::Uri;\nuse crate::http::Method;\nuse crate::util::peer_err2;\n\n#[derive(Debug)]\npub struct HttpRequest<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for HttpRequest<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(cp.clone());\n        inner.map(move |p, l2r| {\n            let mut b = crate::http::request::Builder::default();\n            if let Some(uri) = cp.program_options.request_uri.as_ref() {\n                b.uri(uri);\n            }\n            if let Some(method) = cp.program_options.request_method.as_ref() {\n                b.method(method);\n            }\n            for (hn, hv) in &cp.program_options.request_headers {\n                b.header(hn, hv);\n            }\n            let request = b.body(()).unwrap();\n            http_request_peer(&request, p, l2r)\n        })\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = HttpRequestClass,\n    target = HttpRequest,\n    prefixes = [\"http-request:\"],\n    arg_handling = subspec,\n    overlay = true,\n    StreamOriented,\n    MulticonnectnessDependsOnInnerType,\n    help = r#\"\n[A] Issue HTTP request, receive a 1xx or 2xx reply, then pass\nthe torch to outer peer, if any - lowlevel version.\n\nContent you write becomes body, content you read is body that server has sent.\n\nURI is specified using a separate command-line parameter\n\nExample:\n\n    websocat -Ub - http-request:tcp:example.com:80 --request-uri=http://example.com/ --request-header 'Connection: close'\n\"#\n);\n\n/// Inner peer is a TCP peer configured to this host\n#[derive(Debug)]\npub struct Http<T: Specifier>(pub T, pub Uri);\nimpl<T: Specifier> Specifier for Http<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(cp.clone());\n        let uri = self.1.clone();\n        inner.map(move |p, l2r| {\n            let mut b = crate::http::request::Builder::default();\n            b.uri(uri.clone());\n            if let Some(method) = cp.program_options.request_method.as_ref() {\n                b.method(method);\n            }\n            for (hn, hv) in &cp.program_options.request_headers {\n                b.header(hn, hv);\n            }\n            let request = b.body(()).unwrap();\n            http_request_peer(&request, p, l2r)\n        })\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = HttpClass,\n    target = Http,\n    prefixes = [\"http:\"],\n    arg_handling = {\n        fn construct(self: &HttpClass, arg: &str) -> super::Result<Rc<dyn Specifier>> {\n            let uri : Uri = format!(\"http:{}\", arg).parse()?;\n            let tcp_peer;\n            {\n                let auth = uri.authority_part().unwrap();\n                let host = auth.host();\n                let port = auth.port_part();\n                let addr = if let Some(p) = port {\n                    format!(\"tcp:{}:{}\", host, p)\n                } else {\n                    format!(\"tcp:{}:80\", host)\n                };\n                tcp_peer = crate::spec(addr.as_ref())?;\n            }\n            Ok(Rc::new(Http(tcp_peer, uri)))\n        }\n        fn construct_overlay(\n            self: &HttpClass,\n            _inner: Rc<dyn Specifier>,\n        ) -> super::Result<Rc<dyn Specifier>> {\n            panic!(\"Error: construct_overlay called on non-overlay specifier class\")\n        }\n    },\n    overlay = false,\n    StreamOriented,\n    SingleConnect,\n    help = r#\"\n[A] Issue HTTP request, receive a 1xx or 2xx reply, then pass\nthe torch to outer peer, if any - highlevel version.\n\nContent you write becomes body, content you read is body that server has sent.\n\nURI is specified inline.\n\nExample:\n\n    websocat  -b - http://example.com < /dev/null\n\"#\n);\n\n\n\n#[derive(Copy,Clone,PartialEq,Debug)]\nenum HttpHeaderEndDetectionState {\n    Neutral,\n    FirstCr,\n    FirstLf,\n    SecondCr,\n    FoundHeaderEnd,\n}\n\nstruct WaitForHttpHead<R : AsyncRead>\n{\n    buf: Option<Vec<u8>>,\n    offset : usize,\n    state: HttpHeaderEndDetectionState,\n    io : Option<R>,\n}\n\nstruct WaitForHttpHeadResult {\n    buf: Vec<u8>,\n    // Before the offset is header, after the offset is debt\n    offset: usize,\n}\n\nimpl<R:AsyncRead> WaitForHttpHead<R> {\n    pub fn new(r:R) -> WaitForHttpHead<R> {\n        WaitForHttpHead {\n            buf: Some(Vec::with_capacity(512)),\n            offset: 0,\n            state: HttpHeaderEndDetectionState::Neutral,\n            io: Some(r),\n        }\n    }\n}\n\nimpl<R:AsyncRead> Future for WaitForHttpHead<R> {\n    type Item = (WaitForHttpHeadResult, R);\n    type Error = Box<dyn std::error::Error>;\n\n    fn poll(&mut self) -> ::futures::Poll<Self::Item, Self::Error> {\n        loop {\n            if self.buf.is_none() || self.io.is_none() {\n                Err(\"WaitForHttpHeader future polled after completion\")?;\n            }\n            let ret;\n            {\n                let buf = self.buf.as_mut().unwrap();\n                let io = self.io.as_mut().unwrap();\n                if buf.len() < self.offset + 1024 {\n                    buf.resize(self.offset + 1024, 0u8);\n                }\n                ret = try_nb!(io.read(&mut buf[self.offset..]));\n\n                if ret == 0 {\n                    Err(\"Trimmed HTTP head\")?;\n                }\n            }\n\n            // parse\n            for i in self.offset..(self.offset+ret) {\n                let x = self.buf.as_ref().unwrap()[i];\n                use self::HttpHeaderEndDetectionState::*;\n                //eprint!(\"{:?} -> \", self.state);\n                self.state = match (self.state, x) {\n                    (Neutral, b'\\r') => FirstCr,\n                    (FirstCr, b'\\n') => FirstLf,\n                    (FirstLf, b'\\r') => SecondCr,\n                    (SecondCr, b'\\n') => FoundHeaderEnd,\n                    _ => Neutral,\n                };\n                //eprintln!(\"{:?}\", self.state);\n                if self.state == FoundHeaderEnd {\n                    let io = self.io.take().unwrap();\n                    let mut buf = self.buf.take().unwrap();\n                    buf.resize(self.offset + ret, 0u8);\n                    return Ok(::futures::Async::Ready((\n                        WaitForHttpHeadResult { buf, offset: i+1},\n                        io,\n                    )));\n                }\n            }\n\n            self.offset += ret;\n\n            if self.offset > 60_000 {\n                Err(\"HTTP head too long\")?;\n            }\n        }\n    }\n}\n\npub fn http_request_peer(\n    request: &Request,\n    inner_peer: Peer,\n    _l2r: L2rUser,\n) -> BoxedNewPeerFuture {\n    let request = ::http_bytes::request_header_to_vec(request);\n\n    let (r, w, hup) = (inner_peer.0, inner_peer.1, inner_peer.2);\n\n    info!(\"Issuing HTTP request\");\n    let f = ::tokio_io::io::write_all(w, request)\n        .map_err(box_up_err)\n        .and_then(move |(w, request)| {\n            WaitForHttpHead::new(r).and_then(|(res, r)|{\n                debug!(\"Got HTTP response head\");\n                let ret = (move||{\n                    {\n                        let headbuf = &res.buf[0..res.offset];\n                        trace!(\"{:?}\",headbuf);\n                        let p = http_bytes::parse_response_header_easy(headbuf)?;\n                        if p.is_none() {\n                            Err(\"Something wrong with response HTTP head\")?;\n                        }\n                        let p = p.unwrap();\n                        if !p.1.is_empty() {\n                            Err(\"Something wrong with parsing HTTP\")?;\n                        }\n                        let response = p.0;\n                        let status = response.status();\n                        info!(\"HTTP response status: {}\", status);\n                        debug!(\"{:#?}\", response);\n                        if status.is_success() || status.is_informational() {\n                            // OK\n                        } else {\n                            Err(\"HTTP response indicates failure\")?;\n                        }\n                    }\n                    let remaining = res.buf.len() - res.offset;\n                    if remaining == 0 {\n                        Ok(Peer::new(r,w,hup))\n                    } else {\n                        debug!(\"{} bytes of debt to be read\", remaining);\n                        let r = super::trivial_peer::PrependRead {\n                            inner: r,\n                            header: res.buf,\n                            remaining,\n                        };\n                        Ok(Peer::new(r,w,hup))\n                    }\n                })();\n                ::futures::future::result(ret)\n            })\n        })\n    ;\n\n    Box::new(f) as BoxedNewPeerFuture\n}\n\n\n#[derive(Debug)]\npub struct HttpPostSse<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for HttpPostSse<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(cp.clone());\n        inner.map(move |p, l2r| {\n            http_response_post_sse_peer(p, l2r)\n        })\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = HttpPostSseClass,\n    target = HttpPostSse,\n    prefixes = [\"http-post-sse:\"],\n    arg_handling = subspec,\n    overlay = true,\n    MessageOriented,\n    MulticonnectnessDependsOnInnerType,\n    help = r#\"\n[A] Accept HTTP/1 request. Then, if it is GET,\nunidirectionally return incoming messages as server-sent events (SSE).\n\nIf it is POST then, also unidirectionally, write body upstream.\n\nExample - turn SSE+POST pair into a client WebSocket connection:\n\n    websocat -E -t http-post-sse:tcp-l:127.0.0.1:8080 reuse:ws://127.0.0.1:80/websock\n\n`curl -dQQQ http://127.0.0.1:8080/` would send into it and \n`curl -N http://127.0.0.1:8080/` would recv from it.\n\"#\n);\n\n#[derive(Debug)]\nenum ModeOfOperation {\n    PostBody,\n    GetSse,\n}\n\npub fn http_response_post_sse_peer(\n    inner_peer: Peer,\n    _l2r: L2rUser,\n) -> BoxedNewPeerFuture {\n    let (r, w, hup) = (inner_peer.0, inner_peer.1, inner_peer.2);\n\n    warn!(\"Note: http-post-see mode is not tested and may integrate poorly into current Websocat architecture. Expect it to be of lower quality than other Websocat modes.\");\n    info!(\"Incoming prospective HTTP request\");\n    let f = WaitForHttpHead::new(r).and_then(|(res, r)|{\n        debug!(\"Got HTTP request head\");\n        let ret : Result<_,Box<dyn std::error::Error+'static>> = (move||{\n            let mode;\n            let request;\n            {\n                let headbuf = &res.buf[0..res.offset];\n                trace!(\"{:?}\",headbuf);\n                let p = http_bytes::parse_request_header_easy(headbuf)?;\n                if p.is_none() {\n                    Err(\"Something wrong with request HTTP head\")?;\n                }\n                let p = p.unwrap();\n                if !p.1.is_empty() {\n                    Err(\"Something wrong with parsing HTTP request\")?;\n                }\n                request = p.0;\n                let method = request.method();\n                mode = match *method {\n                    http::method::Method::GET => {\n                        info!(\"GET request. Serving a SSE stream\");\n                        ModeOfOperation::GetSse\n                    },\n                    http::method::Method::POST => {\n                        info!(\"POST request. Writing body once\");\n                        ModeOfOperation::PostBody\n                    },\n                    _ => { \n                        error!(\"HTTP request method is {}, but we expect only GET or POST in this mode\", method);\n                        Err(\"Wrong HTTP request method\")?\n                    }\n                };\n                debug!(\"{:#?}\", request);\n            }\n\n            // Now it's time to generate a successful reply.\n            // (maybe actually we should read the full request first, but\n            // let's do some thing at first and correct thing after that).\n\n            use crate::http::header::{HOST,SERVER,CACHE_CONTROL, CONTENT_TYPE};\n\n            let mut reply = crate::http::response::Builder::default();\n            let status = match mode {\n                ModeOfOperation::GetSse => 200,\n                ModeOfOperation::PostBody => 204,\n            };\n            reply.status(status);\n            if let Some(x) = request.headers().get(HOST) {\n                reply.header(HOST, x);\n            }\n            reply.header(\"Server\", \"websocat\");\n            match mode {\n                ModeOfOperation::GetSse => {\n                    reply.header(CACHE_CONTROL, \"no-cache\");\n                    reply.header(CONTENT_TYPE, \"text/event-stream\");\n                }\n                ModeOfOperation::PostBody => (),\n            }\n            let reply = reply.body(()).unwrap();\n            let reply = ::http_bytes::response_header_to_vec(&reply);\n\n            Ok(::tokio_io::io::write_all(w, reply)\n                .map_err(box_up_err)\n                .and_then(move |(w, request)| {\n\n                    debug!(\"Response writing finished\");\n\n                    // Infinitely hang reading or writing\n                    // If use DevNull instead, connection may get closed prematurely\n                    let dummy = crate::trivial_peer::CloggedPeer;\n\n                    match mode {\n                        ModeOfOperation::GetSse => {\n                            // Will it call shutdown(2) on the socket?\n                            drop(r);\n                            \n                            let w = SseStream::new(w);\n                            \n                            Ok(Peer::new(dummy, w, hup))\n                        },\n                        ModeOfOperation::PostBody => {\n                            debug!(\"Start streaming POST body upstream, ignoring reverse data\");\n                            \n                            // Will it call shutdown(2) on the socket?\n                            drop(w);\n\n                            let remaining = res.buf.len() - res.offset;\n                            if remaining == 0 {\n                                Ok(Peer::new(r,dummy,hup))\n                            } else {\n                                debug!(\"{} bytes of debt to be read\", remaining);\n                                let r = super::trivial_peer::PrependRead {\n                                    inner: r,\n                                    header: res.buf,\n                                    remaining,\n                                };\n                                Ok(Peer::new(r,dummy,hup))\n                            }\n                        },\n                    }\n            }))\n        })();\n        match ret {\n            Err(x) => peer_err2(x),\n            Ok(x) => Box::new(x),\n        }\n    });\n    Box::new(f) as BoxedNewPeerFuture\n}\n\n#[derive(Clone,Copy,Debug)]\nenum SseState {\n    BeforeLine(usize),\n    InsideLine,\n    AfterLine,\n    Trailer,\n}\n\nstruct SseStream<W : Write>\n{\n    io : W,\n    state: SseState,\n    consumed_actual_buffer : usize,\n}\n\nimpl<W : Write> SseStream<W> {\n    pub fn new(w: W) -> Self {\n        SseStream {\n            io: w,\n            state: SseState::BeforeLine(0),\n            consumed_actual_buffer: 0,\n        }\n    }\n}\n\nimpl<W:AsyncWrite> AsyncWrite for SseStream<W> {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        self.io.shutdown()\n    }\n}\n\nimpl<W:AsyncWrite> Write for SseStream<W> {\n    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {\n        // assumes buffer does not change on EAGAINs\n        loop {\n            let s = self.state;\n            let mut need_write = 0;\n            debug!(\"SSE state {:?}\", s);\n            let ret = match s {\n                SseState::BeforeLine(x) => {\n                    self.io.write(&b\"data: \"[x..6])\n                }\n                SseState::InsideLine => {\n                    let buf = &buf[self.consumed_actual_buffer..];\n                    let max = buf.iter().position(|&x|x==b'\\n').unwrap_or(buf.len());\n                    let buf = &buf[0..max];\n                    need_write = buf.len();\n                    self.io.write(buf)\n                }\n                SseState::AfterLine => {\n                    self.io.write(b\"\\n\")\n                }\n                SseState::Trailer => {\n                    self.io.write(b\"\\n\")\n                }\n            };\n            let ll = ret?;\n            self.state = match s {\n                SseState::BeforeLine(x) => {\n                    let nl = ll + x;\n                    if nl == 6 {\n                        SseState::InsideLine\n                    } else {\n                        SseState::BeforeLine(nl)\n                    }\n                }\n                SseState::InsideLine => {\n                    self.consumed_actual_buffer += ll;\n                    if ll < need_write {\n                        SseState::InsideLine\n                    } else {\n                        SseState::AfterLine\n                    }\n                }\n                SseState::AfterLine => {\n                    if self.consumed_actual_buffer < buf.len() {\n                        if buf[self.consumed_actual_buffer] == b'\\n' {\n                            self.consumed_actual_buffer += 1;\n                        }\n                        SseState::BeforeLine(0)\n                    } else {\n                        SseState::Trailer\n                    }\n                }\n                SseState::Trailer => {\n                    let r = self.consumed_actual_buffer;\n                    self.consumed_actual_buffer = 0;\n                    self.state = SseState::BeforeLine(0);\n                    debug!(\"r={} buflen={}\", r, buf.len());\n                    return Ok(r)\n                }\n            };\n            debug!(\" new SSE state {:?}\", self.state);\n        }\n    }\n    fn flush(&mut self) -> std::io::Result<()> {\n        self.io.flush()\n    }\n}\n\n#[test]\nfn test_basic_sse_stream() {\n    let mut v = vec![];\n    {\n        let mut ss = SseStream::new(std::io::Cursor::new(&mut v));\n    }\n}\n"
  },
  {
    "path": "src/http_serve.rs",
    "content": "use self::hyper::http::h1::Incoming;\nuse self::hyper::method::Method;\nuse self::hyper::uri::RequestUri;\nuse self::hyper::uri::RequestUri::AbsolutePath;\nuse super::hyper;\n\nuse futures::future::Future;\nuse std::fs::File;\nuse std::rc::Rc;\n\nuse crate::options::StaticFile;\nuse crate::trivial_peer::get_literal_peer_now;\nuse crate::Peer;\n\nuse crate::my_copy::{copy, CopyOptions};\n\nconst BAD_REQUEST :&[u8] = b\"HTTP/1.1 400 Bad Request\\r\\nServer: websocat\\r\\nContent-Type: text/plain\\r\\nConnection: close\\r\\n\\r\\nOnly WebSocket connections are welcome here\\n\";\n\nconst NOT_FOUND: &[u8] = b\"HTTP/1.1 404 Not Found\\r\\nServer: websocat\\r\\nContent-Type: text/plain\\r\\nConnection: close\\r\\n\\r\\nURI does not match any -F option and is not a WebSocket connection.\\n\";\n\nconst NOT_FOUND2: &[u8] = b\"HTTP/1.1 500 Not Found\\r\\nServer: websocat\\r\\nContent-Type: text/plain\\r\\nConnection: close\\r\\n\\r\\nFailed to open the file on server side.\\n\";\n\nconst BAD_METHOD :&[u8] = b\"HTTP/1.1 400 Bad Request\\r\\nServer: websocat\\r\\nContent-Type: text/plain\\r\\nConnection: close\\r\\n\\r\\nHTTP method should be GET\\n\";\n\nconst BAD_URI_FORMAT :&[u8] = b\"HTTP/1.1 400 Bad Request\\r\\nServer: websocat\\r\\nContent-Type: text/plain\\r\\nConnection: close\\r\\n\\r\\nURI should be an absolute path\\n\";\n\npub fn get_static_file_reply(len: Option<u64>, ct: &str) -> Vec<u8> {\n    let mut q = Vec::with_capacity(256);\n    q.extend_from_slice(b\"HTTP/1.1 200 OK\\r\\nServer: websocat\\r\\nContent-Type: \");\n    q.extend_from_slice(ct.as_bytes());\n    q.extend_from_slice(b\"\\r\\n\");\n    if let Some(x) = len {\n        q.extend_from_slice(b\"Content-Length: \");\n        q.extend_from_slice(format!(\"{}\", x).as_bytes());\n        q.extend_from_slice(b\"\\r\\n\");\n    }\n    q.extend_from_slice(b\"\\r\\n\");\n    q\n}\n\n#[allow(clippy::needless_pass_by_value)]\npub fn http_serve(\n    p: Peer,\n    incoming: Option<Incoming<(Method, RequestUri)>>,\n    serve_static_files: Rc<Vec<StaticFile>>,\n) -> Box<dyn Future<Item = (), Error = ()>> {\n    let mut serve_file = None;\n    let content = if serve_static_files.is_empty() {\n        BAD_REQUEST.to_vec()\n    } else if let Some(inc) = incoming {\n        info!(\"HTTP-serving {:?}\", inc.subject);\n        if inc.subject.0 == Method::Get {\n            match inc.subject.1 {\n                AbsolutePath(x) => {\n                    let mut reply = None;\n                    for sf in &*serve_static_files {\n                        if sf.uri == x {\n                            match File::open(&sf.file) {\n                                Ok(f) => {\n                                    let fs = match f.metadata() {\n                                        Err(_) => None,\n                                        Ok(x) => Some(x.len()),\n                                    };\n                                    reply = Some(get_static_file_reply(fs, &sf.content_type));\n                                    serve_file = Some(f);\n                                }\n                                Err(_) => {\n                                    reply = Some(NOT_FOUND2.to_vec());\n                                }\n                            }\n                        }\n                    }\n                    reply.unwrap_or_else(|| NOT_FOUND.to_vec())\n                }\n                _ => BAD_URI_FORMAT.to_vec(),\n            }\n        } else {\n            BAD_METHOD.to_vec()\n        }\n    } else {\n        BAD_REQUEST.to_vec()\n    };\n    let reply = get_literal_peer_now(content);\n\n    let co = CopyOptions {\n        buffer_size: 1024,\n        once: false,\n        stop_on_reader_zero_read: true,\n        skip: false,\n        max_ops: None,\n    };\n\n    if let Some(f) = serve_file {\n        Box::new(\n            copy(reply, p.1, co, vec![])\n                .map_err(drop)\n                .and_then(move |(_len, _, conn)| {\n                    let co2 = CopyOptions {\n                        buffer_size: 65536,\n                        once: false,\n                        stop_on_reader_zero_read: true,\n                        skip: false,\n                        max_ops: None,\n                    };\n                    let wr = crate::file_peer::ReadFileWrapper(f);\n                    copy(wr, conn, co2, vec![]).map(|_| ()).map_err(drop)\n                }),\n        )\n    } else {\n        Box::new(copy(reply, p.1, co, vec![]).map(|_| ()).map_err(drop))\n    }\n}\n"
  },
  {
    "path": "src/jsonrpc_peer.rs",
    "content": "use futures::future::ok;\n\nuse std::rc::Rc;\n\nuse super::{BoxedNewPeerFuture, Peer};\nuse super::{ConstructParams, PeerConstructor, Specifier};\n\nuse std::io::Read;\nuse tokio_io::AsyncRead;\n\nuse std::io::Error as IoError;\n\n#[derive(Debug)]\npub struct JsonRpc<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for JsonRpc<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(cp.clone());\n        inner.map(move |p, _| jsonrpc_peer(p, cp.program_options.jsonrpc_omit_jsonrpc))\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = JsonRpcClass,\n    target = JsonRpc,\n    prefixes = [\"jsonrpc:\"],\n    arg_handling = subspec,\n    overlay = true,\n    MessageOriented,\n    MulticonnectnessDependsOnInnerType,\n    help = r#\"\n[A] Turns messages like `abc 1,2` into `{\"jsonrpc\":\"2.0\",\"id\":412, \"method\":\"abc\", \"params\":[1,2]}`.\n\nFor simpler manual testing of websocket-based JSON-RPC services\n\nExample: TODO\n\"#\n);\n\npub fn jsonrpc_peer(inner_peer: Peer, omit_jsonrpc: bool) -> BoxedNewPeerFuture {\n    let filtered = JsonRpcWrapper(inner_peer.0, 1, omit_jsonrpc);\n    let thepeer = Peer::new(filtered, inner_peer.1, inner_peer.2);\n    Box::new(ok(thepeer)) as BoxedNewPeerFuture\n}\nstruct JsonRpcWrapper(Box<dyn AsyncRead>, u64, bool);\n\nimpl Read for JsonRpcWrapper {\n    fn read(&mut self, b: &mut [u8]) -> Result<usize, IoError> {\n        let l = b.len();\n        assert!(l > 1);\n        let n = self.0.read(&mut b[..l])?;\n        if n == 0 {\n            return Ok(0);\n        }\n        let mut method = Vec::with_capacity(20);\n        let mut params = Vec::with_capacity(20);\n        enum PS {\n            BeforeMethodName,\n            InsideMethodName,\n            AfterMethodName,\n            InsideParams,\n        }\n        let mut s = PS::BeforeMethodName;\n        for &c in b[..n].iter() {\n            match s {\n                PS::BeforeMethodName => {\n                    if c == b' ' || c == b'\\t' || c == b'\\n' {\n                        // ignore\n                    } else {\n                        method.push(c);\n                        s = PS::InsideMethodName;\n                    }\n                }\n                PS::InsideMethodName => {\n                    if c == b' ' || c == b'\\t' || c == b'\\n' {\n                        s = PS::AfterMethodName;\n                    } else {\n                        method.push(c);\n                    }\n                }\n                PS::AfterMethodName => {\n                    if c == b' ' || c == b'\\t' || c == b'\\n' {\n                        // ignore\n                    } else {\n                        params.push(c);\n                        s = PS::InsideParams;\n                    }\n                }\n                PS::InsideParams => {\n                    params.push(c);\n                }\n            }\n        }\n\n        let mut bb = ::std::io::Cursor::new(b);\n        use std::io::Write;\n        //{\"jsonrpc\":\"2.0\",\"id\":412, \"method\":\"abc\", \"params\":[1,2]}\n        if self.2 {\n            let _ = bb.write_all(b\"{\\\"id\\\":\");\n        } else {\n            let _ = bb.write_all(b\"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":\");\n        }\n        let _ = bb.write_all(format!(\"{}\", self.1).as_bytes());\n        self.1 += 1;\n        let _ = bb.write_all(b\", \\\"method\\\":\\\"\");\n        let _ = bb.write_all(&method);\n        let _ = bb.write_all(b\"\\\", \\\"params\\\":\");\n        let needs_brackets = params.is_empty() || params[0] != b'{' && params[0] != b'[';\n        if !params.is_empty() && params[params.len() - 1] == b'\\n' {\n            let l = params.len() - 1;\n            params.truncate(l);\n        }\n        if !params.is_empty() && params[params.len() - 1] == b'\\r' {\n            let l = params.len() - 1;\n            params.truncate(l);\n        }\n        if needs_brackets {\n            let _ = bb.write_all(b\"[\");\n        }\n        let _ = bb.write_all(&params);\n        if needs_brackets {\n            let _ = bb.write_all(b\"]\");\n        }\n        let _ = bb.write_all(b\"}\\n\");\n        if bb.position() as usize == l {\n            warn!(\"Buffer too small, JSON RPC message may be truncated.\");\n        }\n        Ok(bb.position() as usize)\n    }\n}\nimpl AsyncRead for JsonRpcWrapper {}\n"
  },
  {
    "path": "src/lengthprefixed_peer.rs",
    "content": "use futures::future::ok;\n\nuse std::rc::Rc;\n\nuse crate::{io_other_error, simple_err, peer_strerr};\n\nuse super::{BoxedNewPeerFuture, Peer};\nuse super::{ConstructParams, PeerConstructor, Specifier};\n\nuse std::io::{Read, Write};\nuse tokio_io::{AsyncRead, AsyncWrite};\n\nuse std::io::Error as IoError;\n\n#[derive(Debug)]\npub struct LengthPrefixed<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for LengthPrefixed<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(cp.clone());\n        inner.map(move |p, _| {\n            lengthprefixed_peer(\n                p,\n                cp.program_options.lengthprefixed_header_bytes,\n                cp.program_options.lengthprefixed_little_endian,\n                cp.program_options.lengthprefixed_skip_read_direction,\n                cp.program_options.lengthprefixed_skip_write_direction,\n            )\n        })\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = LengthPrefixedClass,\n    target = LengthPrefixed,\n    prefixes = [\"lengthprefixed:\"],\n    arg_handling = subspec,\n    overlay = true,\n    MessageOriented,\n    MulticonnectnessDependsOnInnerType,\n    help = r#\"\nTurn stream of bytes to/from data packets with length-prefixed framing.  [A]\n\nYou can choose the number of header bytes (1 to 8) and endianness. Default is 4 bytes big endian.\n\nThis affects both reading and writing - attach this overlay to stream specifier to turn it into a packet-orineted specifier.\n\nMind the buffer size (-B). All packets should fit in there.\n\nExamples:\n\n    websocat -u -b udp-l:127.0.0.1:1234 lengthprefixed:writefile:test.dat\n\n    websocat -u -b lengthprefixed:readfile:test.dat udp:127.0.0.1:1235\n\nThis would save incoming UDP packets to a file, then replay the datagrams back to UDP socket\n\n    websocat -b lengthprefixed:- ws://127.0.0.1:1234/ --binary-prefix=B --text-prefix=T\n\nThis allows to mix and match text and binary WebSocket messages to and from stdio without the base64 overhead.\n\"#\n);\n\npub fn lengthprefixed_peer(\n    inner_peer: Peer,\n    num_bytes_in_length_prefix: usize,\n    little_endian: bool,\n    lengthprefixed_skip_read_direction: bool,\n    lengthprefixed_skip_write_direction: bool,\n) -> BoxedNewPeerFuture {\n    if !(1..=8).contains(&num_bytes_in_length_prefix) {\n        return peer_strerr(\"Number of header bytes for lengthprefixed overlay should be from 1 to 8\");\n    }\n\n    let (length_starting_pos, length_ending_pos) = if little_endian {\n        (0, num_bytes_in_length_prefix)\n    } else {\n        (8 - num_bytes_in_length_prefix, 8)\n    };\n    let reader = Lengthprefixed2PacketWrapper {\n        inner: inner_peer.0,\n        length_buffer: [0; 8],\n        length_starting_pos,\n        length_pos: length_starting_pos,\n        length_ending_pos,\n        little_endian,\n        data_read_so_far: 0,\n    };\n    let writer = Packet2LengthPrefixedWrapper {\n        inner: inner_peer.1,\n        length_buffer: [0; 8],\n        length_starting_pos,\n        length_pos: length_starting_pos,\n        length_ending_pos,\n        little_endian,\n        data_written_so_far: 0,\n    };\n    let thepeer = match (lengthprefixed_skip_read_direction, lengthprefixed_skip_write_direction) {\n        (true, true)   => Peer::new(reader.inner, writer.inner, inner_peer.2),\n        (true, false)  => Peer::new(reader.inner, writer,       inner_peer.2),\n        (false, true)  => Peer::new(reader,       writer.inner, inner_peer.2),\n        (false, false) => Peer::new(reader,       writer,       inner_peer.2),\n    };\n    Box::new(ok(thepeer)) as BoxedNewPeerFuture\n}\nstruct Lengthprefixed2PacketWrapper {\n    inner: Box<dyn AsyncRead>,\n    length_buffer: [u8; 8],\n    length_starting_pos: usize,\n    length_ending_pos: usize,\n    length_pos: usize,\n    little_endian: bool,\n    data_read_so_far: usize,\n}\nimpl Read for Lengthprefixed2PacketWrapper {\n    fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {\n        loop {\n            assert!(self.length_pos <= self.length_ending_pos);\n            assert!(self.length_pos >= self.length_starting_pos);\n            if self.length_ending_pos != self.length_pos {\n                match self\n                    .inner\n                    .read(&mut self.length_buffer[self.length_pos..self.length_ending_pos])\n                {\n                    Err(e) => return Err(e),\n                    Ok(0) => {\n                        if self.length_pos != self.length_starting_pos {\n                            error!(\"Possibly trimmed length-prefixed data.\")\n                        }\n                        return Ok(0);\n                    }\n                    Ok(n) => {\n                        self.length_pos += n;\n                        continue;\n                    }\n                }\n            } else {\n                let packet_len = if self.little_endian {\n                    u64::from_le_bytes(self.length_buffer)\n                } else {\n                    u64::from_be_bytes(self.length_buffer)\n                };\n                if packet_len >= (buf.len() as u64) {\n                    error!(\"Failed to process too big packet. You may need to increase the -B buffer size.\");\n                    return Err(io_other_error(simple_err(\"Packet length overflow\".into())));\n                }\n                let packet_len = packet_len as usize;\n                if packet_len == 0 {\n                    return Ok(0);\n                }\n\n                if self.data_read_so_far == packet_len {\n                    self.data_read_so_far = 0;\n                    self.length_buffer = [0; 8];\n                    self.length_pos = self.length_starting_pos;\n                    return Ok(packet_len);\n                }\n\n                // Assume we are called with the same buffer until we return success, so we\n                // can use buffer as a persistent scratch space\n                match self.inner.read(&mut buf[self.data_read_so_far..packet_len]) {\n                    Err(e) => return Err(e),\n                    Ok(0) => {\n                        return Err(io_other_error(simple_err(\"Data trimmed\".into())));\n                    }\n                    Ok(n) => {\n                        self.data_read_so_far += n;\n                        continue;\n                    }\n                }\n            }\n        }\n    }\n}\nimpl AsyncRead for Lengthprefixed2PacketWrapper {}\n\nstruct Packet2LengthPrefixedWrapper {\n    inner: Box<dyn AsyncWrite>,\n    length_buffer: [u8; 8],\n    length_starting_pos: usize,\n    length_ending_pos: usize,\n    length_pos: usize,\n    little_endian: bool,\n    data_written_so_far: usize,\n}\n\nimpl Write for Packet2LengthPrefixedWrapper {\n    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {\n        // Assuming `write` is retried with the same buffer when we return WouldBlock\n        loop {\n            if self.length_pos == self.length_starting_pos {\n                if self.little_endian {\n                    self.length_buffer = (buf.len() as u64).to_le_bytes()\n                } else {\n                    self.length_buffer = (buf.len() as u64).to_be_bytes()\n                }\n            }\n            if self.length_pos < self.length_ending_pos {\n                match self\n                    .inner\n                    .write(&self.length_buffer[self.length_pos..self.length_ending_pos])\n                {\n                    Err(x) => return Err(x),\n                    Ok(n) => self.length_pos += n,\n                }\n                continue;\n            }\n\n            if self.data_written_so_far == buf.len() {\n                self.data_written_so_far = 0;\n                self.length_pos = self.length_starting_pos;\n                self.length_buffer = [0; 8];\n                return Ok(buf.len());\n            }\n\n            match self.inner.write(&buf[self.data_written_so_far..]) {\n                Err(e) => return Err(e),\n                Ok(n) => self.data_written_so_far += n,\n            }\n        }\n    }\n\n    fn flush(&mut self) -> std::io::Result<()> {\n        self.inner.flush()\n    }\n}\n\nimpl AsyncWrite for Packet2LengthPrefixedWrapper {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        self.inner.shutdown()\n    }\n}\n"
  },
  {
    "path": "src/lib.rs",
    "content": "//! Note: library usage is not semver/API-stable\n//!\n//! Type evolution of a websocat run:\n//!\n//! 1. `&str` - string as passed to command line. When it meets the list of `SpecifierClass`es, there appears:\n//! 2. `SpecifierStack` - specifier class, final string argument and vector of overlays.\n//! 3. `Specifier` - more rigid version of SpecifierStack, with everything parsable parsed. May be nested. When `construct` is called, we get:\n//! 4. `PeerConstructor` - a future or stream that returns one or more connections. After completion, we get one or more of:\n//! 5. `Peer` - an active connection. Once we have two of them, we can start a:\n//! 6. `Session` with two `Transfer`s - forward and reverse.\n\n#![allow(renamed_and_removed_lints)]\n\nextern crate futures;\n#[macro_use]\nextern crate tokio_io;\nextern crate tokio_current_thread;\nextern crate tokio_reactor;\nextern crate tokio_tcp;\nextern crate tokio_udp;\nextern crate tokio_codec;\nextern crate tokio_timer;\nextern crate websocket;\nextern crate websocket_base;\nextern crate http_bytes;\nextern crate anymap;\npub use http_bytes::http;\n\nextern crate tk_listen;\nextern crate net2;\n\n#[macro_use]\nextern crate log;\n\n#[macro_use]\nextern crate slab_typesafe;\n\n#[macro_use]\nextern crate smart_default;\n#[macro_use]\nextern crate derivative;\n\nuse futures::future::Future;\nuse tokio_io::{AsyncRead, AsyncWrite};\n\nuse futures::Stream;\n\nuse std::cell::RefCell;\nuse std::rc::Rc;\nuse std::str::FromStr;\n\ntype Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;\n\n/// First representation of websocat command-line, partially parsed.\npub struct WebsocatConfiguration1 {\n    pub opts: Options,\n    pub addr1: String,\n    pub addr2: String,\n}\n\nimpl WebsocatConfiguration1 {\n    /// Is allowed to call blocking calls\n    /// happens only at start of websocat\n    pub fn parse1(self) -> Result<WebsocatConfiguration2> {\n        Ok(WebsocatConfiguration2 {\n            opts: self.opts,\n            s1: SpecifierStack::from_str(self.addr1.as_str())?,\n            s2: SpecifierStack::from_str(self.addr2.as_str())?,\n        })\n    }\n}\n\n/// Second representation of websocat configuration: everything\n/// (e.g. socket addresses) should already be parsed and verified\n/// A structural form: two chains of specifier nodes.\n/// Futures/async is not yet involved at this stage, but everything\n/// should be checked and ready to do to start it (apart from OS errors)\n/// \n/// This form is designed to be editable by lints and command-line options.\npub struct WebsocatConfiguration2 {\n    pub opts: Options,\n    pub s1: SpecifierStack,\n    pub s2: SpecifierStack,\n}\n\nimpl WebsocatConfiguration2 {\n    pub fn parse2(self) -> Result<WebsocatConfiguration3> {\n        Ok(WebsocatConfiguration3 {\n            opts: self.opts,\n            s1: <dyn Specifier>::from_stack(&self.s1)?,\n            s2: <dyn Specifier>::from_stack(&self.s2)?,\n        })\n    }\n}\n\n/// An immutable chain of functions that results in a `Future`s or `Streams` that rely on each other.\n/// This is somewhat like a frozen form of `WebsocatConfiguration2`.\npub struct WebsocatConfiguration3 {\n    pub opts: Options,\n    pub s1: Rc<dyn Specifier>,\n    pub s2: Rc<dyn Specifier>,\n}\n\nimpl WebsocatConfiguration3 {\n    pub fn serve<OE>(self, onerror: std::rc::Rc<OE>) -> impl Future<Item = (), Error = ()>\n    where\n        OE: Fn(Box<dyn std::error::Error>) + 'static,\n    {\n        serve(self.s1, self.s2, self.opts, onerror)\n    }\n}\n\npub mod options;\npub use crate::options::Options;\n\n#[derive(SmartDefault)]\npub struct ProgramState(\n    #[default(anymap::AnyMap::with_capacity(2))]\n    anymap::AnyMap\n);\n\n/// Some information passed from the left specifier Peer to the right\n#[derive(Default, Clone)]\npub struct LeftSpecToRightSpec {\n    /// URI the client requested when connecting to WebSocket\n    uri: Option<String>,\n    /// Address:port of connecting client, if it is TCP\n    client_addr: Option<String>,\n    /// All incoming HTTP headers\n    headers: Vec<(String, String)>,\n}\n\npub type L2rWriter = Rc<RefCell<LeftSpecToRightSpec>>;\npub type L2rReader = Rc<LeftSpecToRightSpec>;\n\n#[derive(Clone)]\npub enum L2rUser {\n    FillIn(L2rWriter),\n    ReadFrom(L2rReader),\n}\n\n/// Resolves if/when TCP socket gets reset\npub type HupToken = Box<dyn Future<Item=(), Error=Box<dyn std::error::Error>>>;\n\npub struct Peer(Box<dyn AsyncRead>, Box<dyn AsyncWrite>, Option<HupToken>);\n\npub type BoxedNewPeerFuture = Box<dyn Future<Item = Peer, Error = Box<dyn std::error::Error>>>;\npub type BoxedNewPeerStream = Box<dyn Stream<Item = Peer, Error = Box<dyn std::error::Error>>>;\n\n#[macro_use]\npub mod specifier;\npub use crate::specifier::{\n    ClassMessageBoundaryStatus, ClassMulticonnectStatus, ConstructParams, Specifier,\n    SpecifierClass, SpecifierStack,\n};\n\n#[macro_use]\npub mod all_peers;\n\npub mod lints;\nmod my_copy;\n\npub use crate::util::{brokenpipe, io_other_error, simple_err2, wouldblock};\n\n#[cfg(all(unix, feature = \"unix_stdio\"))]\npub mod stdio_peer;\n\npub mod file_peer;\npub mod mirror_peer;\npub mod net_peer;\npub mod stdio_threaded_peer;\npub mod trivial_peer;\npub mod ws_client_peer;\npub mod ws_peer;\npub mod ws_server_peer;\npub mod ws_lowlevel_peer;\npub mod http_peer;\n\n#[cfg(feature = \"tokio-process\")]\npub mod process_peer;\n\n\n#[cfg(all(windows,feature = \"windows_named_pipes\"))]\npub mod windows_np_peer;\n\n#[cfg(unix)]\npub mod unix_peer;\n\npub mod broadcast_reuse_peer;\npub mod jsonrpc_peer;\npub mod timestamp_peer;\npub mod line_peer;\npub mod lengthprefixed_peer;\npub mod foreachmsg_peer;\npub mod primitive_reuse_peer;\npub mod reconnect_peer;\n\npub mod socks5_peer;\n#[cfg(feature = \"ssl\")]\npub mod ssl_peer;\n\n#[cfg(feature = \"crypto_peer\")]\npub mod crypto_peer;\n\n#[cfg(feature = \"prometheus_peer\")]\npub mod prometheus_peer;\n\npub mod specparse;\n\npub type PeerOverlay = Rc<dyn Fn(Peer, L2rUser) -> BoxedNewPeerFuture>;\n\npub enum PeerConstructor {\n    ServeOnce(BoxedNewPeerFuture),\n    ServeMultipleTimes(BoxedNewPeerStream),\n    Overlay1(BoxedNewPeerFuture, PeerOverlay),\n    OverlayM(BoxedNewPeerStream, PeerOverlay),\n    Error(Box<dyn std::error::Error>),\n}\n\n/// A remnant of the hack\npub fn spawn_hack<T>(f: T)\nwhere\n    T: Future<Item = (), Error = ()> + 'static,\n{\n    tokio_current_thread::TaskExecutor::current()\n        .spawn_local(Box::new(f))\n        .unwrap()\n}\n\npub mod util;\npub use crate::util::{box_up_err, multi, once, peer_err, peer_err_s, peer_strerr, simple_err};\n\npub mod readdebt;\n\npub use crate::specparse::spec;\n\npub struct Transfer {\n    from: Box<dyn AsyncRead>,\n    to: Box<dyn AsyncWrite>,\n}\npub struct Session {\n    t1: Transfer,\n    t2: Transfer,\n    opts: Rc<Options>,\n    hup1: Option<HupToken>,\n    hup2: Option<HupToken>,\n}\n\npub mod sessionserve;\npub use crate::sessionserve::serve;\n"
  },
  {
    "path": "src/line_peer.rs",
    "content": "use futures::future::ok;\n\nuse std::rc::Rc;\n\nuse super::{BoxedNewPeerFuture, Peer};\nuse super::{ConstructParams, PeerConstructor, Specifier};\n\nuse std::io::Read;\nuse tokio_io::AsyncRead;\n\nuse std::io::Error as IoError;\n\n#[derive(Debug)]\npub struct Message2Line<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for Message2Line<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(cp.clone());\n        let zt = cp.program_options.linemode_zero_terminated;\n        inner.map(move |p, _| packet2line_peer(p, zt))\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = Message2LineClass,\n    target = Message2Line,\n    prefixes = [\"msg2line:\"],\n    arg_handling = subspec,\n    overlay = true,\n    StreamOriented,\n    MulticonnectnessDependsOnInnerType,\n    help = r#\"\nLine filter: Turns messages from packet stream into lines of byte stream. [A]\n\nEnsure each message (a chunk from one read call from underlying connection)\ncontains no inner newlines (or zero bytes) and terminates with one newline.\n\nReverse of the `line2msg:`.\n\nUnless --null-terminated, replaces both newlines (\\x0A) and carriage returns (\\x0D) with spaces (\\x20) for each read.\n\nDoes not affect writing at all. Use this specifier on both ends to get bi-directional behaviour.\n\nAutomatically inserted by --line option on top of the stack containing a websocket.\n\nExample: TODO\n\"#\n);\n\n#[derive(Debug)]\npub struct Line2Message<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for Line2Message<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let retain_newlines = !cp.program_options.linemode_strip_newlines;\n        let strict = cp.program_options.linemode_strict;\n        let nullt = cp.program_options.linemode_zero_terminated;\n        let inner = self.0.construct(cp.clone());\n        inner.map(move |p, _| line2packet_peer(p, retain_newlines, strict, nullt))\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name=Line2MessageClass, \n    target=Line2Message,\n    prefixes=[\"line2msg:\"], \n    arg_handling=subspec,\n    overlay = true,\n    MessageOriented,\n    MulticonnectnessDependsOnInnerType,\n    help=r#\"\nLine filter: turn lines from byte stream into messages as delimited by '\\\\n' or '\\\\0' [A]\n\nEnsure that each message (a successful read call) is obtained from a line [A]\ncoming from underlying specifier, buffering up or splitting content as needed.\n\nReverse of the `msg2line:`.\n\nDoes not affect writing at all. Use this specifier on both ends to get bi-directional behaviour.\n\nAutomatically inserted by --line option at the top of the stack opposite to websocket-containing stack.\n\nExample: TODO\n\"#\n);\n\npub fn packet2line_peer(inner_peer: Peer, null_terminated: bool) -> BoxedNewPeerFuture {\n    let filtered = Packet2LineWrapper(inner_peer.0, null_terminated);\n    let thepeer = Peer::new(filtered, inner_peer.1, inner_peer.2);\n    Box::new(ok(thepeer)) as BoxedNewPeerFuture\n}\nstruct Packet2LineWrapper(Box<dyn AsyncRead>, bool);\n\nimpl Read for Packet2LineWrapper {\n    fn read(&mut self, b: &mut [u8]) -> Result<usize, IoError> {\n        let l = b.len();\n        assert!(l > 1);\n        let mut n = self.0.read(&mut b[..(l - 1)])?;\n        if n == 0 {\n            return Ok(n);\n        }\n        if !self.1 {\n            // newline-terminated\n\n            // chomp away \\n or \\r\\n\n            if n > 0 && b[n - 1] == b'\\n' {\n                n -= 1;\n            }\n            if n > 0 && b[n - 1] == b'\\r' {\n                n -= 1;\n            }\n            // replace those with spaces\n            for c in b.iter_mut().take(n) {\n                if *c == b'\\n' || *c == b'\\r' {\n                    *c = b' ';\n                }\n            }\n            // add back one \\n\n            b[n] = b'\\n';\n            n += 1;\n        } else {\n            // null-terminated\n            if n > 0 && b[n - 1] == b'\\x00' {\n                n -= 1;\n            }\n            for c in b.iter_mut().take(n) {\n                if *c == b'\\x00' {\n                    warn!(\"zero byte in a message in null-terminated mode\");\n                }\n            }\n            b[n] = b'\\x00';\n            n += 1;\n        }\n\n        Ok(n)\n    }\n}\nimpl AsyncRead for Packet2LineWrapper {}\n\npub fn line2packet_peer(\n    inner_peer: Peer,\n    retain_newlines: bool,\n    strict: bool,\n    null_terminated: bool,\n) -> BoxedNewPeerFuture {\n    let filtered = Line2PacketWrapper {\n        inner: inner_peer.0,\n        queue: vec![],\n        retain_newlines,\n        allow_incomplete_lines: !strict,\n        drop_too_long_lines: strict,\n        eof: false,\n        null_terminated,\n    };\n    let thepeer = Peer::new(filtered, inner_peer.1, inner_peer.2);\n    Box::new(ok(thepeer)) as BoxedNewPeerFuture\n}\nstruct Line2PacketWrapper {\n    inner: Box<dyn AsyncRead>,\n    queue: Vec<u8>,\n    retain_newlines: bool,\n    allow_incomplete_lines: bool,\n    drop_too_long_lines: bool,\n    eof: bool,\n    null_terminated: bool,\n}\n\nimpl Line2PacketWrapper {\n    #[allow(clippy::collapsible_if)]\n    fn deliver_the_line(&mut self, buf: &mut [u8], mut n: usize) -> Option<usize> {\n        if n > buf.len() {\n            if self.drop_too_long_lines {\n                error!(\"Dropping too long line of {} bytes because of buffer (-B option) is only {} bytes\", n, buf.len());\n                drop(self.queue.drain(0..n));\n                return None;\n            } else {\n                warn!(\"Splitting too long line of {} bytes because of buffer (-B option) is only {} bytes\", n, buf.len());\n                n = buf.len();\n            }\n        } else {\n            if !self.retain_newlines && !self.null_terminated {\n                if n > 0 && (buf[n - 1] == b'\\n') {\n                    n -= 1\n                }\n                if n > 0 && (buf[n - 1] == b'\\r') {\n                    n -= 1\n                }\n            }\n            if self.null_terminated {\n                if n > 0 && (buf[n - 1] == b'\\x00') {\n                    n -= 1\n                }\n            }\n        }\n\n        buf[0..n].copy_from_slice(&self.queue[0..n]);\n        drop(self.queue.drain(0..n));\n        Some(n)\n    }\n}\n\nimpl Read for Line2PacketWrapper {\n    #[allow(clippy::collapsible_if)]\n    fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {\n        //eprint!(\"ql={} \", self.queue.len());\n        if self.eof {\n            return Ok(0);\n        }\n\n        let char_to_look_at = if self.null_terminated { b'\\x00' } else { b'\\n' };\n        let mut queued_line_len = None;\n        for i in 0..self.queue.len() {\n            if self.queue[i] == char_to_look_at {\n                queued_line_len = Some(i);\n                break;\n            }\n        }\n        //eprint!(\"qll={:?} \", queued_line_len);\n\n        if let Some(mut n) = queued_line_len {\n            n += 1;\n            if let Some(nn) = self.deliver_the_line(buf, n) {\n                Ok(nn)\n            } else {\n                // line dropped, recursing\n                self.read(buf)\n            }\n        } else {\n            let mut n = self.inner.read(buf)?;\n\n            if n == 0 {\n                self.eof = true;\n                if !self.queue.is_empty() {\n                    if self.allow_incomplete_lines {\n                        warn!(\"Sending possibly incomplete line.\");\n                        let bl = self.queue.len();\n                        if let Some(nn) = self.deliver_the_line(buf, bl) {\n                            return Ok(nn);\n                        }\n                    } else {\n                        warn!(\n                            \"Throwing away {} bytes of incomplete line\",\n                            self.queue.len()\n                        );\n                    }\n                }\n                return Ok(0);\n            }\n\n            let happy_case = if !self.null_terminated {\n                self.queue.is_empty() && (!buf[0..(n - 1)].contains(&b'\\n')) && buf[n - 1] == b'\\n'\n            } else {\n                self.queue.is_empty()\n                    && (!buf[0..(n - 1)].contains(&b'\\x00'))\n                    && buf[n - 1] == b'\\x00'\n            };\n\n            if happy_case {\n                // Specifically to avoid allocations when data is already nice\n                if !self.retain_newlines && !self.null_terminated {\n                    if n > 0 && (buf[n - 1] == b'\\n') {\n                        n -= 1\n                    }\n                    if n > 0 && (buf[n - 1] == b'\\r') {\n                        n -= 1\n                    }\n                }\n                if self.null_terminated {\n                    if n > 0 && (buf[n - 1] == b'\\x00') {\n                        n -= 1\n                    }\n                }\n                //eprintln!(\"happy n={}\", n);\n                Ok(n)\n            } else {\n                // Just queue up and recurse\n                self.queue.extend_from_slice(&buf[0..n]);\n                //eprintln!(\" recurse\");\n                self.read(buf)\n            }\n        }\n    }\n}\nimpl AsyncRead for Line2PacketWrapper {}\n"
  },
  {
    "path": "src/lints.rs",
    "content": "#![allow(clippy::collapsible_if,clippy::needless_pass_by_value)]\n\nuse super::{Options, Result, SpecifierClass, SpecifierStack, WebsocatConfiguration2};\nuse super::specifier::SpecifierNode;\nuse std::rc::Rc;\nuse std::str::FromStr;\nuse std::ops::Not;\n\nextern crate hyper;\nextern crate url;\n\nuse std::net::{IpAddr, SocketAddr};\n\nuse super::socks5_peer::{SocksHostAddr, SocksSocketAddr};\n\n#[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone)]\npub enum StdioUsageStatus {\n    /// Does not use standard input or output at all\n    None,\n    /// Uses a reuser for connecting multiple peers at stdio, not distinguishing between IsItself and Indirectly\n    WithReuser,\n    /// Stdio wrapped into something (but not the reuser)\n    Indirectly,\n    /// Is the `-` or `stdio:` or `threadedstdio:` itself.\n    IsItself,\n}\n\ntrait ClassExt {\n    fn is_stdio(&self) -> bool;\n    fn is_reuser(&self) -> bool;\n}\n\npub type OnWarning = Box<dyn for<'a> Fn(&'a str) + 'static>;\n\n#[rustfmt::skip]\nimpl ClassExt for Rc<dyn SpecifierClass> {\n    fn is_stdio(&self) -> bool {\n        [\n            \"StdioClass\",\n            \"ThreadedStdioClass\",\n            \"ThreadedStdioSubstituteClass\",\n        ].contains(&self.get_name())\n    }\n    fn is_reuser(&self) -> bool {\n        [\n            \"ReuserClass\",\n            \"BroadcastReuserClass\",\n        ].contains(&self.get_name())\n    }\n}\n\npub trait SpecifierStackExt {\n    fn stdio_usage_status(&self) -> StdioUsageStatus;\n    fn reuser_count(&self) -> usize;\n    fn contains(&self, t: &'static str) -> bool;\n    fn is_multiconnect(&self) -> bool;\n    fn is_stream_oriented(&self) -> bool;\n    fn autotoreconn_misuse(&self)  -> bool;\n    fn insert_line_class_in_proper_place(&mut self, x: Rc<dyn SpecifierClass>);\n}\nimpl SpecifierStackExt for SpecifierStack {\n    fn stdio_usage_status(&self) -> StdioUsageStatus {\n        use self::StdioUsageStatus::*;\n\n        if !self.addrtype.cls.is_stdio() {\n            return None;\n        }\n\n        let mut sus: StdioUsageStatus = IsItself;\n\n        for overlay in self.overlays.iter().rev() {\n            if overlay.cls.is_reuser() {\n                sus = WithReuser;\n            } else if sus == IsItself {\n                sus = Indirectly;\n            }\n        }\n        sus\n    }\n    fn reuser_count(&self) -> usize {\n        let mut c = 0;\n        for overlay in &self.overlays {\n            if overlay.cls.is_reuser() {\n                c += 1;\n            }\n        }\n        c\n    }\n    fn contains(&self, t: &'static str) -> bool {\n        for overlay in &self.overlays {\n            if overlay.cls.get_name() == t {\n                return true;\n            }\n        }\n        self.addrtype.cls.get_name() == t\n    }\n    fn autotoreconn_misuse(&self)  -> bool {\n        let mut autoreconnect_found = false;\n        for overlay in &self.overlays {\n            if overlay.cls.get_name() == \"AutoReconnectClass\" {\n                autoreconnect_found = true;\n            }\n            if overlay.cls.get_name() == \"BroadcastReuserClass\" || overlay.cls.get_name() == \"ReuserClass\" {\n                if autoreconnect_found {\n                    return true;\n                }\n            }\n        }\n        false\n    }\n    fn is_multiconnect(&self) -> bool {\n        use super::ClassMulticonnectStatus::*;\n        match self.addrtype.cls.multiconnect_status() {\n            MultiConnect => (),\n            SingleConnect => return false,\n            MulticonnectnessDependsOnInnerType => unreachable!(),\n        }\n        for overlay in self.overlays.iter().rev() {\n            match overlay.cls.multiconnect_status() {\n                MultiConnect => (),\n                SingleConnect => return false,\n                MulticonnectnessDependsOnInnerType => (),\n            }\n        }\n        true\n    }\n    fn is_stream_oriented(&self) -> bool {\n        use super::ClassMessageBoundaryStatus::*;\n        let mut q = match self.addrtype.cls.message_boundary_status() {\n            StreamOriented => true,\n            MessageOriented => false,\n            MessageBoundaryStatusDependsOnInnerType => unreachable!(),\n        };\n        for overlay in self.overlays.iter().rev() {\n            match overlay.cls.message_boundary_status() {\n                StreamOriented => q = true,\n                MessageOriented => q = false,\n                MessageBoundaryStatusDependsOnInnerType => (),\n            }\n        }\n        q\n    }\n    fn insert_line_class_in_proper_place(&mut self, x: Rc<dyn SpecifierClass>) {\n        use super::ClassMessageBoundaryStatus::*;\n        let mut insert_idx = 0;\n        for overlay in &self.overlays {\n            match overlay.cls.message_boundary_status() {\n                StreamOriented => break,\n                MessageOriented => break,\n                MessageBoundaryStatusDependsOnInnerType => insert_idx += 1,\n            }\n        }\n        self.overlays.insert(insert_idx, SpecifierNode{cls: x});\n    }\n}\n\nimpl WebsocatConfiguration2 {\n    pub fn inetd_mode(&self) -> bool {\n        self.contains_class(\"InetdClass\")\n    }\n\n    #[rustfmt::skip]\n    #[allow(clippy::nonminimal_bool)]\n    pub fn websocket_used(&self) -> bool {\n        false \n        || self.contains_class(\"WsConnectClass\")\n        || self.contains_class(\"WsClientClass\")\n        || self.contains_class(\"WsClientSecureClass\")\n        || self.contains_class(\"WsServerClass\")\n    }\n\n    #[rustfmt::skip]\n    #[allow(clippy::nonminimal_bool)]\n    pub fn exec_used(&self) -> bool {\n        false \n        || self.contains_class(\"ExecClass\")\n        || self.contains_class(\"CmdClass\")\n        || self.contains_class(\"ShCClass\")\n    }\n\n    pub fn contains_class(&self, x: &'static str) -> bool {\n        self.s1.contains(x) || self.s2.contains(x)\n    }\n\n    pub fn get_exec_parameter(&self) -> Option<&str> {\n        if self.s1.addrtype.cls.get_name() == \"ExecClass\" {\n            return Some(self.s1.addr.as_str());\n        }\n        if self.s2.addrtype.cls.get_name() == \"ExecClass\" {\n            return Some(self.s2.addr.as_str());\n        }\n        None\n    }\n\n    fn l_stdio(&mut self, multiconnect: bool, reuser_has_been_inserted: &mut bool, r#async: bool) -> Result<()> {\n        use self::StdioUsageStatus::{Indirectly, IsItself, None, WithReuser};\n        match (self.s1.stdio_usage_status(), self.s2.stdio_usage_status()) {\n            (_, None) => (),\n            (None, WithReuser) => (),\n            (None, IsItself) | (None, Indirectly) => {\n                if multiconnect {\n                    self.s2.overlays.insert(\n                        0,\n                        SpecifierNode{cls:Rc::new(super::broadcast_reuse_peer::BroadcastReuserClass)},\n                    );\n                    *reuser_has_been_inserted = true;\n                }\n            }\n            (IsItself, IsItself) => {\n                info!(\"Special mode, exception from usual one-stdio rule. Acting like `cat(1)`\");\n                self.s2 = SpecifierStack::from_str(\"mirror:\")?;\n                if self.opts.unidirectional ^ self.opts.unidirectional_reverse {\n                    self.opts.unidirectional = false;\n                    self.opts.unidirectional_reverse = false;\n                }\n                return Ok(());\n            }\n            (_, _) => {\n                Err(\"Too many usages of stdin/stdout. Specify it either on left or right address, not on both.\")?;\n            }\n        }\n\n        #[cfg(all(unix, feature = \"unix_stdio\"))]\n        {\n            if r#async {\n                if self.s1.addrtype.cls.get_name() == \"StdioClass\" {\n                    debug!(\"Substituting StdioClass with AsyncStdioClass at the left\");\n                    self.s1.addrtype = SpecifierNode{cls:Rc::new(crate::stdio_peer::AsyncStdioClass)};\n                } \n                if self.s2.addrtype.cls.get_name() == \"StdioClass\" {\n                    debug!(\"Substituting StdioClass with AsyncStdioClass at the right\");\n                    self.s2.addrtype = SpecifierNode{cls:Rc::new(crate::stdio_peer::AsyncStdioClass)};\n                } \n            }\n        }\n\n        Ok(())\n    }\n\n    fn l_reuser(&mut self, reuser_has_been_inserted: bool) -> Result<()> {\n        if self.s1.reuser_count() + self.s2.reuser_count() > 1 {\n            if reuser_has_been_inserted {\n                error!(\"The reuser you specified conflicts with automatically inserted reuser based on usage of stdin/stdout in multiconnect mode.\");\n            }\n            Err(\"Too many usages of connection reuser. Please limit to only one instance.\")?;\n        }\n        Ok(())\n    }\n\n    fn l_linemode(&mut self) -> Result<()> {\n        if !self.opts.no_auto_linemode && self.opts.websocket_text_mode {\n            match (self.s1.is_stream_oriented(), self.s2.is_stream_oriented()) {\n                (false, false) => {}\n                (true, true) => {}\n                (true, false) => {\n                    info!(\"Auto-inserting the line mode\");\n                    self.s1.insert_line_class_in_proper_place(Rc::new(\n                        super::line_peer::Line2MessageClass,\n                    ));\n                    self.s2.insert_line_class_in_proper_place(Rc::new(\n                        super::line_peer::Message2LineClass,\n                    ));\n                }\n                (false, true) => {\n                    info!(\"Auto-inserting the line mode\");\n                    self.s2.insert_line_class_in_proper_place(Rc::new(\n                        super::line_peer::Line2MessageClass,\n                    ));\n                    self.s1.insert_line_class_in_proper_place(Rc::new(\n                        super::line_peer::Message2LineClass,\n                    ));\n                }\n            }\n        };\n        Ok(())\n    }\n    fn l_listener_on_the_right(&mut self, on_warning: &OnWarning) -> Result<()> {\n        if !self.opts.oneshot && self.s2.is_multiconnect() && !self.s1.is_multiconnect() {\n            on_warning(\"You have specified a listener on the right (as the second positional argument) instead of on the left. It will only serve one connection.\\nChange arguments order to enable multiple parallel connections or use --oneshot argument to make single connection explicit.\");\n        }\n        Ok(())\n    }\n    fn l_reuser_for_append(&mut self, multiconnect: bool) -> Result<()> {\n        if multiconnect\n            && (self.s2.addrtype.cls.get_name() == \"WriteFileClass\"\n                || self.s2.addrtype.cls.get_name() == \"AppendFileClass\")\n            && self.s2.reuser_count() == 0\n        {\n            info!(\"Auto-inserting the reuser\");\n            self.s2\n                .overlays\n                .push(SpecifierNode{cls:Rc::new(super::primitive_reuse_peer::ReuserClass)});\n        };\n        Ok(())\n    }\n    fn l_exec(&mut self, on_warning: &OnWarning) -> Result<()> {\n        if self.s1.addrtype.cls.get_name() == \"ExecClass\" && self.s2.addrtype.cls.get_name() == \"ExecClass\"\n        {\n            Err(\"Can't use exec: more than one time. Replace one of them with sh-c: or cmd:.\")?;\n        }\n\n        if let Some(x) = self.get_exec_parameter() {\n            if self.opts.exec_args.is_empty() && x.contains(' ') {\n                on_warning(\"Warning: you specified exec: without the corresponding --exec-args at the end of command line. Unlike in cmd: or sh-c:, spaces inside exec:'s direct parameter are interpreted as part of program name, not as separator.\");\n            }\n        }\n        Ok(())\n    }\n    fn l_uri_staticfiles(&mut self, on_warning: &OnWarning) -> Result<()> {\n        if self.opts.restrict_uri.is_some() && !self.contains_class(\"WsServerClass\") {\n            on_warning(\"--restrict-uri is meaningless without a WebSocket server\");\n        }\n\n        if !self.opts.serve_static_files.is_empty() && !self.contains_class(\"WsServerClass\") {\n            on_warning(\"--static-file (-F) is meaningless without a WebSocket server\");\n        }\n\n        for sf in &self.opts.serve_static_files {\n            if !sf.uri.starts_with('/') {\n                on_warning(&format!(\n                    \"Static file's URI `{}` should begin with `/`?\",\n                    sf.uri\n                ));\n            }\n            if !sf.file.exists() {\n                on_warning(&format!(\"File {:?} does not exist\", sf.file));\n            }\n            if !sf.content_type.contains('/') {\n                on_warning(&format!(\n                    \"Content-Type `{}` lacks `/` character\",\n                    sf.content_type\n                ));\n            }\n        }\n        Ok(())\n    }\n    fn l_environ(&mut self, on_warning: &OnWarning) -> Result<()> {\n        if self.opts.exec_set_env {\n            if !self.exec_used() {\n                on_warning(\"-e (--set-environment) is meaningless without a exec: or sh-c: or cmd: address\");\n            }\n            if !self.contains_class(\"TcpListenClass\") && !self.contains_class(\"WsServerClass\") {\n                on_warning(\"-e (--set-environment) is currently meaningless without a websocket server and/or TCP listener\");\n            }\n        }\n\n        if !self.opts.headers_to_env.is_empty() && !self.opts.exec_set_env {\n            on_warning(\"--header-to-env is meaningless without -e (--set-environment)\");\n        }\n\n        Ok(())\n    }\n    fn l_closebug(&mut self, on_warning: &OnWarning) -> Result<()> {\n        if !self.opts.oneshot && self.s1.is_multiconnect() {\n            if self.s1.contains(\"TcpListenClass\")\n                || self.s1.contains(\"UnixListenClass\")\n                || self.s1.contains(\"SeqpacketListenClass\")\n            {\n                if !self.opts.unidirectional\n                    && (self.opts.unidirectional_reverse || !self.opts.exit_on_eof)\n                {\n                    on_warning(\"Unfortunately, serving multiple clients without --exit-on-eof (-E) or with -U option is prone to socket leak in this websocat version\");\n                }\n            }\n        }\n        Ok(())\n    }\n\n    fn l_socks5_c(\n        s: &mut SpecifierStack,\n        opts: &mut Options,\n        on_warning: &OnWarning,\n        secure: bool,\n    ) -> Result<()> {\n        let url = if secure {\n            #[cfg(not(feature = \"ssl\"))]\n            {\n                Err(\"SSL support not compiled in\")?;\n            }\n            format!(\"wss://{}\", s.addr)\n        } else {\n            format!(\"ws://{}\", s.addr)\n        };\n\n        // Overwrite WsClientClass\n        s.addrtype = SpecifierNode{cls: Rc::new(super::net_peer::TcpConnectClass) };\n\n        match opts.auto_socks5.unwrap() {\n            SocketAddr::V4(sa4) => {\n                s.addr = format!(\"{}:{}\", sa4.ip(), sa4.port());\n            }\n            SocketAddr::V6(sa6) => {\n                s.addr = format!(\"[{}]:{}\", sa6.ip(), sa6.port());\n            }\n        }\n\n        use self::hyper::Url;\n        use self::url::Host;\n        let u = Url::parse(&url)?;\n\n        if !u.has_host() {\n            Err(\"WebSocket URL has no host\")?;\n        }\n\n        let port = u.port_or_known_default().unwrap_or(80);\n        let host = u.host().unwrap();\n\n        let host = match host {\n            Host::Domain(dom) => SocksHostAddr::Name(dom.to_string()),\n            Host::Ipv4(ip4) => SocksHostAddr::Ip(IpAddr::V4(ip4)),\n            Host::Ipv6(ip6) => SocksHostAddr::Ip(IpAddr::V6(ip6)),\n        };\n        if opts.socks_destination.is_none() {\n            opts.socks_destination = Some(SocksSocketAddr { host, port });\n        }\n        if secure && opts.tls_domain.is_none() {\n            opts.tls_domain = u.host_str().map(|x| x.to_string());\n        }\n\n        if opts.ws_c_uri != \"ws://0.0.0.0/\" {\n            on_warning(\n                \"Looks like you've overridden ws-c-uri. We are overwriting it for --socks5 option.\",\n            );\n        }\n\n        opts.ws_c_uri = url;\n\n        s.overlays\n            .push(SpecifierNode{cls: Rc::new(super::ws_client_peer::WsConnectClass)});\n        if secure {\n            #[cfg(feature = \"ssl\")]\n            s.overlays.push(SpecifierNode{cls: Rc::new(super::ssl_peer::TlsConnectClass)});\n        }\n        s.overlays.push(SpecifierNode{cls: Rc::new(super::socks5_peer::SocksProxyClass)});\n\n        Ok(())\n    }\n\n    fn l_socks5(&mut self, on_warning: &OnWarning) -> Result<()> {\n        if self.opts.socks_destination.is_some()\n            ^ (self.contains_class(\"SocksProxyClass\") || self.contains_class(\"SocksBindClass\"))\n        {\n            on_warning(\n                \"--socks5-destination option and socks5-connect: overlay should go together\",\n            );\n        }\n\n        if self.opts.socks5_bind_script.is_some() ^ self.contains_class(\"SocksBindClass\") {\n            on_warning(\"--socks5-bind-script option and socks5-bind: overlay should go together\");\n        }\n\n        if self.opts.auto_socks5.is_some() {\n            if !((self.s1.addrtype.cls.get_name() == \"WsClientClass\"\n                || self.s1.addrtype.cls.get_name() == \"WsClientSecureClass\")\n                ^ (self.s2.addrtype.cls.get_name() == \"WsClientClass\"\n                    || self.s2.addrtype.cls.get_name() == \"WsClientSecureClass\"))\n            {\n                Err(\"User-friendly --socks5 option supports socksifying exactly one non-raw websocket client connection. You are using two or none.\")?;\n            }\n\n            if self.s1.addrtype.cls.get_name() == \"WsClientClass\" {\n                WebsocatConfiguration2::l_socks5_c(\n                    &mut self.s1,\n                    &mut self.opts,\n                    on_warning,\n                    false,\n                )?;\n            }\n            if self.s1.addrtype.cls.get_name() == \"WsClientSecureClass\" {\n                WebsocatConfiguration2::l_socks5_c(&mut self.s1, &mut self.opts, on_warning, true)?;\n            }\n            if self.s2.addrtype.cls.get_name() == \"WsClientClass\" {\n                WebsocatConfiguration2::l_socks5_c(\n                    &mut self.s2,\n                    &mut self.opts,\n                    on_warning,\n                    false,\n                )?;\n            }\n            if self.s2.addrtype.cls.get_name() == \"WsClientSecureClass\" {\n                WebsocatConfiguration2::l_socks5_c(&mut self.s2, &mut self.opts, on_warning, true)?;\n            }\n        }\n        Ok(())\n    }\n\n    #[cfg(feature = \"ssl\")]\n    fn l_ssl(&mut self, _on_warning: &OnWarning) -> Result<()> {\n        if self.opts.pkcs12_der.is_some() &&  !self.contains_class(\"TlsAcceptClass\") {\n            Err(\"--pkcs12-der makes no sense without an TLS connections acceptor\")?;\n        }\n        if self.opts.pkcs12_der.is_none() &&  self.contains_class(\"TlsAcceptClass\") {\n            Err(\"You need to specify server key and certificate using the --pkcs12-der option to use the TLS connections acceptor\")?;\n        }\n        if self.opts.client_pkcs12_der.is_some() && !self.contains_class(\"WsClientSecureClass\") && !self.contains_class(\"TlsConnectClass\") {\n            Err(\"--client-pkcs12-der makes no sense without wss:// or ssl: connectors\")?;\n        }\n        #[cfg(target_os = \"macos\")]\n        {\n            if (self.opts.pkcs12_der.is_some() && self.opts.pkcs12_passwd.is_none()) || (self.opts.client_pkcs12_der.is_some() && self.opts.client_pkcs12_passwd.is_none()) {\n                _on_warning(\"PKCS12 archives without password may be unsupported on Mac\");\n\n                for x in ::std::env::args() {\n                    if x.contains(\"test.pkcs12\") {\n                        _on_warning(\"If you want a pre-made test certificate, use other file: `--pkcs12-der 1234.pkcs12 --pkcs12-passwd 1234`\");\n                        break;\n                    }\n                }\n            }\n        }\n        Ok(())\n    }\n\n    fn l_ping(&mut self, _on_warning: &OnWarning) -> Result<()> {\n        if self.opts.ws_ping_interval.is_some() || self.opts.ws_ping_timeout.is_some() {\n            if !self.websocket_used() {\n                _on_warning(\"--ping-interval or --ping-timeout options are not effective if no WebSocket usage is specified\")\n            }\n        }\n        if self.opts.ws_ping_timeout.is_some() && self.opts.ws_ping_interval.is_none() {\n            _on_warning(\"--ping-timeout specified without --ping-interval. This will probably lead to unconditional disconnection after that interval.\")\n        }\n        if let (Some(t), Some(i)) = (self.opts.ws_ping_timeout, self.opts.ws_ping_interval) {\n            if t <= i {\n                _on_warning(\"--ping-timeout's value is not more than --ping-interval. Expect spurious disconnections.\");\n            }\n        }\n        if self.opts.ws_ping_timeout.is_some() {\n            if self.opts.unidirectional_reverse || self.opts.exit_on_eof {\n                // OK\n            } else {\n                _on_warning(\"--ping-interval is currently not very effective without -E or -U\")\n            }\n        }\n        if self.opts.print_ping_rtts && self.opts.ws_ping_interval.is_none() {\n            _on_warning(\"--print-ping-rtts is not effective without --ping-interval\");\n        }\n        Ok(())\n    }\n\n    fn l_proto(&mut self, _on_warning: &OnWarning) -> Result<()> {\n        if self.opts.websocket_protocol.is_some() {\n            if !(self.contains_class(\"WsConnectClass\")\n                || self.contains_class(\"WsClientClass\")\n                || self.contains_class(\"WsClientSecureClass\"))\n            {\n                if self.contains_class(\"WsServerClass\") {\n                    _on_warning(\"--protocol option is unused. Maybe you want --server-protocol?\")\n                } else {\n                    _on_warning(\"--protocol option is unused.\")\n                }\n            }\n        }\n        if self.opts.websocket_reply_protocol.is_some() {\n            if !self.contains_class(\"WsServerClass\") {\n                _on_warning(\"--server-protocol option is unused.\")\n            }\n        }\n        Ok(())\n    }\n\n    fn l_eeof_unidir(&mut self, _on_warning: &OnWarning) -> Result<()> {\n        if self.opts.exit_on_eof {\n           if self.opts.unidirectional || self.opts.unidirectional_reverse {\n               _on_warning(\"--exit-on-eof and --unidirectional[-reverse] options are now useless together\");\n               _on_warning(\"You may want to remove --exit-on-eof. If you are happy with what happens, consider `-uU` instead of `-uE`.\");\n           }\n        }\n        Ok(())\n    }\n\n    fn l_udp(&mut self, _on_warning: &OnWarning) -> Result<()> {\n        if self.opts.udp_join_multicast_addr.is_empty().not() {\n            if self.opts.udp_broadcast {\n                _on_warning(\"Both --udp-broadcast and a multicast address is set. This is strange.\");\n            }\n            let ifs = self.opts.udp_join_multicast_iface_v4.len() + self.opts.udp_join_multicast_iface_v6.len();\n            if ifs != 0 {\n                let mut v4_multicasts = 0;\n                let mut v6_multicasts = 0;\n                for i in &self.opts.udp_join_multicast_addr {\n                    match i {\n                        std::net::IpAddr::V4(_) => v4_multicasts += 1,\n                        std::net::IpAddr::V6(_) => v6_multicasts += 1,\n                    }\n                }\n                if v4_multicasts != self.opts.udp_join_multicast_iface_v4.len() {\n                    return Err(\"--udp-multicast-iface-v4 option mush be specified the same number of times as IPv4 addresses for --udp-multicast (alternatively --udp-multicast-iface-* options should be not specified at all)\")?;\n                }\n                if v6_multicasts != self.opts.udp_join_multicast_iface_v6.len() {\n                    return Err(\"--udp-multicast-iface-v6 option mush be specified the same number of times as IPv6 addresses for --udp-multicast (alternatively --udp-multicast-iface-* options should be not specified at all)\")?;\n                }\n            }\n        } else  if self.opts.udp_multicast_loop {\n            return Err(\"--udp-multicast-loop is not applicable without --udp-multicast\")?;\n        }\n        Ok(())\n    }\n    fn l_crypto(&mut self, _on_warning: &OnWarning) -> Result<()> {\n        #[cfg(feature=\"crypto_peer\")]\n        if self.opts.crypto_key.is_some() {\n            if !self.contains_class(\"CryptoClass\") {\n                _on_warning(\"--crypto-key option is meaningless without a `crypto:` overlay\");\n            }\n        }\n        Ok(())\n    }\n    fn l_prometheus(&mut self, _on_warning: &OnWarning) -> Result<()> {\n        #[cfg(feature=\"prometheus_peer\")]\n        if self.opts.prometheus.is_some() {\n            if !self.contains_class(\"PrometheusClass\") {\n                self.s2.overlays.insert(0, SpecifierNode { cls: Rc::new(crate::prometheus_peer::PrometheusClass) });\n            }\n        } else if self.contains_class(\"PrometheusClass\") {\n            _on_warning(\"Using `prometheus:` overlay without `--prometheus` option is meaningless\");\n        }\n        Ok(())\n    }\n    fn l_sizelimits(&mut self, _on_warning: &OnWarning) -> Result<()> {\n        if self.opts.max_ws_message_length < self.opts.max_ws_frame_length {\n            _on_warning(\"Lowering --max-ws-message-length without also lowering --max-ws-frame-length may be meaningless, as the former only affects whether to begin accept a new frame or not, given accumulated message size. Succesfully accepted frames within the frame size limit may exceed the message size.\")\n        }\n        Ok(())\n    }\n    fn l_compress(&mut self, _on_warning: &OnWarning) -> Result<()> {\n        let mut cn = 0;\n        let mut un = 0;\n        if self.opts.compress_deflate { cn += 1 }\n        if self.opts.compress_gzip { cn += 1 }\n        if self.opts.compress_zlib { cn += 1 }\n        if self.opts.uncompress_deflate { un += 1 }\n        if self.opts.uncompress_gzip { un += 1 }\n        if self.opts.uncompress_zlib { un += 1 }\n        if cn > 1 {\n            return Err(\"Multiple --compress-* options specifed\")?;\n        }\n        if un > 1 {\n            return Err(\"Multiple --uncompress-* options specifed\")?;\n        }\n\n        #[cfg(not(feature=\"compression\"))]\n        {\n            if cn > 0 || un > 0 {\n                return Err(\"Compression support is not selected during Websocat compilation\")?;\n            }\n        }\n        Ok(())\n    }\n\n    fn l_autoreconn_reuse(&mut self, _on_warning: &OnWarning) -> Result<()> {\n        if self.s1.autotoreconn_misuse() || self.s2.autotoreconn_misuse() {\n            _on_warning(\"Warning: `autoreconnect:reuse:` is a bad overlay combination. Maybe you want `reuse:autoreconnect:\");\n        }\n        Ok(())\n    }\n\n    pub fn lint_and_fixup(&mut self, on_warning: OnWarning) -> Result<()> {\n        let multiconnect = !self.opts.oneshot && self.s1.is_multiconnect();\n        let mut reuser_has_been_inserted = false;\n\n        self.l_prometheus(&on_warning)?;\n        self.l_stdio(multiconnect, &mut reuser_has_been_inserted, self.opts.asyncstdio)?;\n        self.l_reuser(reuser_has_been_inserted)?;\n        self.l_linemode()?;\n        self.l_listener_on_the_right(&on_warning)?;\n        self.l_reuser_for_append(multiconnect)?;\n        self.l_exec(&on_warning)?;\n        self.l_uri_staticfiles(&on_warning)?;\n        self.l_environ(&on_warning)?;\n        self.l_closebug(&on_warning)?;\n        self.l_socks5(&on_warning)?;\n        #[cfg(feature = \"ssl\")]\n        self.l_ssl(&on_warning)?;\n        self.l_ping(&on_warning)?;\n        self.l_proto(&on_warning)?;\n        self.l_eeof_unidir(&on_warning)?;\n        self.l_udp(&on_warning)?;\n        self.l_crypto(&on_warning)?;\n        self.l_sizelimits(&on_warning)?;\n        self.l_compress(&on_warning)?;\n        self.l_autoreconn_reuse(&on_warning)?;\n\n        // TODO: UDP connect oneshot mode\n        // TODO: tests for the linter\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/main.rs",
    "content": "#![allow(renamed_and_removed_lints)]\n#![allow(unknown_lints)]\n#![allow(clippy::deprecated_cfg_attr)]\n\n#[macro_use]\nextern crate websocat;\n\nextern crate futures;\nextern crate tokio;\nextern crate tokio_stdin_stdout;\n\nextern crate websocket_base;\n\nextern crate env_logger;\n#[macro_use]\nextern crate log;\n\n#[cfg(feature = \"openssl-probe\")]\nextern crate openssl_probe;\n\n#[allow(unused_imports)]\n#[macro_use]\nextern crate structopt;\n\nextern crate atty;\n\nextern crate http_bytes;\nuse http_bytes::http;\n\nuse std::net::{IpAddr, SocketAddr};\nuse std::path::PathBuf;\n\nuse structopt::StructOpt;\n\nuse websocat::options::StaticFile;\nuse websocat::socks5_peer::{SocksHostAddr, SocksSocketAddr};\nuse websocat::{Options, SpecifierClass, WebsocatConfiguration1};\n\ntype Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;\n\nuse std::ffi::OsString;\n\n#[derive(StructOpt, Debug)]\n#[structopt(\n    after_help = \"\nBasic examples:\n  Command-line websocket client:\n    websocat ws://ws.vi-server.org/mirror/\n    \n  WebSocket server\n    websocat -s 8080\n    \n  WebSocket-to-TCP proxy:\n    websocat --binary ws-l:127.0.0.1:8080 tcp:127.0.0.1:5678\n    \n\",\n    usage = \"websocat ws://URL | wss://URL               (simple client)\\n    websocat -s port                            (simple server)\\n    websocat [FLAGS] [OPTIONS] <addr1> <addr2>  (advanced mode)\"\n)]\nstruct Opt {\n    /// In simple mode, WebSocket URL to connect.\n    /// In advanced mode first address (there are many kinds of addresses) to use.\n    /// See --help=types for info about address types.\n    /// If this is an address for listening, it will try serving multiple connections.\n    addr1: Option<String>,\n    /// In advanced mode, second address to connect.\n    /// If this is an address for listening, it will accept only one connection.\n    addr2: Option<String>,\n\n    #[structopt(\n        short = \"u\",\n        long = \"unidirectional\",\n        help = \"Inhibit copying data in one direction\"\n    )]\n    unidirectional: bool,\n    #[structopt(\n        short = \"U\",\n        long = \"unidirectional-reverse\",\n        help = \"Inhibit copying data in the other direction (or maybe in both directions if combined with -u)\"\n    )]\n    unidirectional_reverse: bool,\n\n    #[structopt(\n        long = \"exit-on-eof\",\n        short = \"E\",\n        help = \"Close a data transfer direction if the other one reached EOF\"\n    )]\n    exit_on_eof: bool,\n\n    #[structopt(\n        short = \"t\",\n        long = \"text\",\n        help = \"Send message to WebSockets as text messages\"\n    )]\n    websocket_text_mode: bool,\n\n    #[structopt(\n        short = \"b\",\n        long = \"binary\",\n        help = \"Send message to WebSockets as binary messages\"\n    )]\n    websocket_binary_mode: bool,\n\n    #[structopt(\n        long = \"oneshot\",\n        help = \"Serve only once. Not to be confused with -1 (--one-message)\"\n    )]\n    oneshot: bool,\n\n    #[structopt(\n        short = \"h\",\n        long = \"help\",\n        help = \"See the help.\\n--help=short is the list of easy options and address types\\n--help=long lists all options and types (see [A] markers)\\n--help=doc also shows longer description and examples.\"\n    )]\n    help: Option<String>,\n\n    #[structopt(\n        long = \"dump-spec\",\n        help = \"[A] Instead of running, dump the specifiers representation to stdout\"\n    )]\n    dumpspec: bool,\n\n    /// Specify this Sec-WebSocket-Protocol: header when connecting\n    #[structopt(long = \"protocol\")]\n    websocket_protocol: Option<String>,\n\n    /// Force this Sec-WebSocket-Protocol: header when accepting a connection\n    #[structopt(long = \"server-protocol\")]\n    websocket_reply_protocol: Option<String>,\n\n    #[structopt(\n        long = \"udp-oneshot\",\n        help = \"[A] udp-listen: replies only one packet per client\"\n    )]\n    udp_oneshot_mode: bool,\n\n    /// [A] Set SO_BROADCAST\n    #[structopt(long=\"udp-broadcast\")]\n    udp_broadcast: bool,\n\n    /// [A] Set IP[V6]_MULTICAST_LOOP\n    #[structopt(long=\"udp-multicast-loop\")]\n    udp_multicast_loop: bool,\n\n    /// [A] Set IP_TTL, also IP_MULTICAST_TTL if applicable\n    #[structopt(long=\"udp-ttl\")]\n    udp_ttl: Option<u32>,\n\n    /// [A] Issue IP[V6]_ADD_MEMBERSHIP for specified multicast address.\n    /// Can be specified multiple times.\n    #[structopt(long=\"udp-multicast\")]\n    udp_join_multicast_addr: Vec<std::net::IpAddr>,\n\n    /// [A] IPv4 address of multicast network interface.\n    /// Has to be either not specified or specified the same number of times as multicast IPv4 addresses. Order matters.\n    #[structopt(long=\"udp-multicast-iface-v4\")]\n    udp_join_multicast_iface_v4: Vec<std::net::Ipv4Addr>,\n\n    /// [A] Index of network interface for IPv6 multicast.\n    /// Has to be either not specified or specified the same number of times as multicast IPv6 addresses. Order matters.\n    #[structopt(long=\"udp-multicast-iface-v6\")]\n    udp_join_multicast_iface_v6: Vec<u32>,\n\n    /// [A] Set SO_REUSEADDR for UDP socket. Listening TCP sockets are always reuseaddr.\n    #[structopt(long=\"udp-reuseaddr\")]\n    udp_reuseaddr: bool,\n\n    #[structopt(\n        long = \"unlink\",\n        help = \"[A] Unlink listening UNIX socket before binding to it\"\n    )]\n    unlink_unix_socket: bool,\n\n    #[structopt(\n        long = \"accept-from-fd\",\n        help = \"[A] Do not call `socket(2)` in UNIX socket listener peer, start with `accept(2)` using specified file descriptor number as argument instead of filename\"\n    )]\n    unix_socket_accept_from_fd: bool,\n\n    #[structopt(\n        long = \"exec-args\",\n        raw(allow_hyphen_values = r#\"true\"#),\n        help = \"[A] Arguments for the `exec:` specifier. Must be the last option, everything after it gets into the exec args list.\"\n    )]\n    exec_args: Vec<String>,\n\n    #[structopt(\n        long = \"ws-c-uri\",\n        help = \"[A] URI to use for ws-c: overlay\",\n        default_value = \"ws://0.0.0.0/\"\n    )]\n    ws_c_uri: String,\n\n    #[structopt(\n        long = \"linemode-strip-newlines\",\n        help = \"[A] Don't include trailing \\\\n or \\\\r\\\\n coming from streams in WebSocket messages\"\n    )]\n    linemode_strip_newlines: bool,\n\n    #[structopt(\n        long = \"--no-line\",\n        help = \"[A] Don't automatically insert line-to-message transformation\"\n    )]\n    no_auto_linemode: bool,\n\n    #[structopt(\n        long = \"origin\",\n        help = \"Add Origin HTTP header to websocket client request\"\n    )]\n    origin: Option<String>,\n\n    #[structopt(\n        long = \"header\",\n        short = \"H\",\n        help = \"Add custom HTTP header to websocket client request. Separate header name and value with a colon and optionally a single space. Can be used multiple times. Note that single -H may eat multiple further arguments, leading to confusing errors. Specify headers at the end or with equal sign like -H='X: y'.\",\n        parse(try_from_str = \"interpret_custom_header\")\n    )]\n    custom_headers: Vec<(String, Vec<u8>)>,\n\n    #[structopt(\n        long = \"server-header\",\n        help = \"Add custom HTTP header to websocket upgrade reply. Separate header name and value with a colon and optionally a single space. Can be used multiple times. Note that single -H may eat multiple further arguments, leading to confusing errors.\",\n        parse(try_from_str = \"interpret_custom_header\")\n    )]\n    custom_reply_headers: Vec<(String, Vec<u8>)>,\n\n    /// Forward specified incoming request header to\n    /// H_* environment variable for `exec:`-like specifiers.\n    #[structopt(\n        long = \"header-to-env\",\n    )]\n    headers_to_env: Vec<String>,\n\n    #[structopt(\n        long = \"websocket-version\",\n        help = \"Override the Sec-WebSocket-Version value\"\n    )]\n    websocket_version: Option<String>,\n\n    #[structopt(\n        long = \"no-close\",\n        short = \"n\",\n        help = \"Don't send Close message to websocket on EOF\"\n    )]\n    websocket_dont_close: bool,\n\n    #[structopt(\n        short = \"1\",\n        long = \"one-message\",\n        help = \"Send and/or receive only one message. Use with --no-close and/or -u/-U.\"\n    )]\n    one_message: bool,\n\n    #[structopt(\n        short = \"s\",\n        long = \"server-mode\",\n        help = \"Simple server mode: specify TCP port or addr:port as single argument\"\n    )]\n    server_mode: bool,\n\n    #[structopt(\n        long = \"no-fixups\",\n        help = \"[A] Don't perform automatic command-line fixups. May destabilize websocat operation. Use --dump-spec without --no-fixups to discover what is being inserted automatically and read the full manual about Websocat internal workings.\"\n    )]\n    no_lints: bool,\n\n    #[structopt(\n        short = \"B\",\n        long = \"buffer-size\",\n        help = \"Maximum message size, in bytes\",\n        default_value = \"65536\"\n    )]\n    buffer_size: usize,\n\n    #[structopt(\n        short = \"v\",\n        parse(from_occurrences),\n        help = \"Increase verbosity level to info or further\"\n    )]\n    verbosity: u8,\n\n    #[structopt(\n        short = \"q\",\n        help = \"Suppress all diagnostic messages, except of startup errors\"\n    )]\n    quiet: bool,\n\n    #[structopt(\n        long = \"queue-len\",\n        help = \"[A] Number of pending queued messages for broadcast reuser\",\n        default_value = \"16\"\n    )]\n    broadcast_queue_len: usize,\n\n    #[structopt(\n        short = \"S\",\n        long = \"strict\",\n        help = \"strict line/message mode: drop too long messages instead of splitting them, drop incomplete lines.\"\n    )]\n    strict_mode: bool,\n\n    #[structopt(\n        short = \"0\",\n        long = \"null-terminated\",\n        help = \"Use \\\\0 instead of \\\\n for linemode\"\n    )]\n    linemode_zero_terminated: bool,\n\n    #[structopt(\n        long = \"restrict-uri\",\n        help = \"When serving a websocket, only accept the given URI, like `/ws`\\nThis liberates other URIs for things like serving static files or proxying.\"\n    )]\n    restrict_uri: Option<String>,\n\n    #[structopt(\n        short = \"F\",\n        long = \"static-file\",\n        help = \"Serve a named static file for non-websocket connections.\\nArgument syntax: <URI>:<Content-Type>:<file-path>\\nArgument example: /index.html:text/html:index.html\\nDirectories are not and will not be supported for security reasons.\\nCan be specified multiple times. Recommended to specify them at the end or with equal sign like `-F=...`, otherwise this option may eat positional arguments\",\n        parse(try_from_str = \"interpret_static_file\")\n    )]\n    serve_static_files: Vec<StaticFile>,\n\n    #[structopt(\n        short = \"e\",\n        long = \"set-environment\",\n        help = \"Set WEBSOCAT_* environment variables when doing exec:/cmd:/sh-c:\\nCurrently it's WEBSOCAT_URI and WEBSOCAT_CLIENT for\\nrequest URI and client address (if TCP)\\nBeware of ShellShock or similar security problems.\"\n    )]\n    exec_set_env: bool,\n\n    #[structopt(\n        long = \"reuser-send-zero-msg-on-disconnect\",\n        help = \"[A] Make reuse-raw: send a zero-length message to the peer when some clients disconnects.\"\n    )]\n    reuser_send_zero_msg_on_disconnect: bool,\n\n    #[structopt(\n        long = \"exec-sighup-on-zero-msg\",\n        help = \"[A] Make exec: or sh-c: or cmd: send SIGHUP on UNIX when facing incoming zero-length message.\"\n    )]\n    process_zero_sighup: bool,\n\n    #[structopt(\n        long = \"exec-sighup-on-stdin-close\",\n        help = \"[A] Make exec: or sh-c: or cmd: send SIGHUP on UNIX when input is closed.\"\n    )]\n    process_exit_sighup: bool,\n\n    #[structopt(\n        long = \"exec-exit-on-disconnect\",\n        help = \"[A] Make exec: or sh-c: or cmd: immediately exit when connection is closed, don't wait for termination.\"\n    )]\n    process_exit_on_disconnect: bool,\n\n    #[structopt(\n        long = \"jsonrpc\",\n        help = \"Format messages you type as JSON RPC 2.0 method calls. First word becomes method name, the rest becomes parameters, possibly automatically wrapped in [].\"\n    )]\n    jsonrpc: bool,\n\n    #[structopt(\n        long = \"socks5-destination\",\n        help = \"[A] Examples: 1.2.3.4:5678  2600:::80  hostname:5678\",\n        parse(try_from_str = \"interpret_socks_destination\")\n    )]\n    socks_destination: Option<SocksSocketAddr>,\n\n    #[structopt(\n        long = \"socks5\",\n        help = \"Use specified address:port as a SOCKS5 proxy. Example: --socks5 127.0.0.1:9050\"\n    )]\n    auto_socks5: Option<SocketAddr>,\n\n    #[structopt(\n        long = \"socks5-bind-script\",\n        help = \"[A] Execute specified script in `socks5-bind:` mode when remote port number becomes known.\",\n        parse(from_os_str)\n    )]\n    socks5_bind_script: Option<OsString>,\n\n    #[structopt(\n        long = \"socks5-user-pass\",\n        help = \"[A] Specify username:password for SOCKS5 proxy. If not specified, the default is to use no authentication.\"\n    )]\n    socks5_user_pass: Option<String>,\n\n    #[structopt(\n        long = \"tls-domain\",\n        alias = \"ssl-domain\",\n        help = \"[A] Specify domain for SNI or certificate verification when using tls-connect: overlay\"\n    )]\n    tls_domain: Option<String>,\n\n    #[cfg(feature = \"ssl\")]\n    #[structopt(\n        long = \"pkcs12-der\",\n        help = \"Pkcs12 archive needed to accept SSL connections, certificate and key.\\nA command to output it: openssl pkcs12 -export -out output.pkcs12 -inkey key.pem -in cert.pem\\nUse with -s (--server-mode) option or with manually specified TLS overlays.\\nSee moreexamples.md for more info.\",\n        parse(try_from_os_str = \"websocat::ssl_peer::interpret_pkcs12\")\n    )]\n    pkcs12_der: Option<Vec<u8>>,\n\n    #[cfg(feature = \"ssl\")]\n    #[structopt(\n        long = \"pkcs12-passwd\",\n        help = \"Password for --pkcs12-der pkcs12 archive. Required on Mac.\"\n    )]\n    pkcs12_passwd: Option<String>,\n\n    #[cfg(feature = \"ssl\")]\n    #[structopt(\n        long = \"client-pkcs12-der\",\n        help = \"[A] Client identity TLS certificate\",\n        parse(try_from_os_str = \"websocat::ssl_peer::interpret_pkcs12\")\n    )]\n    client_pkcs12_der: Option<Vec<u8>>,\n\n    #[cfg(feature = \"ssl\")]\n    #[structopt(\n        long = \"client-pkcs12-passwd\",\n        help = \"[A] Password for --client-pkcs12-der pkcs12 archive. Required on Mac.\"\n    )]\n    client_pkcs12_passwd: Option<String>,\n\n    #[cfg(feature = \"ssl\")]\n    #[structopt(\n        long = \"insecure\",\n        short = \"k\",\n        help = \"Accept invalid certificates and hostnames while connecting to TLS\"\n    )]\n    tls_insecure: bool,\n\n    /// Maximum number of simultaneous connections for listening mode\n    #[structopt(long = \"conncap\")]\n    max_parallel_conns: Option<usize>,\n\n    /// Send WebSocket pings each this number of seconds\n    #[structopt(long = \"ping-interval\")]\n    ws_ping_interval: Option<u64>,\n\n    /// Drop WebSocket connection if Pong message not received for this number of seconds\n    #[structopt(long = \"ping-timeout\")]\n    ws_ping_timeout: Option<u64>,\n    \n    /// [A] Just a Sec-WebSocket-Key value without running main Websocat\n    #[structopt(long = \"just-generate-key\")]\n    just_generate_key: bool,\n\n    /// [A] Just a Sec-WebSocket-Accept value based on supplied\n    /// Sec-WebSocket-Key value without running main Websocat\n    #[structopt(long = \"just-generate-accept\")]\n    just_generate_accept: Option<String>,\n\n    /// [A] URI to use for `http-request:` specifier\n    #[structopt(long = \"request-uri\")]\n    request_uri: Option<http::Uri>,\n\n    /// [A] Method to use for `http-request:` specifier\n    #[structopt(long = \"request-method\", short=\"X\")]\n    request_method: Option<http::Method>,\n\n    /// [A] Specify HTTP request headers for `http-request:` specifier.\n    #[structopt(\n        long = \"request-header\", \n        parse(try_from_str = \"interpret_custom_header2\"),\n    )]\n    request_headers: Vec<(http::header::HeaderName, http::header::HeaderValue)>,\n\n    /// [A] Don't exit when encountered a zero message.\n    /// Zero messages are used internally in Websocat,\n    /// so it may fail to close connection at all.\n    #[structopt(long = \"no-exit-on-zeromsg\")]\n    no_exit_on_zeromsg: bool,\n\n    /// [A] Silently drop incoming zero-length WebSocket messages.\n    /// They may cause connection close due to\n    /// usage of zero-len message as EOF flag inside Websocat.\n    #[structopt(long = \"websocket-ignore-zeromsg\")]\n    websocket_ignore_zeromsg: bool,\n    \n    /// Maximum number of messages to copy in one direction.\n    #[structopt(long = \"max-messages\")]\n    max_messages: Option<usize>,\n\n    /// Maximum number of messages to copy in the other direction.\n    #[structopt(long = \"max-messages-rev\")]\n    max_messages_rev: Option<usize>,\n\n    /// [A] Delay before reconnect attempt for `autoreconnect:` overlay.\n    #[structopt(long = \"--autoreconnect-delay-millis\", default_value=\"20\")]\n    autoreconnect_delay_millis: u64,\n\n\n    /// [A] Prepend specified text to each received WebSocket text message.\n    /// Also strip this prefix from outgoing messages, explicitly marking\n    /// them as text even if `--binary` is specified\n    #[structopt(long = \"--text-prefix\")]\n    pub ws_text_prefix: Option<String>,\n    \n    /// [A] Prepend specified text to each received WebSocket binary message.\n    /// Also strip this prefix from outgoing messages, explicitly marking\n    /// them as binary even if `--text` is specified\n    #[structopt(long = \"--binary-prefix\")]\n    pub ws_binary_prefix: Option<String>,\n\n    /// Encode incoming binary WebSocket messages in one-line Base64\n    /// If `--binary-prefix` (see `--help=full`) is set, outgoing WebSocket messages\n    /// that start with the prefix are decoded from base64 prior to sending.\n    #[structopt(long = \"--base64\")]\n    pub ws_binary_base64: bool,\n\n    /// [A] Encode incoming text WebSocket messages in one-line Base64.\n    /// I don't know whether it can be ever useful, but it's for symmetry with `--base64`.\n    #[structopt(long = \"--base64-text\")]\n    pub ws_text_base64: bool,\n\n    /// Close connection with a status code.\n    #[structopt(long = \"--close-status-code\")]\n    pub close_status_code: Option<u16>,\n\n    /// Close connection with a reason message. This option only takes effect if\n    /// --close-status-code option is provided as well.\n    #[structopt(long = \"--close-reason\")]\n    pub close_reason: Option<String>,\n\n    /// [A] On UNIX, set stdin and stdout to nonblocking mode instead of spawning a thread.\n    /// This should improve performance, but may break other programs running on the same console.\n    #[structopt(long = \"--async-stdio\")]\n    pub asyncstdio: bool,\n\n    /// [A] Inhibit using stdin/stdout in a nonblocking way if it is not a tty \n    #[structopt(long = \"--no-async-stdio\")]\n    pub noasyncstdio: bool,\n\n    /// Add `Authorization: Basic` HTTP request header with this base64-encoded parameter.\n    /// Also available as `WEBSOCAT_BASIC_AUTH` environment variable\n    #[structopt(long = \"--basic-auth\")]\n    pub basic_auth: Option<String>,\n\n    /// Add `Authorization: Basic` HTTP request header base64-encoded content of the specified file\n    #[structopt(long = \"--basic-auth-file\")]\n    pub basic_auth_file: Option<PathBuf>,\n\n    /// [A] Wait for reading to finish before closing foreachmsg:'s peer\n    #[structopt(long = \"--foreachmsg-wait-read\")]\n    pub foreachmsg_wait_reads: bool,\n\n    /// [A] Print a line to stdout for each port being listened\n    #[structopt(long = \"--stdout-announce-listening-ports\")]\n    pub announce_listens: bool,\n\n    /// [A] Use monotonic clock for `timestamp:` overlay\n    #[structopt(long = \"--timestamp-monotonic\")]\n     pub timestamp_monotonic: bool,\n\n    /// Print measured round-trip-time to stderr after each received WebSocket pong.\n    #[structopt(long = \"print-ping-rtts\")]\n    pub print_ping_rtts: bool,\n\n    /// [A] Specify encryption/decryption key for `crypto:` specifier. Requires `base64:`, `file:` or `pwd:` prefix.\n    #[cfg(feature = \"crypto_peer\")]\n    #[structopt(long = \"crypto-key\", parse(try_from_str = \"websocat::crypto_peer::interpret_opt\"))]\n    pub crypto_key: Option<[u8; 32]>,\n\n    /// [A] Swap encryption and decryption operations in `crypto:` specifier - encrypt on read, decrypto on write.\n    #[cfg(feature = \"crypto_peer\")]\n    #[structopt(long = \"crypto-reverse\")]\n    pub crypto_reverse: bool,\n\n    /// Expose Prometheus metrics on specified IP address and port in addition to running usual Websocat session\n    #[cfg(feature = \"prometheus_peer\")]\n    #[structopt(long = \"prometheus\")]\n    pub prometheus: Option<SocketAddr>,\n\n    /// [A] Override the byte which byte_to_exit_on: overlay looks for\n    #[structopt(long = \"byte-to-exit-on\", default_value = \"28\")]\n    byte_to_exit_on: u8,\n\n    /// [A] Maximum size of incoming WebSocket messages (sans of one data frame), to prevent memory overflow\n    #[structopt(long = \"max-ws-message-length\", default_value = \"209715200\")]\n    pub max_ws_message_length: usize,\n    /// [A] Maximum size of incoming WebSocket frames, to prevent memory overflow\n    #[structopt(long = \"max-ws-frame-length\", default_value = \"104857600\")]\n    pub max_ws_frame_length: usize,\n\n    /// Prepend copied data with a specified string. Can be specified multiple times.\n    #[structopt(long = \"preamble\", short=\"p\")]\n    pub preamble: Vec<String>,\n\n    /// Prepend copied data with a specified string (reverse direction). Can be specified multiple times.\n    #[structopt(long = \"preamble-reverse\", short=\"P\")]\n    pub preamble_reverse: Vec<String>,\n\n\n    /// [A] Compress data coming to a WebSocket using deflate method. Affects only binary WebSocket messages.\n    #[structopt(long = \"compress-deflate\")]\n    pub compress_deflate: bool,\n\n    /// [A] Compress data coming to a WebSocket using zlib method. Affects only binary WebSocket messages.\n    #[structopt(long = \"compress-zlib\")]\n    pub compress_zlib: bool,\n\n    /// [A] Compress data coming to a WebSocket using gzip method. Affects only binary WebSocket messages.\n    #[structopt(long = \"compress-gzip\")]\n    pub compress_gzip: bool,\n\n    /// [A] Uncompress data coming from a WebSocket using deflate method. Affects only binary WebSocket messages.\n    #[structopt(long = \"uncompress-deflate\")]\n    pub uncompress_deflate: bool,\n\n    /// [A] Uncompress data coming from a WebSocket using deflate method. Affects only binary WebSocket messages.\n    #[structopt(long = \"uncompress-zlib\")]\n    pub uncompress_zlib: bool,\n\n    /// [A] Uncompress data coming from a WebSocket using deflate method. Affects only binary WebSocket messages.\n    #[structopt(long = \"uncompress-gzip\")]\n    pub uncompress_gzip: bool,\n\n    /// [A] Omit `jsonrpc` field when using `--jsonrpc`, e.g. for Chromium\n    #[structopt(long = \"jsonrpc-omit-jsonrpc\")]\n    pub jsonrpc_omit_jsonrpc: bool,\n\n    /// [A] Stop replying to incoming WebSocket pings after specified number of replies \n    #[structopt(long = \"inhibit-pongs\")]\n    pub inhibit_pongs: Option<usize>,\n\n    /// [A] Stop sending pings after this number of sent pings\n    #[structopt(long = \"max-sent-pings\")]\n    pub max_sent_pings: Option<usize>,\n\n    /// [A] Use this number of length header bytes for `lengthprefixed:` overlay.\n    #[structopt(long = \"--lengthprefixed-nbytes\", default_value = \"4\")]\n    pub lengthprefixed_header_bytes: usize,\n\n    /// [A] Use little-endian framing headers instead of big-endian for `lengthprefixed:` overlay.\n    #[structopt(long = \"--lengthprefixed-little-endian\")]\n    pub lengthprefixed_little_endian: bool,\n\n    /// [A] Only affect one direction of the `lengthprefixed:` overlay, bypass tranformation for the other one.\n    #[structopt(long = \"--lengthprefixed-skip-read-direction\")]\n    pub lengthprefixed_skip_read_direction: bool,\n\n    /// [A] Only affect one direction of the `lengthprefixed:` overlay, bypass tranformation for the other one.\n    #[structopt(long = \"--lengthprefixed-skip-write-direction\")]\n    pub lengthprefixed_skip_write_direction: bool,\n\n    /// Set `User-Agent` request header to this value. Similar to setting it with `-H`.\n    #[structopt(long = \"--ua\")]\n    pub useragent: Option<String>,\n}\n\n// TODO: make it byte-oriented/OsStr?\nfn interpret_custom_header(x: &str) -> Result<(String, Vec<u8>)> {\n    let colon = x.find(':');\n    let colon = if let Some(colon) = colon {\n        colon\n    } else {\n        Err(\"Argument to --header must contain `:` character\")?\n    };\n    let hn = &x[0..colon];\n    let mut hv = &x[colon + 1..];\n    if hv.starts_with(' ') {\n        hv = &x[colon + 2..];\n    }\n    Ok((hn.to_owned(), hv.as_bytes().to_vec()))\n}\n\nfn interpret_custom_header2(x: &str) -> Result<(http::header::HeaderName, http::header::HeaderValue)> {\n    let colon = x.find(':');\n    let colon = if let Some(colon) = colon {\n        colon\n    } else {\n        Err(\"Specified header must contain `:` character\")?\n    };\n    let hn = &x[0..colon];\n    let mut hv = &x[colon + 1..];\n    if hv.starts_with(' ') {\n        hv = &x[colon + 2..];\n    }\n    use std::str::FromStr;\n    let hn = http::header::HeaderName::from_str(hn)?;\n    let hv = http::header::HeaderValue::from_str(hv)?;\n    Ok((hn,hv))\n}\n\nfn interpret_static_file(x: &str) -> Result<StaticFile> {\n    let colon1 = match x.find(':') {\n        Some(x) => x,\n        None => Err(\"Argument to --static-file must contain two colons (`:`)\")?,\n    };\n    let uri = &x[0..colon1];\n    let rest = &x[colon1 + 1..];\n    let colon2 = match rest.find(':') {\n        Some(x) => x,\n        None => Err(\"Argument to --static-file must contain two colons (`:`)\")?,\n    };\n    let ct = &rest[0..colon2];\n    let fp = &rest[colon2 + 1..];\n    if uri.is_empty() || ct.is_empty() || fp.is_empty() {\n        Err(\"Empty URI, content-type or path in --static-file parameter\")?\n    }\n    Ok(StaticFile {\n        uri: uri.to_string(),\n        content_type: ct.to_string(),\n        file: fp.into(),\n    })\n}\n\nfn interpret_socks_destination(x: &str) -> Result<SocksSocketAddr> {\n    let colon = x.rfind(':');\n    let colon = if let Some(colon) = colon {\n        colon\n    } else {\n        Err(\"Argument to --socks5-destination must contain a `:` character\")?\n    };\n    let h = &x[0..colon];\n    let p = &x[colon + 1..];\n\n    let port: u16 = p.parse()?;\n\n    let host = if let Ok(ip4) = h.parse() {\n        SocksHostAddr::Ip(IpAddr::V4(ip4))\n    } else if let Ok(ip6) = h.parse() {\n        SocksHostAddr::Ip(IpAddr::V6(ip6))\n    } else {\n        SocksHostAddr::Name(h.to_string())\n    };\n\n    Ok(SocksSocketAddr { host, port })\n}\n\npub mod help;\n\n// Based on https://github.com/rust-clique/clap-verbosity-flag/blob/master/src/lib.rs\nmod logging {\n\n    extern crate env_logger;\n    extern crate log;\n\n    use self::env_logger::Builder as LoggerBuilder;\n    use self::log::Level;\n\n    pub fn setup_env_logger(ll: u8) -> Result<(), Box<dyn (::std::error::Error)>> {\n        if ::std::env::var(\"RUST_LOG\").is_ok() {\n            if ll > 0 {\n                eprintln!(\"websocat: RUST_LOG environment variable overrides any -v\");\n            }\n            env_logger::init();\n            return Ok(());\n        }\n\n        let lf = match ll {\n            //0 => Level::Error,\n            0 => Level::Warn,\n            1 => Level::Info,\n            2 => Level::Debug,\n            _ => Level::Trace,\n        }\n        .to_level_filter();\n\n        LoggerBuilder::new()\n            .filter(Some(\"websocat\"), lf)\n            .filter(None, Level::Warn.to_level_filter())\n            .try_init()?;\n        Ok(())\n    }\n\n}\n\nfn run() -> Result<()> {\n    if std::env::args().nth(1).unwrap_or_default() == \"--long-help\" {\n        help::longhelp();\n        return Ok(());\n    }\n    if [\"-?\", \"-h\", \"--help\"].contains(&std::env::args().nth(1).unwrap_or_default().as_str()) {\n        help::shorthelp();\n        return Ok(());\n    }\n    let mut logging_already_set = false;\n    if std::env::var(\"WEBSOCAT_EARLY_LOG\").is_ok() {\n        logging::setup_env_logger(0)?;\n        logging_already_set = true;\n    }\n\n    let mut cmd = Opt::from_args();\n\n    let mut quiet = cmd.quiet;\n\n    if let Some(h) = cmd.help {\n        if &h == \"long\" || &h == \"full\" || &h == \"all\" {\n            help::longhelp();\n            return Ok(());\n        } else if &h == \"doc\" {\n            help::dochelp();\n            return Ok(());\n        }\n\n        help::shorthelp();\n        return Ok(());\n    }\n\n    if cmd.just_generate_key {\n        println!(\"{}\", websocket_base::header::WebSocketKey::new().serialize());\n        return Ok(());\n    }\n\n    if let Some(key) = cmd.just_generate_accept {\n        use std::str::FromStr;\n        let k = websocket_base::header::WebSocketKey::from_str(&key)?;\n        println!(\"{}\", websocket_base::header::WebSocketAccept::new(&k).serialize());\n        return Ok(());\n    }\n\n    let mut recommend_explicit_text_or_bin = false;\n\n    if cmd.websocket_binary_mode && cmd.websocket_text_mode {\n        Err(\"--binary and --text are mutually exclusive\")?;\n    }\n    if !cmd.websocket_binary_mode && !cmd.websocket_text_mode {\n        cmd.websocket_text_mode = true;\n        recommend_explicit_text_or_bin = true;\n    }\n\n    if cmd.noasyncstdio && cmd.asyncstdio {\n        Err(\"--no-async-stdio and --async-stdio are not meaningful together\")?;\n    }\n\n    if !cmd.noasyncstdio && atty::isnt(atty::Stream::Stdin) && atty::isnt(atty::Stream::Stdout) {\n        cmd.asyncstdio = true;\n    }\n\n    //if !cmd.serve_static_files.is_empty() && cmd.restrict_uri.is_none() {\n    //    Err(\"Specify --static-file is not supported without --restrict-uri\")?\n    //}\n\n    if false\n    //    || cmd.oneshot\n    {\n        Err(\"This mode is not implemented\")?\n    }\n\n    #[cfg(feature = \"openssl-probe\")]\n    {\n        openssl_probe::init_ssl_cert_env_vars();\n    }\n\n    let mut opts: Options = Default::default();\n    {\n        macro_rules! opts {\n            ($($o:ident)*) => {{\n                $(opts.$o = cmd.$o;)*\n            }};\n        }\n        opts!(\n            websocket_text_mode\n            websocket_protocol\n            websocket_reply_protocol\n            udp_oneshot_mode\n            udp_broadcast\n            udp_multicast_loop\n            udp_ttl\n            udp_join_multicast_addr\n            udp_join_multicast_iface_v4\n            udp_join_multicast_iface_v6\n            udp_reuseaddr\n            unidirectional\n            unidirectional_reverse\n            exit_on_eof\n            oneshot\n            unlink_unix_socket\n            unix_socket_accept_from_fd\n            exec_args\n            ws_c_uri\n            linemode_strip_newlines\n            origin\n            custom_headers\n            custom_reply_headers\n            headers_to_env\n            websocket_version\n            websocket_dont_close\n            one_message\n            no_auto_linemode\n            buffer_size\n            linemode_zero_terminated\n            broadcast_queue_len\n            restrict_uri\n            serve_static_files\n            exec_set_env\n            reuser_send_zero_msg_on_disconnect\n            process_zero_sighup\n            process_exit_sighup\n            process_exit_on_disconnect\n            socks_destination\n            auto_socks5\n            socks5_bind_script\n            socks5_user_pass\n            tls_domain\n            max_parallel_conns\n            ws_ping_interval\n            ws_ping_timeout\n            request_uri\n            request_method\n            request_headers\n            websocket_ignore_zeromsg\n            no_exit_on_zeromsg\n            max_messages\n            max_messages_rev\n            autoreconnect_delay_millis\n            ws_text_prefix\n            ws_binary_prefix\n            ws_binary_base64\n            ws_text_base64\n            close_status_code\n            close_reason\n            asyncstdio\n            foreachmsg_wait_reads\n            announce_listens\n            timestamp_monotonic\n            print_ping_rtts\n            byte_to_exit_on\n            max_ws_message_length\n            max_ws_frame_length\n            preamble\n            preamble_reverse\n            compress_deflate\n            compress_zlib\n            compress_gzip\n            uncompress_deflate\n            uncompress_zlib\n            uncompress_gzip\n            jsonrpc_omit_jsonrpc\n            inhibit_pongs\n            max_sent_pings\n            lengthprefixed_header_bytes\n            lengthprefixed_little_endian\n            lengthprefixed_skip_read_direction\n            lengthprefixed_skip_write_direction\n        );\n        #[cfg(feature = \"ssl\")]\n        {\n            opts! {\n                pkcs12_der\n                pkcs12_passwd\n                client_pkcs12_der\n                client_pkcs12_passwd\n                tls_insecure\n            }\n        }\n        #[cfg(feature = \"crypto_peer\")]\n        {\n            opts! {\n                crypto_key\n                crypto_reverse\n            }\n        }\n        #[cfg(feature = \"prometheus_peer\")]\n        {\n            opts! {\n                prometheus\n            }\n        }\n    };\n\n    if let Some(x) = cmd.useragent {\n        opts.custom_headers.push((\"User-Agent\".to_owned(), x.as_bytes().to_vec()));\n        opts.request_headers.push((http::header::USER_AGENT, http::header::HeaderValue::from_bytes(x.as_bytes()).unwrap()));\n    }\n\n    let mut basic_auth_content : Option<String> = None;\n\n    if let Some(ba) = cmd.basic_auth {\n        basic_auth_content = Some(ba);\n    }\n    if let Ok(ba) = std::env::var(\"WEBSOCAT_BASIC_AUTH\") {\n        if basic_auth_content.is_some() {\n            return Err(\"Multiple request basic auth options specified simultaneously\".into());\n        }\n        basic_auth_content = Some(ba);\n    }\n    if let Some(baf) = cmd.basic_auth_file {\n        if basic_auth_content.is_some() {\n            return Err(\"Multiple request basic auth options specified simultaneously\".into());\n        }\n        let x = std::fs::read_to_string(&baf).inspect_err(|_|{error!(\"Failed to read `{:?}`\", baf);})?;\n        basic_auth_content = Some(x.trim().to_owned());\n    }\n\n    if let Some(ba) = basic_auth_content {\n        let x = base64::encode(&ba);\n        let q = format!(\"Basic {}\", x);\n        opts.custom_headers.push((\"Authorization\".to_owned(), q.as_bytes().to_vec()));\n        opts.request_headers.push((http::header::AUTHORIZATION, http::header::HeaderValue::from_bytes(q.as_bytes()).unwrap()));\n    }\n\n    let (s1, s2): (String, String) = match (cmd.addr1, cmd.addr2) {\n        (None, None) => {\n            for x in std::env::args() {\n                if x == \"-p\" || x == \"-P\" || x == \"--preamble\" || x == \"--preamble-reverse\" {\n                    eprintln!(\"Warning: all dashless arguments after -p or -P are considered part of the preamble. You may want to move -p/-P to the end of the command line.\")\n                }\n            }\n            return Err(\"No URL specified. Use `websocat --help` to show the help message.\")?;\n        }\n        (Some(cmds1), Some(cmds2)) => {\n            // Advanced mode\n            if cmd.jsonrpc {\n                Err(\"--jsonrpc option is only for simple (single-argument) mode.\\nUse `jsonrpc:` specifier manually if you want it in advanced mode.\")?\n            }\n            if cmd.server_mode {\n                Err(\"--server and two positional arguments are incompatible.\\nBuild server command line without -s option, but with `listen` address types\")?\n            }\n            (cmds1, cmds2)\n        }\n        (Some(cmds1), None) => {\n            // Easy mode\n            recommend_explicit_text_or_bin = false;\n            if cmd.server_mode {\n                #[allow(unused)]\n                let mut secure = false;\n                #[cfg(feature = \"ssl\")]\n                {\n                    if opts.pkcs12_der.is_some() {\n                        secure = true;\n                    }\n                }\n\n                opts.exit_on_eof = true;\n                if !secure {\n                    if cmds1.contains(':') {\n                        if !quiet {\n                            eprintln!(\"Listening on ws://{}/\", cmds1);\n                        }\n                        (format!(\"ws-l:{}\", cmds1), \"-\".to_string())\n                    } else {\n                        if !quiet {\n                            eprintln!(\"Listening on ws://127.0.0.1:{}/\", cmds1);\n                        }\n                        (format!(\"ws-l:127.0.0.1:{}\", cmds1), \"-\".to_string())\n                    }\n                } else if cmds1.contains(':') {\n                    if !quiet {\n                        eprintln!(\"Listening on wss://{}/\", cmds1);\n                    }\n                    (format!(\"wss-l:{}\", cmds1), \"-\".to_string())\n                } else {\n                    if !quiet {\n                        eprintln!(\"Listening on wss://127.0.0.1:{}/\", cmds1);\n                    }\n                    (format!(\"wss-l:127.0.0.1:{}\", cmds1), \"-\".to_string())\n                }\n            } else {\n                if !(cmds1.starts_with(\"ws://\") || cmds1.starts_with(\"wss://\")) {\n                    if !quiet {\n                        eprintln!(\"Specify ws:// or wss:// URI to connect to a websocket\");\n                    }\n                    Err(\"Invalid command-line parameters\")?;\n                }\n                (\"-\".to_string(), cmds1)\n            }\n        }\n        (None, Some(_)) => unreachable!(),\n    };\n\n    if opts.websocket_text_mode {\n        opts.read_debt_handling = websocat::readdebt::DebtHandling::Warn;\n    }\n    if cmd.strict_mode {\n        opts.read_debt_handling = websocat::readdebt::DebtHandling::DropMessage;\n        opts.linemode_strict = true;\n    }\n\n    debug!(\"Done first phase of interpreting options.\");\n    let websocat1 = WebsocatConfiguration1 {\n        opts,\n        addr1: s1,\n        addr2: s2,\n    };\n    let mut websocat2 = websocat1.parse1()?;\n    debug!(\"Done second phase of interpreting options.\");\n\n    if websocat2.inetd_mode() {\n        quiet = true;\n    }\n\n    if !quiet && recommend_explicit_text_or_bin {\n        eprintln!(\"websocat: It is recommended to either set --binary or --text explicitly\");\n    }\n    if !quiet && !logging_already_set {\n        logging::setup_env_logger(cmd.verbosity)?;\n    }\n\n    if !cmd.no_lints {\n        websocat2.lint_and_fixup(Box::new(move |e: &str| {\n            if !quiet {\n                eprintln!(\"websocat: {}\", e);\n            }\n        }))?;\n    }\n    if cmd.jsonrpc {\n        websocat2\n            .s1\n            .overlays\n            .insert(0, websocat::specifier::SpecifierNode{cls: ::std::rc::Rc::new(websocat::jsonrpc_peer::JsonRpcClass)});\n    }\n    debug!(\"Done third phase of interpreting options.\");\n    let websocat = websocat2.parse2()?;\n    debug!(\"Done fourth phase of interpreting options.\");\n\n    if cmd.dumpspec {\n        println!(\"{:?}\", websocat.s1);\n        println!(\"{:?}\", websocat.s2);\n        println!(\"{:?}\", websocat.opts);\n        return Ok(());\n    }\n\n    let mut core = tokio::runtime::current_thread::Runtime::new()?;\n\n    let error_handler = std::rc::Rc::new(move |e| {\n        if !quiet {\n            eprintln!(\"websocat: {}\", e);\n        }\n    });\n    let prog = websocat.serve(error_handler);\n    debug!(\"Preparation done. Now actually starting.\");\n    core.block_on(prog)\n        .map_err(|()| \"error running\".to_string())?;\n    Ok(())\n}\n\nfn main() {\n    let r = run();\n\n    if let Err(e) = r {\n        eprintln!(\"websocat: {}\", e);\n        ::std::process::exit(1);\n    }\n}\n"
  },
  {
    "path": "src/mirror_peer.rs",
    "content": "use super::{BoxedNewPeerFuture, Peer};\n\nuse super::{brokenpipe, io_other_error, wouldblock};\nuse futures;\nuse futures::sink::Sink;\nuse futures::stream::Stream;\nuse std;\nuse std::io::Result as IoResult;\nuse std::io::{Read, Write};\n\nuse futures::Async::{NotReady, Ready};\nuse std::rc::Rc;\n\nuse futures::sync::mpsc;\n\nuse tokio_io::{AsyncRead, AsyncWrite};\n\nuse super::readdebt::{DebtHandling, ProcessMessageResult, ReadDebt, ZeroMessagesHandling};\nuse super::{once, ConstructParams, PeerConstructor, Specifier};\n\n#[derive(Debug, Clone)]\npub struct Mirror;\nimpl Specifier for Mirror {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        once(get_mirror_peer(cp.program_options.read_debt_handling))\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\nspecifier_class!(\n    name = MirrorClass,\n    target = Mirror,\n    prefixes = [\"mirror:\"],\n    arg_handling = noarg,\n    overlay = false,\n    MessageOriented,\n    SingleConnect,\n    help = r#\"\nSimply copy output to input. No arguments needed.\n\nExample: emulate echo.websocket.org\n\n    websocat -t ws-l:127.0.0.1:1234 mirror:\n\"#\n);\n// TODO: doc example, mention echo.websocket.org\n\n#[derive(Clone)]\npub struct LiteralReply(pub Vec<u8>);\nimpl Specifier for LiteralReply {\n    fn construct(&self, _: ConstructParams) -> PeerConstructor {\n        once(get_literal_reply_peer(self.0.clone()))\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\nimpl std::fmt::Debug for LiteralReply {\n    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {\n        write!(f, \"LiteralReply\")\n    }\n}\nspecifier_class!(\n    name = LiteralReplyClass,\n    target = LiteralReply,\n    prefixes = [\"literalreply:\"],\n    arg_handling = into,\n    overlay = false,\n    MessageOriented,\n    SingleConnect,\n    help = r#\"\nReply with a specified string for each input packet.\n\nExample:\n\n    websocat ws-l:0.0.0.0:1234 literalreply:'{\"status\":\"OK\"}'\n\"#\n);\n\nstruct MirrorWrite(mpsc::Sender<Vec<u8>>);\nstruct MirrorRead {\n    debt: ReadDebt,\n    ch: mpsc::Receiver<Vec<u8>>,\n}\n\npub fn get_mirror_peer(debt_handling: DebtHandling) -> BoxedNewPeerFuture {\n    let (sender, receiver) = mpsc::channel::<Vec<u8>>(0);\n    let r = MirrorRead {\n        debt: ReadDebt(Default::default(), debt_handling, ZeroMessagesHandling::Deliver),\n        ch: receiver,\n    };\n    let w = MirrorWrite(sender);\n    let p = Peer::new(r, w, None);\n    Box::new(futures::future::ok(p)) as BoxedNewPeerFuture\n}\npub fn get_literal_reply_peer(content: Vec<u8>) -> BoxedNewPeerFuture {\n    let (sender, receiver) = mpsc::channel::<()>(0);\n    let r = LiteralReplyRead {\n        debt: ReadDebt(Default::default(), DebtHandling::Silent, ZeroMessagesHandling::Deliver),\n        ch: receiver,\n        content,\n    };\n    let w = LiteralReplyHandle(sender);\n    let p = Peer::new(r, w, None);\n    Box::new(futures::future::ok(p)) as BoxedNewPeerFuture\n}\n\nimpl AsyncRead for MirrorRead {}\n\nimpl Read for MirrorRead {\n    fn read(&mut self, buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {\n        if let Some(ret) = self.debt.check_debt(buf) {\n            return ret;\n        }\n        loop {\n            let r = self.ch.poll();\n            return match r {\n                Ok(Ready(Some(x))) => match self.debt.process_message(buf, x.as_slice()) {\n                    ProcessMessageResult::Return(x) => x,\n                    ProcessMessageResult::Recurse => continue,\n                },\n                Ok(Ready(None)) => brokenpipe(),\n                Ok(NotReady) => wouldblock(),\n                Err(_) => brokenpipe(),\n            };\n        }\n    }\n}\n\nimpl AsyncWrite for MirrorWrite {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        Ok(Ready(()))\n    }\n}\n\nimpl Write for MirrorWrite {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        let om = buf.to_vec();\n        match self.0.start_send(om).map_err(io_other_error)? {\n            futures::AsyncSink::NotReady(_) => wouldblock(),\n            futures::AsyncSink::Ready => Ok(buf.len()),\n        }\n    }\n    fn flush(&mut self) -> IoResult<()> {\n        match self.0.poll_complete().map_err(io_other_error)? {\n            NotReady => wouldblock(),\n            Ready(()) => Ok(()),\n        }\n    }\n}\n\nimpl Drop for MirrorWrite {\n    fn drop(&mut self) {\n        info!(\"MirrorWrite drop\");\n        let _ = self.0.start_send(vec![]).map_err(|_| ()).map(|_| ());\n        let _ = self.0.poll_complete().map_err(|_| ()).map(|_| ());\n    }\n}\n\nstruct LiteralReplyHandle(mpsc::Sender<()>);\nstruct LiteralReplyRead {\n    debt: ReadDebt,\n    ch: mpsc::Receiver<()>,\n    content: Vec<u8>,\n}\n\nimpl AsyncWrite for LiteralReplyHandle {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        Ok(Ready(()))\n    }\n}\n\nimpl Write for LiteralReplyHandle {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        match self.0.start_send(()).map_err(io_other_error)? {\n            futures::AsyncSink::NotReady(_) => wouldblock(),\n            futures::AsyncSink::Ready => Ok(buf.len()),\n        }\n    }\n    fn flush(&mut self) -> IoResult<()> {\n        match self.0.poll_complete().map_err(io_other_error)? {\n            NotReady => wouldblock(),\n            Ready(()) => Ok(()),\n        }\n    }\n}\nimpl AsyncRead for LiteralReplyRead {}\nimpl Read for LiteralReplyRead {\n    fn read(&mut self, buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {\n        if let Some(ret) = self.debt.check_debt(buf) {\n            return ret;\n        }\n        loop {\n            let r = self.ch.poll();\n            return match r {\n                Ok(Ready(Some(()))) => match self.debt.process_message(buf, &self.content) {\n                    ProcessMessageResult::Return(x) => x,\n                    ProcessMessageResult::Recurse => continue,\n                },\n                Ok(Ready(None)) => brokenpipe(),\n                Ok(NotReady) => wouldblock(),\n                Err(_) => brokenpipe(),\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "src/my_copy.rs",
    "content": "use std::io;\n\nuse futures::{Future, Poll};\n\nuse crate::{AsyncRead, AsyncWrite};\n\n#[derive(Debug, Copy, Clone)]\npub struct CopyOptions {\n    pub stop_on_reader_zero_read: bool,\n    pub once: bool,\n    pub buffer_size: usize,\n    /// Because of -u or -U\n    pub skip: bool,\n    pub max_ops: Option<usize>,\n}\n\n/// A future which will copy all data from a reader into a writer.\n/// A modified version of tokio_io::copy::Copy.\n///\n/// Created by the [`copy`] function, this future will resolve to the number of\n/// bytes copied or an error if one happens.\n///\n/// [`copy`]: fn.copy.html\n#[derive(Debug)]\npub struct Copy<R, W> {\n    reader: Option<R>,\n    read_done: bool,\n    writer: Option<W>,\n    pos: usize,\n    cap: usize,\n    amt: u64,\n    buf: Box<[u8]>,\n    opts: CopyOptions,\n    read_occurred: bool,\n    remaining_ops: Option<usize>,\n    preamble: Vec<String>,\n    preamble_index: usize,\n}\n\n/// Creates a future which represents copying all the bytes from one object to\n/// another.\n///\n/// The returned future will copy all the bytes read from `reader` into the\n/// `writer` specified. This future will only complete once the `reader` has hit\n/// EOF and all bytes have been written to and flushed from the `writer`\n/// provided.\n///\n/// On success the number of bytes is returned and the `reader` and `writer` are\n/// consumed. On error the error is returned and the I/O objects are consumed as\n/// well.\n///\n/// Unlike original tokio_io::copy::copy, it does not always stop on zero length reads\n/// , handles BrokenPipe error kind as EOF and flushes after every write\npub fn copy<R, W>(reader: R, writer: W, opts: CopyOptions, preamble: Vec<String>) -> Copy<R, W>\nwhere\n    R: AsyncRead,\n    W: AsyncWrite,\n{\n    Copy {\n        reader: Some(reader),\n        read_done: false,\n        writer: Some(writer),\n        amt: 0,\n        pos: 0,\n        cap: 0,\n        // TODO - de-hardcode buffer size\n        buf: vec![0; opts.buffer_size].into_boxed_slice(),\n        opts,\n        read_occurred: false,\n        remaining_ops: opts.max_ops,\n        preamble,\n        preamble_index: 0,\n    }\n}\n\nimpl<R, W> Future for Copy<R, W>\nwhere\n    R: AsyncRead,\n    W: AsyncWrite,\n{\n    type Item = (u64, R, W);\n    type Error = io::Error;\n\n    fn poll(&mut self) -> Poll<(u64, R, W), io::Error> {\n        loop {\n            // First ensure that preamble messages got drained\n            if self.preamble_index < self.preamble.len() {\n                let writer = self.writer.as_mut().unwrap();\n                let i = try_nb!(writer.write(self.preamble[self.preamble_index].as_bytes()));\n                if i == 0 {\n                    return Err(io::Error::new(\n                        io::ErrorKind::WriteZero,\n                        \"write zero byte into writer\",\n                    ));\n                } else {\n                    trace!(\"preamble write {}\", i);\n                    if i != self.preamble[self.preamble_index].len() {\n                        warn!(\"Short write of a preamble. Expect trimmed data.\")\n                    }\n                    self.preamble_index += 1;\n                }\n                try_nb!(writer.flush());\n                continue;\n            }\n\n            // Handle inhibiting options only after preamble is drained.\n            if self.opts.skip {\n                debug!(\"copy skipped\");\n                let reader = self.reader.take().unwrap();\n                let writer = self.writer.take().unwrap();\n                return Ok((0, reader, writer).into());\n            }\n\n            // If our buffer is empty, then we need to read some data to\n            // continue.\n            trace!(\"poll\");\n            if self.pos == self.cap && !self.read_done {\n                if self.read_occurred && self.opts.once {\n                    debug!(\"Once mode requested, so aborting copy\");\n                    self.read_done = true;\n                    continue;\n                }\n                if self.remaining_ops == Some(0) {\n                    debug!(\"Maximum number of messages to copy exceed, so aborting copy\");\n                    self.read_done = true;\n                    continue;\n                }\n                let reader = self.reader.as_mut().unwrap();\n                let rr = reader.read(&mut self.buf);\n                if let Err(ref e) = rr {\n                    if e.kind() == io::ErrorKind::BrokenPipe {\n                        debug!(\"BrokenPipe: read_done\");\n                        self.read_done = true;\n                        continue;\n                    }\n                }\n                let n = try_nb!(rr);\n                trace!(\"read {}\", n);\n                if let Some(ref mut maxops) = self.remaining_ops {\n                    *maxops -= 1;\n                }\n                if n == 0 {\n                    debug!(\"zero len\");\n                    if self.opts.stop_on_reader_zero_read {\n                        debug!(\"read_done\");\n                        self.read_done = true;\n                    }\n                    continue;\n                } else {\n                    self.pos = 0;\n                    self.cap = n;\n                    self.read_occurred = true;\n                }\n            }\n\n            // If our buffer has some data, let's write it out!\n            while self.pos < self.cap {\n                let writer = self.writer.as_mut().unwrap();\n                let i = try_nb!(writer.write(&self.buf[self.pos..self.cap]));\n                if i == 0 {\n                    return Err(io::Error::new(\n                        io::ErrorKind::WriteZero,\n                        \"write zero byte into writer\",\n                    ));\n                } else {\n                    trace!(\"write {}\", i);\n                    self.pos += i;\n                    self.amt += i as u64;\n                }\n                try_nb!(writer.flush());\n            }\n\n            // If we've written al the data and we've seen EOF, flush out the\n            // data and finish the transfer.\n            // done with the entire transfer.\n            if self.pos == self.cap && self.read_done {\n                try_nb!(self.writer.as_mut().unwrap().flush());\n                let reader = self.reader.take().unwrap();\n                let writer = self.writer.take().unwrap();\n                debug!(\"done\");\n                return Ok((self.amt, reader, writer).into());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/net_peer.rs",
    "content": "extern crate net2;\n\nuse futures;\nuse futures::future::Future;\nuse futures::stream::Stream;\nuse futures::unsync::oneshot::{channel, Receiver, Sender};\nuse std;\nuse std::io::Result as IoResult;\nuse std::io::{Read, Write};\nuse std::net::SocketAddr;\nuse tokio_io::{AsyncRead, AsyncWrite};\n\nuse std::cell::RefCell;\nuse std::rc::Rc;\n\nuse tokio_tcp::{TcpListener, TcpStream};\nuse tokio_udp::UdpSocket;\n\nuse super::L2rUser;\nuse super::{box_up_err, peer_err_s, wouldblock, BoxedNewPeerFuture, BoxedNewPeerStream, Peer};\nuse super::{multi, once, ConstructParams, Options, PeerConstructor, Specifier};\n\n#[derive(Debug, Clone)]\npub struct TcpConnect(pub Vec<SocketAddr>);\nimpl Specifier for TcpConnect {\n    fn construct(&self, _: ConstructParams) -> PeerConstructor {\n        // FIXME: connect to multiple things\n        once(tcp_connect_peer(&self.0[..]))\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec );\n}\nspecifier_class!(\n    name = TcpConnectClass,\n    target = TcpConnect,\n    prefixes = [\"tcp:\", \"tcp-connect:\", \"connect-tcp:\", \"tcp-c:\", \"c-tcp:\"],\n    arg_handling = parseresolve,\n    overlay = false,\n    StreamOriented,\n    SingleConnect,\n    help = r#\"\nConnect to specified TCP host and port. Argument is a socket address.\n\nExample: simulate netcat netcat\n\n    websocat - tcp:127.0.0.1:22\n\nExample: redirect websocket connections to local SSH server over IPv6\n\n    websocat ws-l:0.0.0.0:8084 tcp:[::1]:22\n\"#\n);\n\n#[derive(Debug, Clone)]\npub struct TcpListen(pub SocketAddr);\nimpl Specifier for TcpListen {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        multi(tcp_listen_peer(&self.0, p.left_to_right, p.program_options.announce_listens))\n    }\n    specifier_boilerplate!(noglobalstate multiconnect no_subspec );\n}\nspecifier_class!(\n    name = TcpListenClass,\n    target = TcpListen,\n    prefixes = [\"tcp-listen:\", \"listen-tcp:\", \"tcp-l:\", \"l-tcp:\"],\n    arg_handling = parse,\n    overlay = false,\n    StreamOriented,\n    MultiConnect,\n    help = r#\"\nListen TCP port on specified address.\n    \nExample: echo server\n\n    websocat tcp-l:0.0.0.0:1441 mirror:\n    \nExample: redirect TCP to a websocket\n\n    websocat tcp-l:0.0.0.0:8088 ws://echo.websocket.org\n\"#\n);\n\n#[derive(Debug, Clone)]\npub struct UdpConnect(pub SocketAddr);\nimpl Specifier for UdpConnect {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        once(udp_connect_peer(&self.0, &p.program_options))\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec );\n}\nspecifier_class!(\n    name = UdpConnectClass,\n    target = UdpConnect,\n    prefixes = [\"udp:\", \"udp-connect:\", \"connect-udp:\", \"udp-c:\", \"c-udp:\"],\n    arg_handling = parse,\n    overlay = false,\n    MessageOriented,\n    SingleConnect,\n    help = r#\"\nSend and receive packets to specified UDP socket, from random UDP port  \n\"#\n);\n\n#[derive(Debug, Clone)]\npub struct UdpListen(pub SocketAddr);\nimpl Specifier for UdpListen {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        once(udp_listen_peer(&self.0, &p.program_options))\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec );\n}\nspecifier_class!(\n    name = UdpListenClass,\n    target = UdpListen,\n    prefixes = [\"udp-listen:\", \"listen-udp:\", \"udp-l:\", \"l-udp:\"],\n    arg_handling = parse,\n    overlay = false,\n    MessageOriented,\n    SingleConnect,\n    help = r#\"\nBind an UDP socket to specified host:port, receive packet\nfrom any remote UDP socket, send replies to recently observed\nremote UDP socket.\n\nNote that it is not a multiconnect specifier like e.g. `tcp-listen`:\nentire lifecycle of the UDP socket is the same connection.\n\nFile a feature request on Github if you want proper DNS-like request-reply UDP mode here.\n\"#\n);\n\n/*\nstruct RcReadProxy<R>(Rc<R>) where for<'a> &'a R : AsyncRead;\n\nimpl<R> AsyncRead for RcReadProxy<R> where for<'a> &'a R : AsyncRead{}\nimpl<R> Read for RcReadProxy<R> where for<'a> &'a R : AsyncRead {\n    fn read(&mut self, buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {\n        (&*self.0).read(buf)\n    }\n}\n\nstruct RcWriteProxy<W>(Rc<W>) where for<'a> &'a W : AsyncWrite;\n\nimpl<W> AsyncWrite for RcWriteProxy<W> where for<'a> &'a W : AsyncWrite {\n    fn shutdown(&mut self) -> futures::Poll<(),std::io::Error> {\n        (&*self.0).shutdown()\n    }\n}\nimpl<W> Write for RcWriteProxy<W> where for<'a> &'a W : AsyncWrite {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        (&*self.0).write(buf)\n    }\n    fn flush(&mut self) -> IoResult<()> {\n        (&*self.0).flush()\n    }\n}\n*/\n\n// based on https://github.com/tokio-rs/tokio-core/blob/master/examples/proxy.rs\n#[derive(Clone)]\nstruct MyTcpStream(Rc<TcpStream>, bool);\n\nimpl Read for MyTcpStream {\n    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {\n        (&*self.0).read(buf)\n    }\n}\n\nimpl Write for MyTcpStream {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        (&*self.0).write(buf)\n    }\n\n    fn flush(&mut self) -> IoResult<()> {\n        Ok(())\n    }\n}\n\nimpl AsyncRead for MyTcpStream {}\n\nimpl AsyncWrite for MyTcpStream {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        self.0.shutdown(std::net::Shutdown::Write)?;\n        Ok(().into())\n    }\n}\n\nimpl Drop for MyTcpStream {\n    fn drop(&mut self) {\n        let i_am_read_part = self.1;\n        if i_am_read_part {\n            let _ = self.0.shutdown(std::net::Shutdown::Read);\n        }\n    }\n}\n\npub fn tcp_race(addrs: &[SocketAddr]) -> Box<dyn Future<Item = TcpStream, Error = Box<dyn std::error::Error + Send + Sync>> + Send> {\n    // Apply Happy Eyeballs in case of multiple proposed addresses.\n    if addrs.len() > 1 {\n        debug!(\"Setting up a race between multiple TCP client sockets. Who connects the first?\");\n    }\n    use futures::stream::futures_unordered::FuturesUnordered;\n    let mut fu = FuturesUnordered::new();\n    for addr in addrs {\n        let addr = *addr;\n        fu.push(\n            TcpStream::connect(&addr)\n            .map(move |x| {\n                info!(\"Connected to TCP {}\", addr);\n                x\n            })\n            .map_err(|e|Box::new(e) as Box<dyn std::error::Error + Send + Sync>)\n        );\n    }\n    // reverse Ok and Err variants so that `fold` would exit early on a successful connection, but accumulate errors.\n    let p = fu.then(|x| {\n        let reversed = match x {\n            Ok(a) => Err(a),\n            Err(a) => Ok(a),\n        };\n        futures::future::done(reversed)\n    }).fold(None, |_accum, e|{\n        log::info!(\"Failure during connecting TCP: {}\", e);\n        futures::future::ok(Some(e))\n    }).then(|x| {\n        match x {\n            Ok(a) => Err(a),\n            Err(a) => Ok(a),\n        }\n    }).map_err(|e : Option<_>| e.unwrap());\n    Box::new(p)\n}\n\npub fn tcp_connect_peer(addrs: &[SocketAddr]) -> BoxedNewPeerFuture {\n    let p = tcp_race(addrs)\n    .map(|x : TcpStream| {\n        let x = Rc::new(x);\n        Peer::new(\n            MyTcpStream(x.clone(), true),\n            MyTcpStream(x.clone(), false),\n            None /* TODO */\n        )\n    }).map_err(|e|{let e : Box<dyn std::error::Error> = e; e});\n    /*let p = fu.into_future().and_then(|(x, _losers)| {\n        let peer = x.unwrap();\n        debug!(\"We have a winner. Disconnecting losers.\");\n        futures::future::ok(peer)       \n    });*/\n    //Box::new(p.map_err(|(e,_)|e)) as BoxedNewPeerFuture\n    Box::new(p) as BoxedNewPeerFuture\n}\n\npub fn tcp_listen_peer(addr: &SocketAddr, l2r: L2rUser, announce: bool) -> BoxedNewPeerStream {\n    let bound = match TcpListener::bind(addr) {\n        Ok(x) => x,\n        Err(e) => return peer_err_s(e),\n    };\n    debug!(\"Listening TCP socket\");\n    if announce {\n        println!(\"LISTEN proto=tcp,ip={},port={}\", addr.ip(), addr.port());\n    }\n    use tk_listen::ListenExt;\n    Box::new(\n        bound\n            .incoming()\n            .sleep_on_error(::std::time::Duration::from_millis(500))\n            .map(move |x| {\n                let addr = x.peer_addr().ok();\n                info!(\"Incoming TCP connection from {:?}\", addr);\n\n                match l2r {\n                    L2rUser::FillIn(ref y) => {\n                        let mut z = y.borrow_mut();\n                        z.client_addr = addr.map(|a| format!(\"{}\", a));\n                    }\n                    L2rUser::ReadFrom(_) => {}\n                }\n\n                let x = Rc::new(x);\n                Peer::new(\n                    MyTcpStream(x.clone(), true),\n                    MyTcpStream(x.clone(), false),\n                    None, /* TODO */\n                )\n            })\n            .map_err(|()| crate::simple_err2(\"unreachable error?\")),\n    ) as BoxedNewPeerStream\n}\n\n#[derive(Debug)]\nenum UdpPeerState {\n    ConnectMode,\n    WaitingForAddress((Sender<()>, Receiver<()>)),\n    HasAddress(SocketAddr),\n}\n\nstruct UdpPeer {\n    s: UdpSocket,\n    state: Option<UdpPeerState>,\n    oneshot_mode: bool,\n}\n\n#[derive(Clone)]\nstruct UdpPeerHandle(Rc<RefCell<UdpPeer>>);\n\nfn get_zero_address(addr: &SocketAddr) -> SocketAddr {\n    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};\n    let ip = match addr.ip() {\n        IpAddr::V4(_) => IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),\n        IpAddr::V6(_) => IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)),\n    };\n    SocketAddr::new(ip, 0)\n}\n\nfn apply_udp_options(s: &UdpSocket, opts:&Rc<Options>) -> IoResult<()> {\n    if opts.udp_broadcast {\n        s.set_broadcast(true)?;\n    }\n    let mut multicast_v4 = false;\n    let mut multicast_v6 = false;\n\n    let mut v4ai = opts.udp_join_multicast_iface_v4.iter();\n    let mut v6ai = opts.udp_join_multicast_iface_v6.iter();\n\n    let use_ai = opts.udp_join_multicast_iface_v4.len() + opts.udp_join_multicast_iface_v6.len() > 0;\n\n    for multicast_address in opts.udp_join_multicast_addr.iter() {\n        match multicast_address {\n            std::net::IpAddr::V4(a) => {\n                multicast_v4 = true;\n                let interface_address = if use_ai {\n                    *v4ai.next().unwrap()\n                } else {\n                    std::net::Ipv4Addr::UNSPECIFIED\n                };\n                s.join_multicast_v4(a, &interface_address)?;\n            },\n            std::net::IpAddr::V6(a) => {\n                multicast_v6 = true;\n                let interface_index = if use_ai {\n                    *v6ai.next().unwrap()\n                } else {\n                    0\n                };\n                s.join_multicast_v6(a, interface_index)?;\n            }\n        }\n    }\n\n    if opts.udp_multicast_loop {\n        if multicast_v4 {\n            s.set_multicast_loop_v4(true)?;\n        }\n        if multicast_v6 {\n            s.set_multicast_loop_v6(true)?;\n        }\n    }\n    if let Some(ttl) = opts.udp_ttl {\n        s.set_ttl(ttl)?;\n        if multicast_v4 {\n            s.set_multicast_ttl_v4(ttl)?;\n        }\n    }\n    Ok(())\n}\n\npub fn get_udp(addr: &SocketAddr, opts: &Rc<Options>) -> IoResult<UdpSocket> {\n    let u = match addr {\n        SocketAddr::V4(_) => net2::UdpBuilder::new_v4()?,\n        SocketAddr::V6(_) => net2::UdpBuilder::new_v6()?,\n    };\n    if opts.udp_reuseaddr {\n        u.reuse_address(true)?;\n    }\n    //u.only_v6(true);\n    let u = u.bind(addr)?;\n    UdpSocket::from_std(u, &tokio_reactor::Handle::default())\n}\n\npub fn udp_connect_peer(addr: &SocketAddr, opts: &Rc<Options>) -> BoxedNewPeerFuture {\n    let za = get_zero_address(addr);\n\n    Box::new(futures::future::result(\n        get_udp(&za, opts)\n            .and_then(|x| {\n                x.connect(addr)?;\n                apply_udp_options(&x, opts)?;\n\n                let h1 = UdpPeerHandle(Rc::new(RefCell::new(UdpPeer {\n                    s: x,\n                    state: Some(UdpPeerState::ConnectMode),\n                    oneshot_mode: opts.udp_oneshot_mode,\n                })));\n                let h2 = h1.clone();\n                Ok(Peer::new(h1, h2, None))\n            })\n            .map_err(box_up_err),\n    )) as BoxedNewPeerFuture\n}\n\npub fn udp_listen_peer(addr: &SocketAddr, opts: &Rc<Options>) -> BoxedNewPeerFuture {\n    Box::new(futures::future::result(\n        get_udp(addr, opts)\n            .and_then(|x| {\n                apply_udp_options(&x, opts)?;\n                debug!(\"Ready for serving UDP\");\n                if opts.announce_listens {\n                    println!(\"LISTEN proto=udp,ip={},port={}\", addr.ip(), addr.port());\n                }\n                let h1 = UdpPeerHandle(Rc::new(RefCell::new(UdpPeer {\n                    s: x,\n                    state: Some(UdpPeerState::WaitingForAddress(channel())),\n                    oneshot_mode: opts.udp_oneshot_mode,\n                })));\n                let h2 = h1.clone();\n                Ok(Peer::new(h1, h2, None))\n            })\n            .map_err(box_up_err),\n    )) as BoxedNewPeerFuture\n}\n\nimpl Read for UdpPeerHandle {\n    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {\n        let mut p = self.0.borrow_mut();\n        match p.state.take().expect(\"Assertion failed 193912\") {\n            UdpPeerState::ConnectMode => {\n                p.state = Some(UdpPeerState::ConnectMode);\n                p.s.recv2(buf)\n            }\n            UdpPeerState::HasAddress(oldaddr) => {\n                p.s.recv_from2(buf)\n                    .map(|(ret, addr)| {\n                        if addr != oldaddr {\n                            warn!(\"New client for the same listening UDP socket\");\n                        }\n                        p.state = Some(UdpPeerState::HasAddress(addr));\n                        ret\n                    })\n                    .inspect_err(|_| {\n                        p.state = Some(UdpPeerState::HasAddress(oldaddr));\n                    })\n            }\n            UdpPeerState::WaitingForAddress((cmpl, pollster)) => match p.s.recv_from2(buf) {\n                Ok((ret, addr)) => {\n                    p.state = Some(UdpPeerState::HasAddress(addr));\n                    let _ = cmpl.send(());\n                    Ok(ret)\n                }\n                Err(e) => {\n                    p.state = Some(UdpPeerState::WaitingForAddress((cmpl, pollster)));\n                    Err(e)\n                }\n            },\n        }\n    }\n}\n\nimpl Write for UdpPeerHandle {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        let mut p = self.0.borrow_mut();\n        match p.state.take().expect(\"Assertion failed 193913\") {\n            UdpPeerState::ConnectMode => {\n                p.state = Some(UdpPeerState::ConnectMode);\n                p.s.send2(buf)\n            }\n            UdpPeerState::HasAddress(a) => {\n                if p.oneshot_mode {\n                    p.state = Some(UdpPeerState::WaitingForAddress(channel()));\n                } else {\n                    p.state = Some(UdpPeerState::HasAddress(a));\n                }\n                p.s.send_to2(buf, &a)\n            }\n            UdpPeerState::WaitingForAddress((cmpl, mut pollster)) => {\n                let _ = pollster.poll(); // register wakeup\n                p.state = Some(UdpPeerState::WaitingForAddress((cmpl, pollster)));\n                wouldblock()\n            }\n        }\n    }\n\n    fn flush(&mut self) -> IoResult<()> {\n        Ok(())\n    }\n}\n\nimpl AsyncRead for UdpPeerHandle {}\n\nimpl AsyncWrite for UdpPeerHandle {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        Ok(().into())\n    }\n}\n\n/// Squirreled await from deprecated UdpSocket functions\ntrait UndeprecateNonpollSendRecv {\n    fn recv2(&mut self, buf: &mut [u8]) -> std::io::Result<usize>;\n    fn recv_from2(&mut self, buf: &mut [u8]) -> std::io::Result<(usize, SocketAddr)>;\n    fn send2(&mut self, buf: &[u8]) -> std::io::Result<usize>;\n    fn send_to2(&mut self, buf: &[u8], target: &SocketAddr) -> std::io::Result<usize>;\n}\n\nimpl UndeprecateNonpollSendRecv for UdpSocket {\n    fn recv2(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {\n        match self.poll_recv(buf)? {\n            futures::Async::Ready(n) => Ok(n),\n            futures::Async::NotReady => Err(std::io::ErrorKind::WouldBlock.into()),\n        }\n    }\n\n    fn recv_from2(&mut self, buf: &mut [u8]) -> std::io::Result<(usize, SocketAddr)> {\n        match self.poll_recv_from(buf)? {\n            futures::Async::Ready(ret) => Ok(ret),\n            futures::Async::NotReady => Err(std::io::ErrorKind::WouldBlock.into()),\n        }\n    }\n\n    fn send2(&mut self, buf: &[u8]) -> std::io::Result<usize> {\n        match self.poll_send(buf)? {\n            futures::Async::Ready(n) => Ok(n),\n            futures::Async::NotReady => Err(std::io::ErrorKind::WouldBlock.into()),\n        }\n    }\n\n    fn send_to2(&mut self, buf: &[u8], target: &SocketAddr) -> std::io::Result<usize> {\n        match self.poll_send_to(buf, target)? {\n            futures::Async::Ready(n) => Ok(n),\n            futures::Async::NotReady => Err(std::io::ErrorKind::WouldBlock.into()),\n        }\n    }\n}\n"
  },
  {
    "path": "src/options.rs",
    "content": "pub use super::socks5_peer::SocksSocketAddr;\n\nuse super::readdebt::DebtHandling;\n\nuse std::ffi::OsString;\nuse std::net::SocketAddr;\n\n#[derive(Debug, Clone)]\npub struct StaticFile {\n    pub uri: String,\n    pub file: ::std::path::PathBuf,\n    pub content_type: String,\n}\n\nextern crate http_bytes;\nuse http_bytes::http;\n\n#[derive(SmartDefault, Derivative)]\n#[derivative(Debug)]\npub struct Options {\n    pub websocket_text_mode: bool,\n    pub websocket_protocol: Option<String>,\n    pub websocket_reply_protocol: Option<String>,\n    pub udp_oneshot_mode: bool,\n    pub udp_broadcast: bool,\n    pub udp_multicast_loop: bool,\n    pub udp_ttl: Option<u32>,\n    pub udp_join_multicast_addr: Vec<std::net::IpAddr>,\n    pub udp_join_multicast_iface_v4: Vec<std::net::Ipv4Addr>,\n    pub udp_join_multicast_iface_v6: Vec<u32>,\n    pub udp_reuseaddr: bool,\n    pub unidirectional: bool,\n    pub unidirectional_reverse: bool,\n    pub max_messages: Option<usize>,\n    pub max_messages_rev: Option<usize>,\n    pub exit_on_eof: bool,\n    pub oneshot: bool,\n    pub unlink_unix_socket: bool,\n    pub unix_socket_accept_from_fd: bool,\n    pub exec_args: Vec<String>,\n    pub ws_c_uri: String, // TODO: delete this\n    pub linemode_strip_newlines: bool,\n    pub linemode_strict: bool,\n    pub origin: Option<String>,\n    pub custom_headers: Vec<(String, Vec<u8>)>,\n    pub custom_reply_headers: Vec<(String, Vec<u8>)>,\n    pub websocket_version: Option<String>,\n    pub websocket_dont_close: bool,\n    pub websocket_ignore_zeromsg: bool,\n    pub one_message: bool,\n    pub no_auto_linemode: bool,\n    #[default = 65536]\n    pub buffer_size: usize,\n    #[default = 16]\n    pub broadcast_queue_len: usize,\n    #[default(DebtHandling::Silent)]\n    pub read_debt_handling: DebtHandling,\n    pub linemode_zero_terminated: bool,\n    pub restrict_uri: Option<String>,\n    pub serve_static_files: Vec<StaticFile>,\n    pub exec_set_env: bool,\n    pub no_exit_on_zeromsg: bool,\n    pub reuser_send_zero_msg_on_disconnect: bool,\n    pub process_zero_sighup: bool,\n    pub process_exit_sighup: bool,\n    pub process_exit_on_disconnect: bool,\n    pub socks_destination: Option<SocksSocketAddr>,\n    pub auto_socks5: Option<SocketAddr>,\n    pub socks5_bind_script: Option<OsString>,\n    pub socks5_user_pass: Option<String>,\n    pub tls_domain: Option<String>,\n    #[derivative(Debug = \"ignore\")]\n    pub pkcs12_der: Option<Vec<u8>>,\n    #[derivative(Debug = \"ignore\")]\n    pub pkcs12_passwd: Option<String>,\n    #[derivative(Debug = \"ignore\")]\n    pub client_pkcs12_der: Option<Vec<u8>>,\n    #[derivative(Debug = \"ignore\")]\n    pub client_pkcs12_passwd: Option<String>,\n    pub tls_insecure: bool,\n\n    pub headers_to_env: Vec<String>,\n\n    pub max_parallel_conns: Option<usize>,\n    pub ws_ping_interval: Option<u64>,\n    pub ws_ping_timeout: Option<u64>,\n\n    pub request_uri: Option<http::Uri>,\n    pub request_method: Option<http::Method>,\n    pub request_headers: Vec<(http::header::HeaderName, http::header::HeaderValue)>,\n\n    pub autoreconnect_delay_millis: u64,\n\n    pub ws_text_prefix: Option<String>,\n    pub ws_binary_prefix: Option<String>,\n    pub ws_binary_base64: bool,\n    pub ws_text_base64: bool,\n    pub close_status_code: Option<u16>,\n    pub close_reason: Option<String>,\n\n    /// Only affects linter\n    pub asyncstdio: bool,\n\n    pub foreachmsg_wait_reads: bool,\n    pub announce_listens: bool,\n    pub timestamp_monotonic: bool,\n    pub print_ping_rtts: bool,\n\n    #[cfg(feature = \"crypto_peer\")]\n    pub crypto_key: Option<[u8; 32]>,\n    #[cfg(feature = \"crypto_peer\")]\n    pub crypto_reverse: bool,\n\n    #[cfg(feature = \"prometheus_peer\")]\n    pub prometheus: Option<SocketAddr>,\n\n    #[default = 0x1c]\n    pub byte_to_exit_on: u8,\n\n    #[default = 209715200]\n    pub max_ws_message_length: usize,\n    #[default = 104857600]\n    pub max_ws_frame_length: usize,\n\n    pub preamble: Vec<String>,\n    pub preamble_reverse: Vec<String>,\n\n    pub compress_deflate: bool,\n    pub compress_zlib: bool,\n    pub compress_gzip: bool,\n    pub uncompress_deflate: bool,\n    pub uncompress_zlib: bool,\n    pub uncompress_gzip: bool,\n\n    pub jsonrpc_omit_jsonrpc: bool,\n    pub inhibit_pongs: Option<usize>,\n    pub max_sent_pings: Option<usize>,\n\n    pub lengthprefixed_header_bytes: usize,\n    pub lengthprefixed_little_endian: bool,\n    pub lengthprefixed_skip_read_direction: bool,\n    pub lengthprefixed_skip_write_direction: bool,\n\n}\n"
  },
  {
    "path": "src/primitive_reuse_peer.rs",
    "content": "extern crate futures;\nextern crate tokio_io;\n\nuse futures::future::ok;\nuse std::cell::RefCell;\nuse std::rc::Rc;\n\nuse super::{BoxedNewPeerFuture, Peer};\n\nuse std::io::{Error as IoError, Read, Write};\nuse tokio_io::{AsyncRead, AsyncWrite};\n\nuse super::{once, ConstructParams, PeerConstructor, Specifier};\nuse futures::Future;\nuse std::ops::DerefMut;\n\n#[derive(Debug)]\npub struct Reuser(pub Rc<dyn Specifier>);\nimpl Specifier for Reuser {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        let send_zero_msg_on_disconnect = p.program_options.reuser_send_zero_msg_on_disconnect;\n        let reuser = p.global(GlobalState::default).clone();\n        let mut reuser = reuser.clone();\n        let l2r = p.left_to_right.clone();\n        let inner = || self.0.construct(p).get_only_first_conn(l2r);\n        once(connection_reuser(\n            &mut reuser,\n            inner,\n            send_zero_msg_on_disconnect,\n        ))\n    }\n    specifier_boilerplate!(singleconnect has_subspec globalstate);\n    self_0_is_subspecifier!(...);\n}\n\nspecifier_class!(\n    name = ReuserClass,\n    target = Reuser,\n    prefixes = [\"reuse-raw:\", \"raw-reuse:\"],\n    arg_handling = subspec,\n    overlay = true,\n    MessageBoundaryStatusDependsOnInnerType,\n    SingleConnect,\n    help = r#\"\nReuse subspecifier for serving multiple clients: unpredictable mode. [A]\n\nBetter used with --unidirectional, otherwise replies get directed to\nrandom connected client.\n\nExample: Forward multiple parallel WebSocket connections to a single persistent TCP connection\n\n    websocat -u ws-l:0.0.0.0:8800 reuse:tcp:127.0.0.1:4567\n\nExample (unreliable): don't disconnect SSH when websocket reconnects\n\n    websocat ws-l:[::]:8088 reuse:tcp:127.0.0.1:22\n\"#\n);\n\ntype PeerSlot = Rc<RefCell<Option<Peer>>>;\n\n#[derive(Default, Clone)]\npub struct GlobalState(PeerSlot);\n\n#[derive(Clone)]\nstruct PeerHandle(PeerSlot, bool);\n\nimpl Read for PeerHandle {\n    fn read(&mut self, b: &mut [u8]) -> Result<usize, IoError> {\n        if let Some(ref mut x) = *self.0.borrow_mut().deref_mut() {\n            x.0.read(b)\n        } else {\n            unreachable!()\n        }\n    }\n}\nimpl AsyncRead for PeerHandle {}\n\nimpl Write for PeerHandle {\n    fn write(&mut self, b: &[u8]) -> Result<usize, IoError> {\n        if let Some(ref mut x) = *self.0.borrow_mut().deref_mut() {\n            x.1.write(b)\n        } else {\n            unreachable!()\n        }\n    }\n    fn flush(&mut self) -> Result<(), IoError> {\n        if let Some(ref mut x) = *self.0.borrow_mut().deref_mut() {\n            x.1.flush()\n        } else {\n            unreachable!()\n        }\n    }\n}\nimpl AsyncWrite for PeerHandle {\n    fn shutdown(&mut self) -> futures::Poll<(), IoError> {\n        if self.1 {\n            let _ = self.write(b\"\");\n        }\n        if let Some(ref mut _x) = *self.0.borrow_mut().deref_mut() {\n            // Ignore shutdown attempts\n            Ok(futures::Async::Ready(()))\n        //_x.1.shutdown()\n        } else {\n            unreachable!()\n        }\n    }\n}\n\npub fn connection_reuser<F: FnOnce() -> BoxedNewPeerFuture>(\n    s: &mut GlobalState,\n    inner_peer: F,\n    send_zero_msg_on_disconnect: bool,\n) -> BoxedNewPeerFuture {\n    let need_init = s.0.borrow().is_none();\n\n    let rc = s.0.clone();\n\n    if need_init {\n        info!(\"Initializing\");\n        Box::new(inner_peer().and_then(move |inner| {\n            {\n                let mut b = rc.borrow_mut();\n                let x: &mut Option<Peer> = b.deref_mut();\n                *x = Some(inner);\n            }\n\n            let ps: PeerSlot = rc.clone();\n\n            let ph1 = PeerHandle(ps, send_zero_msg_on_disconnect);\n            let ph2 = ph1.clone();\n            let peer = Peer::new(ph1, ph2, None /* TODO */);\n            ok(peer)\n        })) as BoxedNewPeerFuture\n    } else {\n        info!(\"Reusing\");\n        let ps: PeerSlot = rc.clone();\n\n        let ph1 = PeerHandle(ps, send_zero_msg_on_disconnect);\n        let ph2 = ph1.clone();\n        let peer = Peer::new(ph1, ph2, None /* TODO */);\n        Box::new(ok(peer)) as BoxedNewPeerFuture\n    }\n}\n"
  },
  {
    "path": "src/process_peer.rs",
    "content": "extern crate tokio_process;\n\nuse futures;\nuse std::io::Result as IoResult;\nuse std::io::{Read, Write};\nuse std::{self, process::ExitStatus};\nuse tokio_io::{AsyncRead, AsyncWrite};\n\nuse super::{L2rUser, LeftSpecToRightSpec};\n\nuse std::cell::RefCell;\nuse std::rc::Rc;\n\nuse std::process::Command;\n\nuse self::tokio_process::{Child, CommandExt};\n\nuse super::{once, ConstructParams, PeerConstructor, Specifier};\nuse super::{BoxedNewPeerFuture, Peer};\nuse std::process::Stdio;\n\nfn needenv(p: &ConstructParams) -> Option<&LeftSpecToRightSpec> {\n    match (p.program_options.exec_set_env, &p.left_to_right) {\n        (true, L2rUser::ReadFrom(ref x)) => Some(&**x),\n        _ => None,\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct Cmd(pub String);\nimpl Specifier for Cmd {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        let zero_sighup = p.program_options.process_zero_sighup;\n        let exit_sighup = p.program_options.process_exit_sighup;\n        let exit_on_disconnect = p.program_options.process_exit_on_disconnect;\n        let args = if cfg!(target_os = \"windows\") {\n            let mut args = Command::new(\"cmd\");\n            args.arg(\"/C\").arg(self.0.clone());\n            args\n        } else {\n            let mut args = Command::new(\"sh\");\n            args.arg(\"-c\").arg(self.0.clone());\n            args\n        };\n        let env = needenv(&p);\n        once(Box::new(futures::future::result(process_connect_peer(\n            args,\n            env,\n            zero_sighup,\n            exit_sighup,\n            exit_on_disconnect,\n        ))) as BoxedNewPeerFuture)\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec );\n}\nspecifier_class!(\n    name = CmdClass,\n    target = Cmd,\n    prefixes = [\"cmd:\"],\n    arg_handling = into,\n    overlay = false,\n    StreamOriented,\n    SingleConnect,\n    help = r#\"\nStart specified command line using `sh -c` or `cmd /C` (depending on platform)\n\nOtherwise should be the the same as `sh-c:` (see examples from there).\n\"#\n);\n// TODO: client and example output for each server example\n// TODO: chromium-based examples\n\n#[derive(Debug, Clone)]\npub struct ShC(pub String);\nimpl Specifier for ShC {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        let zero_sighup = p.program_options.process_zero_sighup;\n        let exit_sighup = p.program_options.process_exit_sighup;\n        let exit_on_disconnect = p.program_options.process_exit_on_disconnect;\n        let mut args = Command::new(\"sh\");\n        args.arg(\"-c\").arg(self.0.clone());\n        let env = needenv(&p);\n        once(Box::new(futures::future::result(process_connect_peer(\n            args,\n            env,\n            zero_sighup,\n            exit_sighup,\n            exit_on_disconnect,\n        ))) as BoxedNewPeerFuture)\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec );\n}\nspecifier_class!(\n    name = ShCClass,\n    target = ShC,\n    prefixes = [\"sh-c:\"],\n    arg_handling = into,\n    overlay = false,\n    StreamOriented,\n    SingleConnect,\n    help = r#\"\nStart specified command line using `sh -c` (even on Windows)\n  \nExample: serve a counter\n\n    websocat -U ws-l:127.0.0.1:8008 sh-c:'for i in 0 1 2 3 4 5 6 7 8 9 10; do echo $i; sleep 1; done'\n  \nExample: unauthenticated shell\n\n    websocat --exit-on-eof ws-l:127.0.0.1:5667 sh-c:'bash -i 2>&1'\n\"#\n);\n\n#[derive(Debug, Clone)]\npub struct Exec(pub String);\nimpl Specifier for Exec {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        let zero_sighup = p.program_options.process_zero_sighup;\n        let exit_sighup = p.program_options.process_exit_sighup;\n        let exit_on_disconnect = p.program_options.process_exit_on_disconnect;\n        let mut args = Command::new(self.0.clone());\n        args.args(p.program_options.exec_args.clone());\n        let env = needenv(&p);\n        once(Box::new(futures::future::result(process_connect_peer(\n            args,\n            env,\n            zero_sighup,\n            exit_sighup,\n            exit_on_disconnect,\n        ))) as BoxedNewPeerFuture)\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec );\n}\nspecifier_class!(\n    name = ExecClass,\n    target = Exec,\n    prefixes = [\"exec:\"],\n    arg_handling = into,\n    overlay = false,\n    StreamOriented,\n    SingleConnect,\n    help = r#\"\nExecute a program directly (without a subshell), providing array of arguments on Unix [A]\n\nExample: Serve current date\n\n  websocat -U ws-l:127.0.0.1:5667 exec:date\n  \nExample: pinger\n\n  websocat -U ws-l:127.0.0.1:5667 exec:ping --exec-args 127.0.0.1 -c 1\n  \n\"#\n);\n\nfn process_connect_peer(\n    mut cmd: Command,\n    l2r: Option<&LeftSpecToRightSpec>,\n    zero_sighup: bool,\n    close_sighup: bool,\n    exit_on_disconnect: bool,\n) -> Result<Peer, Box<dyn std::error::Error>> {\n    if let Some(x) = l2r {\n        if let Some(ref z) = x.client_addr {\n            cmd.env(\"WEBSOCAT_CLIENT\", z);\n        };\n        if let Some(ref z) = x.uri {\n            cmd.env(\"WEBSOCAT_URI\", z);\n        };\n        for (hn, hv) in &x.headers {\n            cmd.env(format!(\"H_{}\", hn), hv);\n        }\n    }\n    cmd.stdin(Stdio::piped()).stdout(Stdio::piped());\n    let child = cmd.spawn_async()?;\n    let ph = ProcessPeer {\n        chld: Rc::new(RefCell::new(ForgetfulProcess {\n            chld: Some(child),\n            exit_on_disconnect,\n        })),\n        sighup_on_zero: zero_sighup,\n        sighup_on_close: close_sighup,\n    };\n    Ok(Peer::new(ph.clone(), ph, None /* TODO */))\n}\n\nstruct ForgetfulProcess {\n    chld: Option<Child>,\n    exit_on_disconnect: bool,\n}\n#[derive(Clone)]\nstruct ProcessPeer {\n    chld: Rc<RefCell<ForgetfulProcess>>,\n    sighup_on_zero: bool,\n    sighup_on_close: bool,\n}\n\nimpl Read for ProcessPeer {\n    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {\n        self.chld\n            .borrow_mut()\n            .chld\n            .as_mut()\n            .unwrap()\n            .stdout()\n            .as_mut()\n            .expect(\"assertion failed 1425\")\n            .read(buf)\n    }\n}\n\nimpl Write for ProcessPeer {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        #[cfg(unix)]\n        {\n            if self.sighup_on_zero && buf.is_empty() {\n                // TODO use nix crate?\n                if let Some(ref chld) = self.chld.borrow().chld {\n                    unsafe {\n                        extern crate libc;\n                        libc::kill(chld.id() as libc::pid_t, libc::SIGHUP);\n                    }\n                }\n            }\n        }\n        self.chld\n            .borrow_mut()\n            .chld\n            .as_mut()\n            .unwrap()\n            .stdin()\n            .as_mut()\n            .expect(\"assertion failed 1425\")\n            .write(buf)\n    }\n\n    fn flush(&mut self) -> IoResult<()> {\n        self.chld\n            .borrow_mut()\n            .chld\n            .as_mut()\n            .unwrap()\n            .stdin()\n            .as_mut()\n            .expect(\"assertion failed 1425\")\n            .flush()\n    }\n}\n\nimpl AsyncRead for ProcessPeer {}\n\nimpl AsyncWrite for ProcessPeer {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        #[cfg(unix)]\n        {\n            if self.sighup_on_close {\n                // TODO use nix crate?\n                if let Some(ref chld) = self.chld.borrow().chld {\n                    unsafe {\n                        extern crate libc;\n                        libc::kill(chld.id() as libc::pid_t, libc::SIGHUP);\n                    }\n                }\n            }\n        }\n        debug!(\"Shutdown of process peer's writer\");\n        let mut c: tokio_process::ChildStdin = self\n            .chld\n            .borrow_mut()\n            .chld\n            .as_mut()\n            .unwrap()\n            .stdin()\n            .take()\n            .expect(\"assertion failed 1425\");\n        c.shutdown()\n    }\n}\n\nimpl Drop for ForgetfulProcess {\n    fn drop(&mut self) {\n        use futures::Future;\n        let mut chld = self.chld.take().unwrap();\n        if self.exit_on_disconnect {\n            debug!(\"Forcing child process to exit\");\n            match chld.kill() {\n                Ok(()) => (),\n                Err(e) => {\n                    warn!(\"Error terminating child process: {}\", e);\n                }\n            }\n            tokio::spawn(\n                chld.map(|_exc: ExitStatus| {\n\n                }).map_err(|_|())\n            );\n        } else {\n            tokio::spawn(\n                chld.map(|exc: ExitStatus| {\n                    if exc.success() {\n                        debug!(\"Child process exited\")\n                    } else {\n                        warn!(\"Child process exited unsuccessfully: {:?}\", exc.code());\n                    }\n                })\n                .map_err(|e| {\n                    error!(\"Error waiting for child process termination: {}\", e);\n                }),\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "src/prometheus_peer.rs",
    "content": "use futures::future::ok;\nuse futures::{Async};\nuse prometheus::core::{AtomicU64, Atomic};\n\nuse std::cell::RefCell;\nuse std::net::{SocketAddr, TcpListener};\nuse std::rc::Rc;\nuse std::time::Duration;\n\nuse crate::ws_server_peer::http_serve::get_static_file_reply;\n\nuse super::{BoxedNewPeerFuture, Peer};\nuse super::{ConstructParams, PeerConstructor, Specifier};\n\nuse std::io::{Read, Write, ErrorKind};\nuse tokio_io::{AsyncRead, AsyncWrite};\n\nuse std::io::Error as IoError;\n\nuse prometheus::{Encoder, IntCounter, Histogram};\n\n#[derive(prometheus_metric_storage::MetricStorage)]\n#[metric(subsystem = \"websocat1\")]\npub struct GlobalStats {\n    /// Number of times write function was called\n    w_msgs: IntCounter,\n    /// Number of times read function was called\n    r_msgs: IntCounter,\n\n    /// Total number of written bytes to the `prometheus:` node\n    w_bytes: IntCounter,\n    /// Total number of read bytes from the `prometheus:` node\n    r_bytes: IntCounter,\n\n    /// Number of times `prometheus:` overlay was instantiated\n    connects: IntCounter,\n\n    /// Number of times `prometheus:` overlay's destructor was called\n    disconnects: IntCounter,\n\n    /// Distribution of times between `prometheus:` overlay initiation and destruction\n    #[metric(buckets(0.1, 1, 10, 60, 300, 3600))]\n    session_durations: Histogram,\n\n    /// Distribution of times between one `prometheus:` overlay initiation and the next initiation\n    #[metric(buckets(0.1, 1, 10, 60, 300, 3600))]\n    between_connects: Histogram,\n\n    /// Distribution of the number of total bytes written to the overlay per connection \n    #[metric(buckets(0, 32, 1024, 65536, 1048576, 33554432, 1073741824))]\n    session_w_bytes: Histogram,\n\n    /// Distribution of the number of total bytes read to the overlay per connection \n    #[metric(buckets(0, 32, 1024, 65536, 1048576, 33554432, 1073741824))]\n    session_r_bytes: Histogram,\n\n    /// Distribution of the number of total count of write function calls to the overlay per connection \n    #[metric(buckets(0, 1, 2, 8, 64, 2048, 65536, 2097152))]\n    session_w_msgs: Histogram,\n\n    /// Distribution of the number of total count of read function calls to the overlay per connection \n    #[metric(buckets(0, 1, 2, 8, 64, 2048, 65536, 2097152))]\n    session_r_msgs: Histogram,\n\n    /// Distribution of the `session_r_bytes` divided by `session_durations` values\n    #[metric(buckets(0.5, 10, 100, 1000, 1000_0, 1000_00, 1000_000, 10_000_000, 100_000_000))]\n    session_avg_r_bps: Histogram,\n\n    /// Distribution of the `session_w_bytes` divided by `session_durations` values\n    #[metric(buckets(0.5, 10, 100, 1000, 1000_0, 1000_00, 1000_000, 10_000_000, 100_000_000))]\n    session_avg_w_bps: Histogram,\n\n    /// Distribution of byte lengths underlying `read` function successfully returned\n    #[metric(buckets(0, 1, 10, 50, 300, 1024, 8192, 65536, 4194304))]\n    read_lengths: Histogram,\n\n    /// Number of times `read` function of the underlying specifier returned error (besides EAGAIN)\n    read_errors: IntCounter,\n\n    /// Number of times `read` function of the underlying specifier returned EAGAIN\n    read_wouldblocks: IntCounter,\n\n    /// Number of times `write` function of the underlying specifier returned error (besides EAGAIN)\n    write_errors: IntCounter,\n\n    /// Number of times `write` function of the underlying specifier returned EAGAIN\n    write_wouldblocks: IntCounter,\n\n    /// Distribution of byte lengths underlying `write` function successfully returned\n    #[metric(buckets(0, 1, 10, 50, 300, 1024, 8192, 65536, 4194304))]\n    write_lengths: Histogram,\n\n    /// durations it took to make a function call to underlying node for reading\n    #[metric(buckets(0.1e-3,1e-3,0.01,0.1,1,10))]\n    read_timings: Histogram,\n\n    /// durations it took to make a function call to underlying node for writing\n    #[metric(buckets(0.1e-3,1e-3,0.01,0.1,1,10))]\n    write_timings: Histogram,\n}\n\npub type HGlobalStats = Rc<GlobalStats>;\n\npub type GlobalState = (HGlobalStats, Rc<RefCell<Option<prometheus::HistogramTimer>>>);\n\nstruct Droppie {\n    w_msgs: AtomicU64,\n    r_msgs: AtomicU64,\n    w_bytes: AtomicU64,\n    r_bytes: AtomicU64,\n    session_timing: Option<prometheus::HistogramTimer>,\n    handle: HGlobalStats,\n}\n\nimpl Droppie {\n    fn new(handle: HGlobalStats) -> Droppie {\n        handle.connects.inc();\n        Droppie {\n            session_timing: Some(handle.session_durations.start_timer()),\n            handle: handle,\n            w_msgs: AtomicU64::new(0),\n            r_msgs: AtomicU64::new(0),\n            w_bytes: AtomicU64::new(0),\n            r_bytes: AtomicU64::new(0),\n        }\n    }\n}\n\nimpl Drop for Droppie {\n    fn drop(&mut self) {\n        let t = self.session_timing.take().unwrap().stop_and_record();\n        self.handle.session_r_bytes.observe(self.r_bytes.get() as f64);\n        self.handle.session_w_bytes.observe(self.w_bytes.get() as f64);\n        self.handle.session_r_msgs.observe(self.r_msgs.get() as f64);\n        self.handle.session_w_msgs.observe(self.w_msgs.get() as f64);\n        let r_avg_bps = self.r_bytes.get() as f64 / t;\n        let w_avg_bps = self.w_bytes.get() as f64 / t;\n        self.handle.session_avg_r_bps.observe(r_avg_bps);\n        self.handle.session_avg_w_bps.observe(w_avg_bps);\n        self.handle.disconnects.inc();\n\n    }\n}\n\npub fn new_global_stats() -> GlobalState {\n    (Rc::new(GlobalStats::new(prometheus::default_registry()).unwrap()), Rc::new(RefCell::new(None)))\n}\n\npub fn serve(psa: SocketAddr) -> crate::Result<()> {\n    let tcp = TcpListener::bind(&psa)?;\n    debug!(\"Listening TCP socket for Prometheus metrics\");\n\n    std::thread::spawn(move || {\n        for s in tcp.incoming() {\n            if let Ok(s) = s {\n                let mut s = std::io::BufWriter::new(s); \n                let stats = prometheus::default_registry().gather();\n                let header = get_static_file_reply(None, \"text/plain; version=0.0.4\");\n                let _ = s.write_all(&header[..]);\n                let _ = prometheus::TextEncoder::default().encode(&stats[..], &mut s);\n            }\n            std::thread::sleep(Duration::from_millis(5));\n        }\n    });\n\n    Ok(())\n}\n\n#[derive(Debug)]\npub struct Prometheus<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for Prometheus<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let stats: GlobalState = cp.global(new_global_stats).clone();\n        let inner = self.0.construct(cp.clone());\n\n        inner.map(move |p, _| prometheus_peer(p, stats.clone()))\n    }\n    specifier_boilerplate!(globalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = PrometheusClass,\n    target = Prometheus,\n    prefixes = [\"prometheus:\", \"metrics:\"],\n    arg_handling = subspec,\n    overlay = true,\n    MessageOriented,\n    MulticonnectnessDependsOnInnerType,\n    help = r#\"\n[A] Account connections, messages, bytes and other data and expose Prometheus metrics on a separate port.\n\nNot included by default, build a crate with `--features=prometheus_peer` to have it.\nYou can also use `--features=prometheus_peer,prometheus/process` to have additional metrics.\n\"#\n);\n\npub fn prometheus_peer(inner_peer: Peer, stats: GlobalState) -> BoxedNewPeerFuture {\n    let droppie = Droppie::new(stats.0);\n\n    // stops previous and start new timer\n    *stats.1.borrow_mut() = Some(droppie.handle.between_connects.start_timer());\n\n    let droppie = Rc::new(droppie);\n\n    let r = StatsWrapperR(inner_peer.0, droppie.clone());\n    let w = StatsWrapperW(inner_peer.1, droppie);\n    let thepeer = Peer::new(r, w, inner_peer.2);\n    Box::new(ok(thepeer)) as BoxedNewPeerFuture\n}\nstruct StatsWrapperR(Box<dyn AsyncRead>, Rc<Droppie>);\n\nimpl Read for StatsWrapperR {\n    fn read(&mut self, b: &mut [u8]) -> Result<usize, IoError> {\n        let timer = self.1.handle.read_timings.start_timer();\n        let ret = self.0.read(b);\n        timer.stop_and_record();\n        match &ret {\n            Ok(x) => {\n                self.1.handle.read_lengths.observe(*x as f64);\n                self.1.handle.r_msgs.inc();\n                self.1.handle.r_bytes.inc_by(*x as u64);\n                self.1.r_msgs.inc_by(1);\n                self.1.r_bytes.inc_by(*x as u64);\n            },\n            Err(e) if e.kind() == ErrorKind::WouldBlock => {\n                self.1.handle.read_wouldblocks.inc();\n            },\n            Err(_) => {\n                self.1.handle.read_errors.inc();\n            },\n        };\n\n        ret\n    }\n}\nimpl AsyncRead for StatsWrapperR {}\n\nstruct StatsWrapperW(Box<dyn AsyncWrite>, Rc<Droppie>);\n\nimpl Write for StatsWrapperW {\n    fn write(&mut self, b: &[u8]) -> Result<usize, IoError> {\n        let timer = self.1.handle.write_timings.start_timer();\n        let ret = self.0.write(b);\n        timer.stop_and_record();\n        \n        match &ret {\n            Ok(x) => {\n                self.1.handle.write_lengths.observe(*x as f64);\n                self.1.handle.w_msgs.inc();\n                self.1.handle.w_bytes.inc_by(*x as u64);\n                self.1.w_msgs.inc_by(1);\n                self.1.w_bytes.inc_by(*x as u64);\n            },\n            Err(e) if e.kind() == ErrorKind::WouldBlock => {\n                self.1.handle.write_wouldblocks.inc();\n            },\n            Err(_) => {\n                self.1.handle.write_errors.inc();\n            },\n        };\n\n        ret\n    }\n\n    fn flush(&mut self) -> std::io::Result<()> {\n        self.0.flush()\n    }\n}\nimpl AsyncWrite for StatsWrapperW {\n    fn shutdown(&mut self) -> std::result::Result<Async<()>, std::io::Error> {\n        self.0.shutdown()\n    }\n}\n"
  },
  {
    "path": "src/readdebt.rs",
    "content": "use std;\n\n#[derive(Debug, Clone, Copy)]\npub enum DebtHandling {\n    Silent,\n    Warn,\n    DropMessage,\n}\n\npub enum ZeroMessagesHandling {\n    Drop,\n    Deliver,\n}\n\npub enum ProcessMessageResult {\n    Return(std::result::Result<usize, std::io::Error>),\n    Recurse,\n}\n\n/// A `Read` utility to deal with partial reads\npub struct ReadDebt(pub Option<Vec<u8>>, pub DebtHandling, pub ZeroMessagesHandling);\nimpl ReadDebt {\n    pub fn process_message(&mut self, buf: &mut [u8], buf_in: &[u8]) -> ProcessMessageResult {\n        assert_eq!(self.0, None);\n        let mut l = buf_in.len();\n        if l > buf.len() {\n            match self.1 {\n                DebtHandling::Silent => (),\n                DebtHandling::Warn => {\n                    warn!(\"Incoming message too long ({} > {}): splitting it to parts.\\nUse -B option to increase buffer size or -S option to drop messages instead of splitting.\", l, buf.len());\n                }\n                DebtHandling::DropMessage => {\n                    error!(\"Dropping too large message ({} > {}). Use -B option to increase buffer size.\", l, buf.len());\n                    return ProcessMessageResult::Recurse;\n                }\n            }\n            l = buf.len();\n        }\n        buf[..l].copy_from_slice(&buf_in[..l]);\n\n        if l < buf_in.len() {\n            self.0 = Some(buf_in[l..].to_vec());\n        }\n\n        debug!(\"Fulfilling the debt of {} bytes\", l);\n        if l == 0 {\n            match self.2 {\n                ZeroMessagesHandling::Deliver => (),\n                ZeroMessagesHandling::Drop => {\n                    info!(\"Dropping incoming zero-length message\");\n                    return ProcessMessageResult::Recurse;\n                }\n            }\n        }\n        ProcessMessageResult::Return(Ok(l))\n    }\n    pub fn check_debt(\n        &mut self,\n        buf: &mut [u8],\n    ) -> Option<std::result::Result<usize, std::io::Error>> {\n        if let Some(debt) = self.0.take() {\n            match self.process_message(buf, debt.as_slice()) {\n                ProcessMessageResult::Return(x) => Some(x),\n                ProcessMessageResult::Recurse => unreachable!(),\n            }\n        } else {\n            None\n        }\n    }\n}\n"
  },
  {
    "path": "src/reconnect_peer.rs",
    "content": "extern crate futures;\nextern crate tokio_io;\n\nuse futures::future::ok;\nuse std::cell::RefCell;\nuse std::rc::Rc;\n\nuse super::{BoxedNewPeerFuture, Peer};\n\nuse std::io::{Error as IoError, Read, Write};\nuse tokio_io::{AsyncRead, AsyncWrite};\n\nuse super::{once, simple_err, wouldblock, ConstructParams, PeerConstructor, Specifier};\nuse futures::{Async, Future, Poll};\n\n// TODO: shutdown write part if out writing part is shut down\n// TODO: stop if writing part and reading parts are closed (shutdown)?\n\n#[derive(Debug)]\npub struct AutoReconnect(pub Rc<dyn Specifier>);\nimpl Specifier for AutoReconnect {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        once(autoreconnector(self.0.clone(), cp))\n    }\n    specifier_boilerplate!(singleconnect noglobalstate has_subspec );\n    self_0_is_subspecifier!(...);\n}\nspecifier_class!(\n    name = AutoReconnectClass,\n    target = AutoReconnect,\n    prefixes = [\"autoreconnect:\"],\n    arg_handling = subspec,\n    overlay = true,\n    MessageBoundaryStatusDependsOnInnerType,\n    SingleConnect,\n    help = r#\"\nRe-establish underlying connection on any error or EOF\n\nExample: keep connecting to the port or spin 100% CPU trying if it is closed.\n\n    websocat - autoreconnect:tcp:127.0.0.1:5445\n    \nExample: keep remote logging connection open (or flood the host if port is closed):\n\n    websocat -u ws-l:0.0.0.0:8080 reuse:autoreconnect:tcp:192.168.0.3:1025\n  \nTODO: implement delays between reconnect attempts\n\"#\n);\n\n#[derive(Default)]\nstruct State2 {\n    already_warned: bool,\n}\n\nstruct State {\n    s: Rc<dyn Specifier>,\n    p: Option<Peer>,\n    n: Option<BoxedNewPeerFuture>,\n    cp: ConstructParams,\n    aux: State2,\n    reconnect_delay: std::time::Duration,\n    ratelimiter: Option<tokio_timer::Delay>,\n    reconnect_count_limit: Option<usize>,\n    /// Do not initiate connection now, return not ready outcome instead\n    pegged_until_write: bool,\n}\n\n/// This implementation's poll is to be reused many times, both after returning item and error\nimpl State {\n    //type Item = &'mut Peer;\n    //type Error = Box<::std::error::Error>;\n\n    fn poll(&mut self) -> Poll<&mut Peer, Box<dyn (::std::error::Error)>> {\n        let pp = &mut self.p;\n        let nn = &mut self.n;\n\n        let aux = &mut self.aux;\n\n        loop {\n            if let Some(delay) = self.ratelimiter.as_mut() {\n                match delay.poll() {\n                    Ok(Async::Ready(_)) => {\n                        debug!(\"Waited for reconnect\");\n                        self.ratelimiter = None;\n                    }\n                    Err(e) => error!(\"tokio-timer's Delay: {}\", e),\n                    Ok(Async::NotReady) => return Ok(Async::NotReady),\n                }\n            }\n            if let Some(ref mut p) = *pp {\n                return Ok(Async::Ready(p));\n            }\n            let cp = self.cp.clone();\n\n            // Peer is not present: trying to create a new one\n\n            if self.pegged_until_write {\n                return Ok(Async::NotReady);\n            }\n            if self.reconnect_count_limit == Some(0) {\n                info!(\"autoreconnector reconnect limit reached. Failing connection.\");\n                return Err(Box::new(simple_err(\"No more connections allowed\".to_owned())));\n            }\n\n            if let Some(mut bnpf) = nn.take() {\n                match bnpf.poll() {\n                    Ok(Async::Ready(p)) => {\n                        *pp = Some(p);\n\n                        if let Some(ref mut cl) = self.reconnect_count_limit {\n                            *cl -= 1;\n                        }\n\n                        continue;\n                    }\n                    Ok(Async::NotReady) => {\n                        *nn = Some(bnpf);\n                        return Ok(Async::NotReady);\n                    }\n                    Err(_x) => {\n                        // Stop on error:\n                        //return Err(_x);\n\n                        if let Some(ref mut cl) = self.reconnect_count_limit {\n                            *cl -= 1;\n                        }\n\n                        // Just reconnect again on error\n\n                        if !aux.already_warned {\n                            aux.already_warned = true;\n                            warn!(\"Reconnecting failed. Further failed reconnects announcements will have lower log severity.\");\n                        } else {\n                            info!(\"Reconnecting failed.\");\n                        }\n\n                        self.ratelimiter = Some(tokio_timer::Delay::new(std::time::Instant::now() + self.reconnect_delay));\n                        continue;\n                    }\n                }\n            }\n            let l2r = cp.left_to_right.clone();\n            let pc: PeerConstructor = self.s.construct(cp);\n            *nn = Some(pc.get_only_first_conn(l2r));\n        }\n    }\n}\n\n#[derive(Clone)]\nstruct PeerHandle(Rc<RefCell<State>>);\n\nmacro_rules! getpeer {\n    ($state:ident -> $p:ident) => {\n        let $p: &mut Peer = match $state.poll() {\n            Ok(Async::Ready(p)) => p,\n            Ok(Async::NotReady) => return wouldblock(),\n            Err(e) => {\n                return Err(simple_err(format!(\"{}\", e)));\n            }\n        };\n    };\n}\n\nimpl State {\n    fn reconnect(&mut self) {\n        info!(\"Reconnect\");\n        self.p = None;\n    }\n}\n\nmacro_rules! main_loop {\n    ($state:ident, $p:ident,bytes $e:expr) => {\n        main_loop!(qqq $state, $p, do_reconnect, {\n                                    match $e {\n                                        Ok(0) => { do_reconnect = true; }\n                                        Err(e) => {\n                                            if e.kind() == ::std::io::ErrorKind::WouldBlock {\n                                                return Err(e);\n                                            }\n                                            warn!(\"{}\", e);\n                                            do_reconnect = true;\n                                        }\n                                        Ok(x) => return Ok(x),\n                                    }\n                                });\n    };\n    ($state:ident, $p:ident,none $e:expr) => {\n        main_loop!(qqq $state, $p, do_reconnect, {\n                                    match $e {\n                                        Err(e) => {\n                                            if e.kind() == ::std::io::ErrorKind::WouldBlock {\n                                                return Err(e);\n                                            }\n                                            warn!(\"{}\", e);\n                                            do_reconnect = true;\n                                        }\n                                        Ok(()) => return Ok(()),\n                                    }\n                                });\n    };\n    (qqq $state:ident, $p:ident, $do_reconnect:ident, $the_match:expr) => {\n        let mut $do_reconnect = false;\n        loop {\n            if $do_reconnect {\n                $state.reconnect();\n                $do_reconnect = false;\n            } else {\n                getpeer!($state -> $p);\n                $the_match\n            }\n        }\n    };\n}\n\nimpl Read for PeerHandle {\n    fn read(&mut self, b: &mut [u8]) -> Result<usize, IoError> {\n        let mut state = self.0.borrow_mut();\n        main_loop!(state, p, bytes p.0.read(b));\n    }\n}\nimpl AsyncRead for PeerHandle {}\n\nimpl Write for PeerHandle {\n    fn write(&mut self, b: &[u8]) -> Result<usize, IoError> {\n        let mut state = self.0.borrow_mut();\n        state.pegged_until_write = false;\n        main_loop!(state, p, bytes p.1.write(b));\n    }\n    fn flush(&mut self) -> Result<(), IoError> {\n        let mut state = self.0.borrow_mut();\n        main_loop!(state, p, none p.1.flush());\n    }\n}\nimpl AsyncWrite for PeerHandle {\n    fn shutdown(&mut self) -> futures::Poll<(), IoError> {\n        let mut state = self.0.borrow_mut();\n        state.p = None;\n        Ok(Async::Ready(()))\n    }\n}\n\npub fn autoreconnector(s: Rc<dyn Specifier>, cp: ConstructParams) -> BoxedNewPeerFuture {\n    let reconnect_delay = std::time::Duration::from_millis(cp.program_options.autoreconnect_delay_millis);\n    let s = Rc::new(RefCell::new(State {\n        cp,\n        s,\n        p: None,\n        n: None,\n        aux: Default::default(),\n        reconnect_delay,\n        ratelimiter: None,\n        reconnect_count_limit: None,\n        pegged_until_write: false,\n    }));\n    let ph1 = PeerHandle(s.clone());\n    let ph2 = PeerHandle(s);\n    let peer = Peer::new(ph1, ph2, None /* we handle hups ourselves */);\n    Box::new(ok(peer)) as BoxedNewPeerFuture\n}\n\n\npub fn waitfordata(s: Rc<dyn Specifier>, cp: ConstructParams) -> BoxedNewPeerFuture {\n    let reconnect_delay = std::time::Duration::from_millis(cp.program_options.autoreconnect_delay_millis);\n    let s = Rc::new(RefCell::new(State {\n        cp,\n        s,\n        p: None,\n        n: None,\n        aux: Default::default(),\n        reconnect_delay, // unused\n        ratelimiter: None,\n        reconnect_count_limit: Some(1),\n        pegged_until_write: true,\n    }));\n    let ph1 = PeerHandle(s.clone());\n    let ph2 = PeerHandle(s);\n    let peer = Peer::new(ph1, ph2, None /* we handle hups ourselves, though shouldn't probably */);\n    Box::new(ok(peer)) as BoxedNewPeerFuture\n}\n\n\n#[derive(Debug)]\npub struct WaitForData(pub Rc<dyn Specifier>);\nimpl Specifier for WaitForData {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        once(waitfordata(self.0.clone(), cp))\n    }\n    specifier_boilerplate!(singleconnect has_subspec globalstate);\n    self_0_is_subspecifier!(...);\n}\n\nspecifier_class!(\n    name = WaitForDataClass,\n    target = WaitForData,\n    prefixes = [\"waitfordata:\", \"wait-for-data:\"],\n    arg_handling = subspec,\n    overlay = true,\n    MessageBoundaryStatusDependsOnInnerType,\n    SingleConnect,\n    help = r#\"\nWait for some data to pending being written before starting connecting. [A]\n\nExample: Connect to the TCP server on the left side immediately, but connect to\nthe TCP server on the right side only after some data gets written by the first connection\n\n\n    websocat -b tcp:127.0.0.1:1234 waitfordata:tcp:127.0.0.1:1235\n\nExample: Connect to first WebSocket server, wait for some incoming WebSocket message, then\nconnect to the second WebSocket server and start exchanging text and binary WebSocket messages\nbetween them.\n\n    websocat -b --binary-prefix=b --text-prefix=t ws://127.0.0.1:1234 waitfordata:ws://127.0.0.1:1235/\n\"#\n);\n"
  },
  {
    "path": "src/sessionserve.rs",
    "content": "use super::futures::{Future, Stream};\nuse super::{\n    futures, my_copy, ConstructParams, L2rUser, L2rWriter, Options, Peer, PeerConstructor,\n    ProgramState, Session, Specifier, Transfer,\n};\nuse crate::spawn_hack;\nuse std;\nuse std::cell::RefCell;\nuse std::rc::Rc;\nuse tokio_io;\n\nimpl Session {\n    pub fn run(self) -> Box<dyn Future<Item = (), Error = Box<dyn std::error::Error>>> {\n        let once = self.opts.one_message;\n        let mut co1 = my_copy::CopyOptions {\n            stop_on_reader_zero_read: !self.opts.no_exit_on_zeromsg,\n            once,\n            buffer_size: self.opts.buffer_size,\n            skip: false,\n            max_ops: self.opts.max_messages,\n        };\n        let mut co2 = co1;\n        co2.max_ops = self.opts.max_messages_rev;\n        if self.opts.unidirectional {\n            co2.skip=true;\n        }\n        if self.opts.unidirectional_reverse {\n            co1.skip=true;\n        }\n        let f1 = my_copy::copy(self.t1.from, self.t1.to, co1, self.opts.preamble.clone());\n        let f2 = my_copy::copy(self.t2.from, self.t2.to, co2, self.opts.preamble_reverse.clone());\n\n        let f1 = f1.and_then(|(_, r, w)| {\n            info!(\"Forward finished\");\n            std::mem::drop(r);\n            tokio_io::io::shutdown(w).map(|w| {\n                debug!(\"Forward shutdown finished\");\n                std::mem::drop(w);\n            })\n        });\n        let f2 = f2.and_then(|(_, r, w)| {\n            info!(\"Reverse finished\");\n            std::mem::drop(r);\n            tokio_io::io::shutdown(w).map(|w| {\n                debug!(\"Reverse shutdown finished\");\n                std::mem::drop(w);\n            })\n        });\n\n        type Ret = Box<dyn Future<Item = (), Error = Box<dyn std::error::Error>>>;\n        let tmp = if !self.opts.exit_on_eof {\n            Box::new(\n                f1.join(f2)\n                    .map(|(_, _)| {\n                        info!(\"Both directions finished\");\n                    })\n                    .map_err(|x| Box::new(x) as Box<dyn std::error::Error>),\n            ) as Ret\n        } else {\n            Box::new(\n                f1.select(f2)\n                    .map(|(_, _)| {\n                        info!(\"One of directions finished\");\n                    })\n                    .map_err(|(x, _)| Box::new(x) as Box<dyn std::error::Error>),\n            ) as Ret\n        };\n        // tmp is now everything except of HUP handling\n        if self.hup1.is_none() && self.hup2.is_none() {\n            tmp // no need for complications\n        } else {\n            let mut s = futures::stream::futures_unordered::FuturesUnordered::new();\n            s.push(tmp);\n            if let Some(hup) = self.hup1 {\n                s.push(hup);\n            }\n            if let Some(hup) = self.hup2 {\n                s.push(hup);\n            }\n            Box::new(\n                s.into_future()\n                .map(|(x, _)|x.unwrap())\n                .map_err(|(e,_)|e)\n            ) as Ret\n        }\n    }\n    pub fn new(peer1: Peer, peer2: Peer, opts: Rc<Options>) -> Self {\n        Session{\n            t1: Transfer {\n                from: peer1.0,\n                to: peer2.1,\n            },\n            t2: Transfer {\n                from: peer2.0,\n                to: peer1.1,\n            },\n            opts,\n            hup1: peer1.2,\n            hup2: peer2.2,\n        }\n    }\n}\n\nfn l2r_new() -> L2rWriter {\n    Rc::new(RefCell::new(Default::default()))\n}\n\npub fn serve<OE>(\n    s1: Rc<dyn Specifier>,\n    s2: Rc<dyn Specifier>,\n    opts: Options,\n    onerror: std::rc::Rc<OE>,\n) -> impl Future<Item = (), Error = ()>\nwhere\n    OE: Fn(Box<dyn std::error::Error>) + 'static,\n{\n    futures::future::ok(()).and_then(|()| serve_impl(s1, s2, opts, onerror))\n}\n\n#[allow(clippy::needless_pass_by_value)]\nfn serve_impl<OE>(\n    s1: Rc<dyn Specifier>,\n    s2: Rc<dyn Specifier>,\n    opts: Options,\n    onerror: std::rc::Rc<OE>,\n) -> Box<dyn Future<Item = (), Error = ()>>\nwhere\n    OE: Fn(Box<dyn std::error::Error>) + 'static,\n{\n    debug!(\"Serving {:?} to {:?} with {:?}\", s1, s2, opts);\n    let ps = Rc::new(RefCell::new(ProgramState::default()));\n\n    use crate::PeerConstructor::{Overlay1, OverlayM, ServeMultipleTimes, ServeOnce};\n\n    let e1 = onerror.clone();\n    let e2 = onerror.clone();\n    let e3 = onerror.clone();\n\n    let opts1 = Rc::new(opts);\n    let opts2 = opts1.clone();\n\n    let l2r = l2r_new();\n\n    let cp = Rc::new(RefCell::new(ConstructParams {\n        program_options: opts1.clone(),\n        global_state: ps.clone(),\n        left_to_right: L2rUser::FillIn(l2r.clone()),\n    }));\n\n\n    #[cfg(feature = \"prometheus_peer\")]\n    {\n        if let Some(psa) = opts1.prometheus {\n            let _ /*: crate::prometheus_peer::GlobalState*/ = cp.as_ref().borrow().global(crate::prometheus_peer::new_global_stats);\n            if let Err(e) = crate::prometheus_peer::serve(psa) {\n                error!(\"Error listening Prometheus exposer socket: {}\", e);\n            }\n        }\n    }\n\n    let mut left = s1.construct(cp.borrow().clone());\n\n    if opts2.oneshot {\n        left =\n            PeerConstructor::ServeOnce(left.get_only_first_conn(cp.borrow().left_to_right.clone()));\n    }\n\n    let max_parallel_conns = opts1.max_parallel_conns;\n    let current_parallel_conns = Rc::new(::std::cell::Cell::new(0usize));\n\n    match left {\n        PeerConstructor::Error(e) => {\n            e1(e);\n            Box::new(futures::future::ok(())) as Box<dyn Future<Item = (), Error = ()>>\n        },\n        ServeMultipleTimes(stream) => {\n            let runner = stream\n                .map(move |peer1| {\n                    let mut cpc = current_parallel_conns.get();\n                    let cpc2 = current_parallel_conns.clone();\n                    cpc += 1;\n                    if let Some(cap) = max_parallel_conns {\n                        if cpc > cap {\n                            warn!(\"Dropping connection because of connection cap\");\n                            return;\n                        }\n                    }\n                    info!(\"Serving {} ongoing connections\", cpc);\n                    current_parallel_conns.set(cpc);\n\n                    let opts3 = opts2.clone();\n                    let e1_1 = e1.clone();\n                    let cp2 = cp.borrow().reply();\n                    cp.borrow_mut().reset_l2r();\n                    let l2rc = cp2.left_to_right.clone();\n                    spawn_hack(\n                        s2.construct(cp2)\n                            .get_only_first_conn(l2rc)\n                            .and_then(move |peer2| {\n                                let s = Session::new(peer1, peer2, opts3);\n                                s.run()\n                            })\n                            .map_err(move |e| e1_1(e))\n                            .then(move |r| {\n                                cpc2.set(cpc2.get() - 1);\n                                futures::future::result(r)\n                            }),\n                    )\n                })\n                .for_each(|()| futures::future::ok(()));\n            Box::new(runner.map_err(move |e| e2(e))) as Box<dyn Future<Item = (), Error = ()>>\n        }\n        OverlayM(stream, mapper) => {\n            let runner = stream\n                .map(move |peer1_| {\n                    debug!(\"Underlying connection established\");\n\n                    let mut cpc = current_parallel_conns.get();\n                    let cpc2 = current_parallel_conns.clone();\n                    cpc += 1;\n                    if let Some(cap) = max_parallel_conns {\n                        if cpc > cap {\n                            warn!(\"Dropping connection because of connection cap\");\n                            return;\n                        }\n                    }\n                    info!(\"Serving {} ongoing connections\", cpc);\n                    current_parallel_conns.set(cpc);\n\n                    let cp_ = cp.borrow().deep_clone();\n                    cp.borrow_mut().reset_l2r();\n                    let opts3 = opts2.clone();\n                    let e1_1 = e1.clone();\n                    let s2 = s2.clone();\n                    let l2rc = cp_.left_to_right.clone();\n                    spawn_hack(\n                        mapper(peer1_, l2rc)\n                            .and_then(move |peer1| {\n                                let cp2 = cp_.reply();\n                                let l2rc = cp2.left_to_right.clone();\n                                s2.construct(cp2)\n                                    .get_only_first_conn(l2rc)\n                                    .and_then(move |peer2| {\n                                        let s = Session::new(peer1, peer2, opts3);\n                                        s.run()\n                                    })\n                            })\n                            .map_err(move |e| e1_1(e))\n                            .then(move |r| {\n                                cpc2.set(cpc2.get() - 1);\n                                futures::future::result(r)\n                            }),\n                    )\n                })\n                .for_each(|()| futures::future::ok(()));\n            Box::new(runner.map_err(move |e| e2(e))) as Box<dyn Future<Item = (), Error = ()>>\n        }\n        ServeOnce(peer1c) => {\n            let runner = peer1c.and_then(move |peer1| {\n                let cp2 = cp.borrow().reply();\n                let l2rc = cp2.left_to_right.clone();\n                let right = s2.construct(cp2);\n                let fut = right.get_only_first_conn(l2rc);\n                fut.and_then(move |peer2| {\n                    let s = Session::new(peer1, peer2, opts2);\n                    s.run().map(|()| {\n                        ::std::mem::drop(ps)\n                        // otherwise ps will be dropped sooner\n                        // and stdin/stdout may become blocking sooner\n                    })\n                })\n            });\n            Box::new(runner.map_err(move |e| e3(e))) as Box<dyn Future<Item = (), Error = ()>>\n        }\n        Overlay1(peer1c, mapper) => {\n            let runner = peer1c.and_then(move |peer1_| {\n                let l2rc = cp.borrow().left_to_right.clone();\n                debug!(\"Underlying connection established\");\n                mapper(peer1_, l2rc).and_then(move |peer1| {\n                    let cp2 = cp.borrow().reply();\n                    let l2rc = cp2.left_to_right.clone();\n                    let right = s2.construct(cp2);\n                    let fut = right.get_only_first_conn(l2rc);\n                    fut.and_then(move |peer2| {\n                        let s = Session::new(peer1, peer2, opts2);\n                        s.run().map(|()| {\n                            ::std::mem::drop(ps)\n                            // otherwise ps will be dropped sooner\n                            // and stdin/stdout may become blocking sooner\n                        })\n                    })\n                })\n            });\n            Box::new(runner.map_err(move |e| e3(e))) as Box<dyn Future<Item = (), Error = ()>>\n        }\n    }\n}\n"
  },
  {
    "path": "src/socks5_peer.rs",
    "content": "#![allow(clippy::needless_pass_by_value,clippy::cast_lossless,clippy::identity_op)]\nuse futures::future::{err, ok, Future};\n\nuse std::rc::Rc;\n\nuse super::{box_up_err, peer_strerr, BoxedNewPeerFuture, Peer};\nuse super::{ConstructParams, L2rUser, PeerConstructor, Specifier};\nuse tokio_io::io::{read_exact, write_all};\nuse std::io::Write;\nuse std::net::{IpAddr, Ipv4Addr};\nuse tokio_io::{AsyncRead, AsyncWrite};\n\nuse std::ffi::OsString;\n\n#[derive(Debug, Clone)]\npub enum SocksHostAddr {\n    Ip(IpAddr),\n    Name(String),\n}\n\n#[derive(Debug, Clone)]\npub struct SocksSocketAddr {\n    pub host: SocksHostAddr,\n    pub port: u16,\n}\n\n#[derive(Debug)]\npub struct SocksProxy<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for SocksProxy<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(cp.clone());\n        inner.map(move |p, l2r| {\n            socks5_peer(p, l2r, false, None, &cp.program_options.socks_destination, cp.program_options.socks5_user_pass.clone(), false)\n        })\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = SocksProxyClass,\n    target = SocksProxy,\n    prefixes = [\"socks5-connect:\"],\n    arg_handling = subspec,\n    overlay = true,\n    StreamOriented,\n    MulticonnectnessDependsOnInnerType,\n    help = r#\"\nSOCKS5 proxy client (raw) [A]\n\nExample: connect to a websocket using local `ssh -D` proxy\n\n    websocat -t - ws-c:socks5-connect:tcp:127.0.0.1:1080 --socks5-destination echo.websocket.org:80 --ws-c-uri ws://echo.websocket.org\n\nFor a user-friendly solution, see --socks5 command-line option\n\"#\n);\n\n#[derive(Debug)]\npub struct SocksBind<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for SocksBind<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(cp.clone());\n        inner.map(move |p, l2r| {\n            socks5_peer(\n                p,\n                l2r,\n                true,\n                cp.program_options.socks5_bind_script.clone(),\n                &cp.program_options.socks_destination,\n                cp.program_options.socks5_user_pass.clone(),\n                cp.program_options.announce_listens,\n            )\n        })\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = SocksBindClass,\n    target = SocksBind,\n    prefixes = [\"socks5-bind:\"],\n    arg_handling = subspec,\n    overlay = true,\n    StreamOriented,\n    MulticonnectnessDependsOnInnerType,\n    help = r#\"\nSOCKS5 proxy client (raw, bind command) [A]\n\nExample: bind to a websocket using some remote SOCKS server\n\n    websocat -v -t ws-u:socks5-bind:tcp:132.148.129.183:14124 - --socks5-destination 255.255.255.255:65535\n\nNote that port is typically unpredictable. Use --socks5-bind-script option to know the port.\nSee an example in moreexamples.md for more thorough example.\n\"#\n);\n\ntype RSRRet =\n    Box<dyn Future<Item = (SocksSocketAddr, Peer), Error = Box<dyn (::std::error::Error)>>>;\nfn read_socks_reply(p: Peer) -> RSRRet {\n    let (r, w, hup) = (p.0, p.1, p.2);\n    let reply = [0; 4];\n\n    fn myerr(x: &'static str) -> RSRRet {\n        Box::new(err(x.to_string().into()))\n    }\n\n    Box::new(\n        read_exact(r, reply)\n            .map_err(box_up_err)\n            .and_then(move |(r, reply)| {\n                if reply[0] != b'\\x05' {\n                    return myerr(\"Not a SOCKS5 reply 2\");\n                }\n                if reply[1] != b'\\x00' {\n                    let msg = match reply[1] {\n                        1 => \"SOCKS: General SOCKS server failure\",\n                        2 => \"SOCKS connection not allowed\",\n                        3 => \"SOCKS: network unreachable\",\n                        4 => \"SOCKS: host unreachable\",\n                        5 => \"SOCKS: connection refused\",\n                        6 => \"SOCKS: TTL expired\",\n                        7 => \"SOCKS: Command not supported\",\n                        8 => \"SOCKS: Address type not supported\",\n                        _ => \"SOCKS: Unknown failure\",\n                    };\n                    return myerr(msg);\n                }\n                \n                let ret: RSRRet = match reply[3] {\n                    b'\\x01' => {\n                        // ipv4\n                        let addrport = [0; 4 + 2];\n                        Box::new(read_exact(r, addrport).map_err(box_up_err).and_then(\n                            move |(r, addrport)| {\n                                let port = (addrport[4] as u16) * 256 + (addrport[5] as u16);\n                                let ip = Ipv4Addr::new(\n                                    addrport[0],\n                                    addrport[1],\n                                    addrport[2],\n                                    addrport[3],\n                                );\n                                let host = SocksHostAddr::Ip(IpAddr::V4(ip));\n                                ok((SocksSocketAddr { host, port }, Peer(r, w, hup)))\n                            },\n                        ))\n                    }\n                    b'\\x04' => {\n                        // ipv6\n                        let addrport = [0; 16 + 2];\n                        Box::new(read_exact(r, addrport).map_err(box_up_err).and_then(\n                            move |(r, addrport)| {\n                                let port = (addrport[16] as u16) * 256 + (addrport[17] as u16);\n                                // still not worth to switch to Cargo.toml to add\n                                // \"bytes\" dependency, then scroll up for \"extern crate\",\n                                // then look up docs again to find out where to get that BE thing.\n                                let mut ip = [0u8; 16];\n                                ip.copy_from_slice(&addrport[0..16]);\n                                let host = SocksHostAddr::Ip(IpAddr::V6(ip.into()));\n                                ok((SocksSocketAddr { host, port }, Peer(r, w, hup)))\n                            },\n                        ))\n                    }\n                    b'\\x03' => {\n                        let alen = [0; 1];\n                        Box::new(read_exact(r, alen).map_err(box_up_err).and_then(\n                            move |(r, alen)| {\n                                let alen = alen[0] as usize;\n                                let addrport = vec![0; alen + 2];\n\n                                read_exact(r, addrport).map_err(box_up_err).and_then(\n                                    move |(r, addrport)| {\n                                        let port = (addrport[alen] as u16) * 256\n                                            + (addrport[alen + 1] as u16);\n                                        let host = SocksHostAddr::Name(\n                                            ::std::str::from_utf8(&addrport[0..alen])\n                                                .unwrap_or(\"(invalid hostname)\")\n                                                .to_string(),\n                                        );\n                                        ok((SocksSocketAddr { host, port }, Peer(r, w, hup)))\n                                    },\n                                )\n                            },\n                        ))\n                    }\n                    _ => {\n                        return myerr(\"SOCKS: bound address type is unknown\");\n                    }\n                };\n                ret\n            }),\n    )\n}\n\npub fn socks5_peer(\n    inner_peer: Peer,\n    _l2r: L2rUser,\n    do_bind: bool,\n    bind_script: Option<OsString>,\n    socks_destination: &Option<SocksSocketAddr>,\n    socks5_user_pass: Option<String>,\n    announce_listen: bool,\n) -> BoxedNewPeerFuture {\n    let (desthost, destport) = if let Some(ref sd) = *socks_destination {\n        (sd.host.clone(), sd.port)\n    } else {\n        return peer_strerr(\n            \"--socks5-destination is required for socks5-connect: or socks5-bind: overlays\",\n        );\n    };\n\n    if let SocksHostAddr::Name(ref n) = desthost {\n        if n.len() > 255 {\n            return peer_strerr(\"Destination host name too long for SOCKS5\");\n        }\n    };\n\n    info!(\"Connecting to SOCKS server\");\n    let (r, w, hup) = (inner_peer.0, inner_peer.1, inner_peer.2);\n    let f = write_all(w, b\"\\x05\\x02\\x00\\x02\")\n        .map_err(box_up_err)\n        .and_then(move |(w, _)| {\n            read_exact(r, [0; 2])\n                .map_err(box_up_err)\n                .and_then(move |(r, reply)| {\n                    if reply[0] != 0x05 {\n                        return peer_strerr(\"Not a SOCKS5 reply\");\n                    }\n\n                    let auth_future: BoxedNewPeerFuture = match reply[1] {\n                        0x00 => {\n                            info!(\"SOCKS5: Auth method 0, no auth\");\n                            authenticate_no_auth(r, w)\n                        }\n                        0x02 => {\n                            authenticate_username_password(r, w, &socks5_user_pass)\n                        }\n                        _ => {\n                            return peer_strerr(\"SOCKS5: Unknown authentication method\");\n                        }\n                    };\n\n                    Box::new(auth_future.and_then(move |Peer(r, w, _)| {\n                        let request = build_socks5_request(do_bind, &desthost, destport).unwrap();\n                        Box::new(\n                            write_all(w, request)\n                                .map_err(box_up_err)\n                                .and_then(move |(w, _)| {\n                                    read_socks_reply(Peer(r, w, hup)).and_then(move |(addr, Peer(r, w, hup))| {\n                                        info!(\"SOCKS5 connect/bind: {:?}\", addr);\n                                        \n                                        if do_bind {\n                                            if announce_listen {\n                                                println!(\"LISTEN proto=tcp,port={}\", addr.port);\n                                            }\n                                            if let Some(bs) = bind_script {\n                                                let _ = ::std::process::Command::new(bs)\n                                                    .arg(format!(\"{}\", addr.port))\n                                                    .spawn();\n                                            }\n        \n                                            Box::new(read_socks_reply(Peer(r, w, hup)).and_then(move |(addr, Peer(r, w, hup))| {\n                                                info!(\"SOCKS5 remote connected: {:?}\", addr);\n                                                Box::new(ok(Peer(r, w, hup)))\n                                            }))\n                                                as BoxedNewPeerFuture\n                                        } else {\n                                            Box::new(ok(Peer(r, w, hup))) as BoxedNewPeerFuture\n                                        }\n                                    })\n                                }),\n                        )\n                    }))\n                })\n        });\n\n    Box::new(f) as BoxedNewPeerFuture\n}\n\nfn authenticate_no_auth(\n    r: Box<dyn AsyncRead>,\n    w: Box<dyn AsyncWrite>\n) -> BoxedNewPeerFuture {\n    Box::new(ok(Peer(r, w, None)))\n}\n\nfn authenticate_username_password(\n    r: Box<dyn AsyncRead>,\n    w: Box<dyn AsyncWrite>,\n    socks5_user_pass: &Option<String>,\n) -> BoxedNewPeerFuture {\n    info!(\"SOCKS5: Auth method 2, sending username/password\");\n\n    let (user, pass) = match socks5_user_pass {\n        Some(ref user_pass) => {\n            let parts: Vec<&str> = user_pass.splitn(2, ':').collect();\n            if parts.len() != 2 {\n                return peer_strerr(\"SOCKS5: Invalid username:password format\");\n            }\n            (parts[0].as_bytes(), parts[1].as_bytes())\n        },\n        None => return peer_strerr(\"SOCKS5: Username and password required but not provided\"),\n    };\n    let mut buffer = Vec::with_capacity(1 + 2 + 1 + user.len() + 1 + pass.len());\n    buffer.write_all(&[0x01]).unwrap(); // Version\n    buffer.write_all(&[user.len() as u8]).unwrap();\n    buffer.write_all(user).unwrap();\n    buffer.write_all(&[pass.len() as u8]).unwrap();\n    buffer.write_all(pass).unwrap();\n\n    Box::new(\n        write_all(w, buffer)\n            .map_err(box_up_err)\n            .and_then(move |(w, _)| {\n                read_exact(r, [0; 2])\n                    .map_err(box_up_err)\n                    .and_then(move |(r, reply)| {\n                        if reply[0] != 0x01 || reply[1] != 0x00 {\n                            return peer_strerr(\"SOCKS5: Authentication failed\");\n                        }\n                        Box::new(ok(Peer(\n                            Box::new(r) as Box<dyn AsyncRead>,\n                            Box::new(w) as Box<dyn AsyncWrite>,\n                            None,\n                        )))\n                    })\n            }),\n    )\n}\n\nfn build_socks5_request(\n    do_bind: bool,\n    desthost: &SocksHostAddr,\n    destport: u16,\n) -> Result<Vec<u8>, Box<dyn std::error::Error>> {\n    let mut request = Vec::with_capacity(20);\n    if do_bind {\n        request.write_all(&[0x05, 0x02, 0x00])?; // SOCKS5, CMD BIND\n    } else {\n        request.write_all(&[0x05, 0x01, 0x00])?; // SOCKS5, CMD CONNECT\n    }\n\n    match desthost {\n        SocksHostAddr::Ip(IpAddr::V4(ip4)) => {\n            request.write_all(&[0x01])?; // ATYP IPv4\n            request.write_all(&ip4.octets())?;\n        }\n        SocksHostAddr::Ip(IpAddr::V6(ip6)) => {\n            request.write_all(&[0x04])?; // ATYP IPv6\n            request.write_all(&ip6.octets())?;\n        }\n        SocksHostAddr::Name(name) => {\n            request.write_all(&[0x03])?; // ATYP DOMAINNAME\n            request.write_all(&[name.len() as u8])?;\n            request.write_all(name.as_bytes())?;\n        }\n    }\n\n    request.write_all(&[(destport >> 8) as u8, (destport & 0xFF) as u8])?;\n    Ok(request)\n}\n"
  },
  {
    "path": "src/specifier.rs",
    "content": "use super::{L2rUser, Options, Result};\nuse super::{PeerConstructor, ProgramState};\nuse std;\nuse std::cell::RefCell;\nuse std::rc::Rc;\n\npub enum ClassMessageBoundaryStatus {\n    StreamOriented,\n    MessageOriented,\n    MessageBoundaryStatusDependsOnInnerType,\n}\n\npub enum ClassMulticonnectStatus {\n    MultiConnect,\n    SingleConnect,\n    MulticonnectnessDependsOnInnerType,\n}\n\n/// A trait for a each specified type's accompanying object\n///\n/// Don't forget to register each instance at the `list_of_all_specifier_classes` macro.\npub trait SpecifierClass: std::fmt::Debug {\n    /// The primary name of the class\n    fn get_name(&self) -> &'static str;\n    /// Names to match command line parameters against, with a `:` colon if needed\n    fn get_prefixes(&self) -> Vec<&'static str>;\n    /// --long-help snippet about this specifier\n    fn help(&self) -> &'static str;\n    /// Given the command line text, construct the specifier\n    /// arg is what comes after the colon (e.g. `//echo.websocket.org` in `ws://echo.websocket.org`)\n    fn construct(&self, arg: &str) -> Result<Rc<dyn Specifier>>;\n    /// Given the inner specifier, construct this specifier.\n    fn construct_overlay(&self, inner: Rc<dyn Specifier>) -> Result<Rc<dyn Specifier>>;\n    /// Returns if this specifier is an overlay\n    fn is_overlay(&self) -> bool;\n    /// True if it is not expected to preserve message boundaries on reads\n    fn message_boundary_status(&self) -> ClassMessageBoundaryStatus;\n\n    fn multiconnect_status(&self) -> ClassMulticonnectStatus;\n    /// If it is Some then is_overlay, construct and most other things are ignored and prefix get replaced...\n    fn alias_info(&self) -> Option<&'static str>;\n}\n\nmacro_rules! specifier_alias {\n    (name=$n:ident,\n            prefixes=[$($p:expr),*],\n            alias=$x:expr,\n            help=$h:expr) => {\n        #[derive(Debug,Default)]\n        pub struct $n;\n        impl $crate::SpecifierClass for $n {\n            fn get_name(&self) -> &'static str { stringify!($n) }\n            fn get_prefixes(&self) -> Vec<&'static str> { vec![$($p),*] }\n            fn help(&self) -> &'static str { $h }\n            fn message_boundary_status(&self) -> $crate::ClassMessageBoundaryStatus {\n                panic!(\"Error: message_boundary_status called on alias class\")\n            }\n            fn multiconnect_status(&self) -> $crate::ClassMulticonnectStatus {\n                panic!(\"Error: multiconnect_status called on alias class\")\n            }\n            fn is_overlay(&self) -> bool {\n                false\n            }\n            fn construct(&self, _arg:&str) -> $crate::Result<Rc<dyn Specifier>> {\n                panic!(\"Error: construct called on alias class\")\n            }\n            fn construct_overlay(&self, _inner : Rc<dyn Specifier>) -> $crate::Result<Rc<dyn Specifier>> {\n                panic!(\"Error: construct_overlay called on alias class\")\n            }\n            fn alias_info(&self) -> Option<&'static str> { Some($x) }\n        }\n    };\n}\n\nmacro_rules! specifier_class {\n    (name=$n:ident,\n            target=$t:ident,\n            prefixes=[$($p:expr),*],\n            arg_handling=$c:tt,\n            overlay=$o:expr,\n            $so:expr,\n            $ms:expr,\n            help=$h:expr) => {\n        #[derive(Debug,Default)]\n        pub struct $n;\n        impl $crate::SpecifierClass for $n {\n            fn get_name(&self) -> &'static str { stringify!($n) }\n            fn get_prefixes(&self) -> Vec<&'static str> { vec![$($p),*] }\n            fn help(&self) -> &'static str { $h }\n            fn message_boundary_status(&self) -> $crate::ClassMessageBoundaryStatus {\n                use $crate::ClassMessageBoundaryStatus::*;\n                $so\n            }\n            fn multiconnect_status(&self) -> $crate::ClassMulticonnectStatus {\n                use $crate::ClassMulticonnectStatus::*;\n                $ms\n            }\n            fn is_overlay(&self) -> bool {\n                $o\n            }\n            specifier_class!(construct target=$t $c);\n        }\n    };\n    (construct target=$t:ident noarg) => {\n        fn construct(&self, just_arg:&str) -> $crate::Result<Rc<dyn Specifier>> {\n            if just_arg != \"\" {\n                Err(format!(\"{}-specifer requires no parameters. `{}` is not needed\",\n                    self.get_name(), just_arg))?;\n            }\n            Ok(Rc::new($t))\n        }\n        fn construct_overlay(&self, _inner : Rc<dyn Specifier>) -> $crate::Result<Rc<dyn Specifier>> {\n            panic!(\"Error: construct_overlay called on non-overlay specifier class\")\n        }\n        fn alias_info(&self) -> Option<&'static str> { None }\n    };\n    (construct target=$t:ident into) => {\n        fn construct(&self, just_arg:&str) -> $crate::Result<Rc<dyn Specifier>> {\n            Ok(Rc::new($t(just_arg.into())))\n        }\n        fn construct_overlay(&self, _inner : Rc<dyn Specifier>) -> $crate::Result<Rc<dyn Specifier>> {\n            panic!(\"Error: construct_overlay called on non-overlay specifier class\")\n        }\n        fn alias_info(&self) -> Option<&'static str> { None }\n    };\n    (construct target=$t:ident parse) => {\n        fn construct(&self, just_arg:&str) -> $crate::Result<Rc<dyn Specifier>> {\n            Ok(Rc::new($t(just_arg.parse()?)))\n        }\n        fn construct_overlay(&self, _inner : Rc<dyn Specifier>) -> $crate::Result<Rc<dyn Specifier>> {\n            panic!(\"Error: construct_overlay called on non-overlay specifier class\")\n        }\n        fn alias_info(&self) -> Option<&'static str> { None }\n    };\n    (construct target=$t:ident parseresolve) => {\n        fn construct(&self, just_arg:&str) -> $crate::Result<Rc<dyn Specifier>> {\n            use std::net::ToSocketAddrs;\n            info!(\"Resolving hostname to IP addresses\");\n            let addrs : Vec<std::net::SocketAddr> = just_arg.to_socket_addrs()?.collect();\n            if addrs.is_empty() {\n                Err(\"Failed to resolve this hostname to IP\")?;\n            }\n            for addr in &addrs {\n                info!(\"Got IP: {}\", addr);\n            }\n            Ok(Rc::new($t(addrs)))\n        }\n        fn construct_overlay(&self, _inner : Rc<dyn Specifier>) -> $crate::Result<Rc<dyn Specifier>> {\n            panic!(\"Error: construct_overlay called on non-overlay specifier class\")\n        }\n        fn alias_info(&self) -> Option<&'static str> { None }\n    };\n    (construct target=$t:ident subspec) => {\n        fn construct(&self, just_arg:&str) -> $crate::Result<Rc<dyn Specifier>> {\n            Ok(Rc::new($t($crate::spec(just_arg)?)))\n        }\n        fn construct_overlay(&self, _inner : Rc<dyn Specifier>) -> $crate::Result<Rc<dyn Specifier>> {\n            Ok(Rc::new($t(_inner)))\n        }\n        fn alias_info(&self) -> Option<&'static str> { None }\n    };\n    (construct target=$t:ident {$($x:tt)*}) => {\n        $($x)*\n        fn alias_info(&self) -> Option<&'static str> { None }\n    };\n}\n\n\n#[derive(Debug)]\npub struct SpecifierNode {\n    pub cls: Rc<dyn SpecifierClass>,\n    //pub opt: Rc<std::any::Any>,\n}\n\n#[derive(Debug)]\npub struct SpecifierStack {\n    pub addr: String,\n    pub addrtype: SpecifierNode,\n    pub overlays: Vec<SpecifierNode>,\n}\n\n#[derive(Clone)]\npub struct ConstructParams {\n    pub global_state: Rc<RefCell<ProgramState>>,\n    pub program_options: Rc<Options>,\n    pub left_to_right: L2rUser,\n}\n\n/// All of those methods are about left_to_right mechanism\nimpl ConstructParams {\n    /// Reset left_to_right to default value.\n    pub fn reset_l2r(&mut self) {\n        match self.left_to_right {\n            L2rUser::FillIn(ref mut x) => {\n                *x.borrow_mut() = Default::default();\n                //*x = Rc::new(RefCell::new(Default::default()));\n            }\n            L2rUser::ReadFrom(_) => panic!(\"ConstructParams::reset_l2r called wrong\"),\n        }\n    }\n    /// Clones ConstructParams, changing FillIn to ReadFrom in left_to_right field\n    /// and also disassociating it from the original RefCell.\n    ///\n    /// Panics when called on object with left_to_right set to ReadFrom.\n    pub fn reply(&self) -> Self {\n        let l2r = match self.left_to_right {\n            L2rUser::FillIn(ref x) => Rc::new(x.borrow().clone()),\n            L2rUser::ReadFrom(_) => panic!(\"ConstructParams::reply called wrong\"),\n        };\n        ConstructParams {\n            global_state: self.global_state.clone(),\n            program_options: self.program_options.clone(),\n            left_to_right: L2rUser::ReadFrom(l2r),\n        }\n    }\n\n    pub fn deep_clone(&self) -> Self {\n        let l2r = match self.left_to_right {\n            L2rUser::FillIn(ref x) => L2rUser::FillIn(Rc::new(RefCell::new(x.borrow().clone()))),\n            L2rUser::ReadFrom(_) => {\n                panic!(\n                    \"You are not supposed to use ConstructParams::deep_clone on ReadFrom things\"\n                );\n            }\n        };\n        ConstructParams {\n            global_state: self.global_state.clone(),\n            program_options: self.program_options.clone(),\n            left_to_right: l2r,\n        }\n    }\n\n    /// Access specified-specific global (singleton) data\n    pub fn global<T:std::any::Any, F>(&self, def:F) -> std::cell::RefMut<T> \n        where F : FnOnce()->T\n    {\n        std::cell::RefMut::map(\n            self.global_state.borrow_mut(),\n            |x|{\n                x.0.entry::<T>().or_insert_with(def)\n            }\n        )\n    }\n}\n\n/// A parsed command line argument.\n/// For example, `ws-listen:tcp-l:127.0.0.1:8080` gets parsed into\n/// a `WsUpgrade(TcpListen(SocketAddr))`.\npub trait Specifier: std::fmt::Debug {\n    /// Apply the specifier for constructing a \"socket\" or other connecting device.\n    fn construct(&self, p: ConstructParams) -> PeerConstructor;\n\n    // Specified by `specifier_boilerplate!`:\n    fn is_multiconnect(&self) -> bool;\n    fn uses_global_state(&self) -> bool;\n}\n\nimpl Specifier for Rc<dyn Specifier> {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        (**self).construct(p)\n    }\n\n    fn is_multiconnect(&self) -> bool {\n        (**self).is_multiconnect()\n    }\n    fn uses_global_state(&self) -> bool {\n        (**self).uses_global_state()\n    }\n}\n\nmacro_rules! specifier_boilerplate {\n    (singleconnect $($e:tt)*) => {\n        fn is_multiconnect(&self) -> bool { false }\n        specifier_boilerplate!($($e)*);\n    };\n    (multiconnect $($e:tt)*) => {\n        fn is_multiconnect(&self) -> bool { true }\n        specifier_boilerplate!($($e)*);\n    };\n    (no_subspec $($e:tt)*) => {\n        specifier_boilerplate!($($e)*);\n    };\n    (has_subspec $($e:tt)*) => {\n        specifier_boilerplate!($($e)*);\n    };\n    () => {\n    };\n    (globalstate $($e:tt)*) => {\n        fn uses_global_state(&self) -> bool { true }\n        specifier_boilerplate!($($e)*);\n    };\n    (noglobalstate $($e:tt)*) => {\n        fn uses_global_state(&self) -> bool { false }\n        specifier_boilerplate!($($e)*);\n    };\n}\n\nmacro_rules! self_0_is_subspecifier {\n    (...) => {\n       // removed with old linter\n    };\n    (proxy_is_multiconnect) => {\n        self_0_is_subspecifier!(...);\n        fn is_multiconnect(&self) -> bool { self.0.is_multiconnect() }\n    };\n}\n"
  },
  {
    "path": "src/specparse.rs",
    "content": "use super::{Result};\nuse super::specifier::{Specifier, SpecifierClass, SpecifierStack, SpecifierNode};\nuse std::rc::Rc;\nuse std::str::FromStr;\n\npub fn spec(s: &str) -> Result<Rc<dyn Specifier>> {\n    <dyn Specifier>::from_stack(&SpecifierStack::from_str(s)?)\n}\n\nfn some_checks(s: &str) -> Result<()> {\n    #[cfg(not(feature = \"ssl\"))]\n    {\n        if s.starts_with(\"wss://\") {\n            Err(\"SSL is not compiled in. Use ws:// or get/make another Websocat build.\\nYou can also try to workaround missing SSL by using ws-c:cmd:socat trick (see some ws-c: example)\")?\n        }\n    }\n\n    #[cfg(not(any(target_os = \"linux\", target_os = \"android\")))]\n    {\n        if s.starts_with(\"abstract\") {\n            warn!(\"Abstract-namespaced UNIX sockets are unlikely to be supported here\");\n        }\n    }\n\n    if s.starts_with(\"open:\") {\n        return Err(\"There is no `open:` address type. Consider `open-async:` or `readfile:` or `writefile:` or `appendfile:`\".into());\n    }\n\n    #[cfg(not(unix))]\n    {\n        if s.starts_with(\"unix\") || s.starts_with(\"abstract\") {\n            Err(\"`unix*:` or `abstract*:` are not supported in this Websocat build\")?\n        }\n    }\n\n    #[cfg(not(feature = \"tokio-process\"))]\n    {\n        if s.starts_with(\"sh-c:\") {\n            Err(\"`sh-c:` is not supported in this Websocat build\")?\n        } else if s.starts_with(\"exec:\") {\n            Err(\"`exec:` is not supported in this Websocat build\")?\n        }\n    }\n\n    #[cfg(not(feature = \"crypto_peer\"))]\n    {\n        if s.starts_with(\"crypto:\") {\n            Err(\"`crypto:` support is not compiled in\")?\n        }\n    }\n\n    #[cfg(not(feature = \"prometheus_peer\"))]\n    {\n        if s.starts_with(\"metrics:\") || s.starts_with(\"prometheus:\") {\n            Err(\"`prometheus:` support is not compiled in\")?\n        }\n    }\n\n    Ok(())\n}\n\nimpl FromStr for SpecifierStack {\n    type Err = Box<dyn (::std::error::Error)>;\n    #[allow(clippy::cyclomatic_complexity)]\n    fn from_str(s: &str) -> Result<SpecifierStack> {\n        some_checks(s)?;\n\n        let mut s = s.to_string();\n        let mut overlays = vec![];\n        let addrtype;\n        let addr;\n        let mut found = false;\n\n        'a: loop {\n            macro_rules! my {\n                ($x:expr) => {\n                    for pre in $x.get_prefixes() {\n                        if s.starts_with(pre) {\n                            let rest = &s[pre.len()..].to_string();\n                            if let Some(a) = $x.alias_info() {\n                                s = format!(\"{}{}\", a, rest);\n                                continue 'a;\n                            } else if $x.is_overlay() {\n                                let cls = Rc::new($x) as Rc<dyn SpecifierClass>;\n                                overlays.push(SpecifierNode{cls});\n                                s = rest.to_string();\n                                continue 'a;\n                            } else {\n                                addr = rest.to_string();\n                                let cls = Rc::new($x) as Rc<dyn SpecifierClass>;\n                                addrtype = SpecifierNode{cls};\n                                #[allow(unused_assignments)]\n                                {\n                                    found = true;\n                                }\n                                break 'a;\n                            }\n                        }\n                    }\n                };\n            }\n            list_of_all_specifier_classes!(my);\n            if !found {\n                if let Some(colon) = s.find(':') {\n                    Err(format!(\n                        \"Unknown address or overlay type of `{}:`\",\n                        &s[..colon]\n                    ))?;\n                } else {\n                    Err(format!(\"Unknown address or overlay type of `{}`\\nMaybe you forgot the `:` character?\", s))?;\n                }\n            }\n        }\n\n        Ok(SpecifierStack {\n            addr,\n            addrtype,\n            overlays,\n        })\n    }\n}\n\nimpl dyn Specifier {\n    pub fn from_stack(st: &SpecifierStack) -> Result<Rc<dyn Specifier>> {\n        let mut x = st.addrtype.cls.construct(st.addr.as_str())?;\n        for overlay in st.overlays.iter().rev() {\n            x = overlay.cls.construct_overlay(x)?;\n        }\n        Ok(x)\n    }\n}\n"
  },
  {
    "path": "src/ssl_peer.rs",
    "content": "use futures::future::{ok, Future};\n\nuse std::rc::Rc;\n\nuse super::{box_up_err, peer_err, BoxedNewPeerFuture, Peer};\nuse super::{ConstructParams, L2rUser, Options, PeerConstructor, Specifier};\n\npub extern crate native_tls;\nextern crate readwrite;\nextern crate tokio_tls;\n\nuse self::native_tls::{Identity as Pkcs12, TlsAcceptor, TlsConnector};\nuse self::tokio_tls::{TlsAcceptor as TlsAcceptorExt, TlsConnector as TlsConnectorExt};\n\nuse std::ffi::{OsStr, OsString};\n\npub fn interpret_pkcs12(x: &OsStr) -> ::std::result::Result<Vec<u8>, OsString> {\n    match (|| {\n        use std::io::Read;\n        let mut f = ::std::fs::File::open(x)?;\n        let mut v = Vec::with_capacity(2048);\n        f.read_to_end(&mut v)?;\n        Ok(v)\n    })() {\n        Err(e) => {\n            let e: Box<dyn (::std::error::Error)> = e;\n            let o: OsString = format!(\"{}\", e).into();\n            Err(o)\n        }\n        Ok(x) => Ok(x),\n    }\n}\n\n#[derive(Debug)]\npub struct TlsConnect<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for TlsConnect<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(cp.clone());\n        inner.map(move |p, l2r| {\n            ssl_connect(\n                p,\n                l2r,\n                cp.program_options.tls_domain.clone(),\n                cp.program_options.tls_insecure,\n                cp.program_options.client_pkcs12_der.clone(),\n                cp.program_options.client_pkcs12_passwd.clone(),\n            )\n        })\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = TlsConnectClass,\n    target = TlsConnect,\n    prefixes = [\"ssl-connect:\",\"ssl-c:\",\"ssl:\",\"tls:\",\"tls-connect:\",\"tls-c:\",\"c-ssl:\",\"connect-ssl:\",\"c-tls:\",\"connect-tls:\"],\n    arg_handling = subspec,\n    overlay = true,\n    StreamOriented,\n    MulticonnectnessDependsOnInnerType,\n    help = r#\"\nOverlay to add TLS encryption atop of existing connection [A]\n\nExample: manually connect to a secure websocket\n\n    websocat -t - ws-c:tls-c:tcp:174.129.224.73:1080 --ws-c-uri ws://echo.websocket.org --tls-domain echo.websocket.org\n\nFor a user-friendly solution, see --socks5 command-line option\n\"#\n);\n\n#[derive(Debug)]\npub struct TlsAccept<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for TlsAccept<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(cp.clone());\n        inner.map(move |p, l2r| ssl_accept(p, l2r, cp.program_options.clone()))\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = TlsAcceptClass,\n    target = TlsAccept,\n    prefixes = [\n        \"ssl-accept:\",\n        \"ssl-a:\",\n        \"tls-a:\",\n        \"tls-accept:\",\n        \"a-ssl:\",\n        \"accept-ssl:\",\n        \"accept-tls:\",\n        \"accept-tls:\"\n    ],\n    arg_handling = subspec,\n    overlay = true,\n    StreamOriented,\n    MulticonnectnessDependsOnInnerType,\n    help = r#\"\nAccept an TLS connection using arbitrary backing stream. [A]\n\nExample: The same as in TlsListenClass's example, but with manual acceptor\n\n    websocat -E -b --pkcs12-der=q.pkcs12 tls-a:tcp-l:127.0.0.1:1234 mirror:\n\"#\n);\n\nspecifier_alias!(\n    name = TlsListenClass,\n    prefixes = [\n        \"ssl-listen:\",\n        \"ssl-l:\",\n        \"tls-l:\",\n        \"tls-listen:\",\n        \"l-ssl:\",\n        \"listen-ssl:\",\n        \"listen-tls:\",\n        \"listen-tls:\"\n    ],\n    alias = \"tls-accept:tcp-l:\",\n    help = r#\"\nListen for SSL connections on a TCP port\n\nExample: Non-websocket SSL echo server\n\n    websocat -E -b --pkcs12-der=q.pkcs12 ssl-listen:127.0.0.1:1234 mirror:\n    socat - ssl:127.0.0.1:1234,verify=0\n\"#\n);\n\nspecifier_alias!(\n    name = WssListenClass,\n    prefixes = [\"wss-listen:\", \"wss-l:\", \"l-wss:\", \"wss-listen:\"],\n    alias = \"ws-u:tls-accept:tcp-l:\",\n    help = r#\"\nListen for secure WebSocket connections on a TCP port\n\nExample: wss:// echo server + client for testing\n\n    websocat -E -t --pkcs12-der=q.pkcs12 wss-listen:127.0.0.1:1234 mirror:\n    websocat --ws-c-uri=wss://localhost/ -t - ws-c:cmd:'socat - ssl:127.0.0.1:1234,verify=0'\n\nSee [moreexamples.md](./moreexamples.md) for info about generation of `q.pkcs12`.\n\"#\n);\n\nuse tokio_io::AsyncRead;\n\npub fn ssl_connect(\n    inner_peer: Peer,\n    _l2r: L2rUser,\n    dom: Option<String>,\n    tls_insecure: bool,\n    client_identity : Option<Vec<u8>>,\n    client_identity_password : Option<String>,\n) -> BoxedNewPeerFuture {\n    let hup = inner_peer.2;\n    let squashed_peer = readwrite::ReadWriteAsync::new(inner_peer.0, inner_peer.1);\n\n    fn gettlsc(nohost: bool, noverify: bool, client_identity : Option<Vec<u8>>, client_identity_password : Option<String>) -> native_tls::Result<TlsConnectorExt> {\n        let mut b = TlsConnector::builder();\n        if nohost {\n            b.danger_accept_invalid_hostnames(true);\n        }\n        if noverify {\n            b.danger_accept_invalid_hostnames(true);\n            b.danger_accept_invalid_certs(true);\n        }\n        \n        if let Some(client_ident) = client_identity {\n            let identity = super::ssl_peer::native_tls::Identity::from_pkcs12(\n                &client_ident,\n                &client_identity_password.unwrap_or(\"\".to_string()),\n            )\n            .map_err(|e| {\n                error!(\n                    \"Unable to parse client identity: {}\\nContinuing without a client identity\",\n                    e\n                )\n            })\n            .ok();\n            if let Some(x) = identity {\n                b.identity(x);\n            }\n        }\n\n        let tlsc: TlsConnector = b.build()?;\n        Ok(TlsConnectorExt::from(tlsc))\n    }\n\n    let tls = match gettlsc(dom.is_none(), tls_insecure, client_identity, client_identity_password) {\n        Ok(x) => x,\n        Err(e) => return peer_err(e),\n    };\n\n    info!(\"Connecting to TLS\");\n    if let Some(dom) = dom {\n        Box::new(\n            tls.connect(dom.as_str(), squashed_peer)\n                .map_err(box_up_err)\n                .and_then(move |tls_stream| {\n                    info!(\"Connected to TLS\");\n                    let (r, w) = tls_stream.split();\n                    ok(Peer::new(r, w, hup))\n                }),\n        )\n    } else {\n        Box::new(tls.connect(\"domainverificationdisabled\", squashed_peer).map_err(box_up_err).and_then(move |tls_stream| {\n            warn!(\"Connected to TLS without proper verification of certificate. Use --tls-domain option.\");\n            let (r,w) = tls_stream.split();\n            ok(Peer::new(r,w, hup))\n        }))\n    }\n}\n\npub fn ssl_accept(inner_peer: Peer, _l2r: L2rUser, progopt: Rc<Options>) -> BoxedNewPeerFuture {\n    let hup = inner_peer.2;\n    let squashed_peer = readwrite::ReadWriteAsync::new(inner_peer.0, inner_peer.1);\n\n    fn gettlsa(cert: &[u8], passwd: &str) -> native_tls::Result<TlsAcceptorExt> {\n        let pkcs12 = Pkcs12::from_pkcs12(cert, passwd)?;\n        Ok(TlsAcceptorExt::from(TlsAcceptor::builder(pkcs12).build()?))\n    }\n\n    let der = progopt\n        .pkcs12_der\n        .as_ref()\n        .expect(\"lint should have caught the missing pkcs12_der option\");\n    let passwd = progopt\n        .pkcs12_passwd.as_deref()\n        .unwrap_or(\"\");\n    let tls = match gettlsa(der, passwd) {\n        Ok(x) => x,\n        Err(e) => return peer_err(e),\n    };\n\n    debug!(\"Accepting a TLS connection\");\n    Box::new(\n        tls.accept(squashed_peer)\n            .map_err(box_up_err)\n            .and_then(move |tls_stream| {\n                info!(\"Accepted TLS connection\");\n                match tls_stream.get_ref().peer_certificate() {\n                    Ok(Some(_cert)) => {\n                        // Does not actually work with native-tls\n                        info!(\"  the client presented an identity certificate.\");\n                    }\n                    Ok(None) => {\n                        debug!(\"  no identity certificate from the client. But Websocat may have failed to request it.\");\n                    }\n                    Err(e) => {\n                        warn!(\"Error getting identity certificate from client: {}\", e);\n                    }\n                }\n                let (r, w) = tls_stream.split();\n                ok(Peer::new(r, w, hup))\n            }),\n    )\n}\n"
  },
  {
    "path": "src/stdio_peer.rs",
    "content": "#[cfg(unix)]\nextern crate tokio_file_unix;\nextern crate tokio_reactor;\n#[cfg(all(unix, feature = \"signal_handler\"))]\nextern crate tokio_signal;\nextern crate tokio_stdin_stdout;\n\nuse futures;\nuse futures::future::Future;\nuse std;\nuse std::cell::RefCell;\nuse std::io::Result as IoResult;\nuse std::io::{Read, Write};\nuse std::path::{Path, PathBuf};\nuse std::rc::Rc;\nuse tokio_io::{AsyncRead, AsyncWrite};\n\n#[cfg(unix)]\nuse self::tokio_file_unix::File as UnixFile;\nuse std::fs::{File as FsFile, OpenOptions};\n\nuse super::{BoxedNewPeerFuture, Peer, Result};\nuse futures::Stream;\n\nuse super::{once, spawn_hack, ConstructParams, PeerConstructor, Specifier};\n\n#[derive(Clone, Debug)]\npub struct AsyncStdio;\nimpl Specifier for AsyncStdio {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        let ret = get_stdio_peer(&mut p.global(GlobalState::default));\n        once(ret)\n    }\n    specifier_boilerplate!(globalstate singleconnect no_subspec);\n}\n\nspecifier_class!(\n    name = AsyncStdioClass,\n    target = AsyncStdio,\n    prefixes = [\"asyncstdio:\"],\n    arg_handling = noarg,\n    overlay = false,\n    StreamOriented,\n    SingleConnect,\n    help = r#\"\n[A] Set stdin and stdout to nonblocking mode, then use it as a communication counterpart. UNIX-only.\nMay cause problems with programs running at the same terminal. This specifier backs the `--async-stdio` CLI option. \n\nTypically this specifier can be specified only one time.\n    \nExample: simulate `cat(1)`. This is an exception from \"only one time\" rule above:\n\n    websocat - -\n\nExample: SSH transport\n\n    ssh -c ProxyCommand='websocat asyncstdio: ws://myserver/mywebsocket' user@myserver\n\"#\n);\n\n\nspecifier_class!(\n    name = InetdClass,\n    target = AsyncStdio,\n    prefixes = [\"inetd:\"],\n    arg_handling = noarg,\n    overlay = false,\n    StreamOriented,\n    SingleConnect,\n    help = r#\"\nLike `asyncstdio:`, but intended for inetd(8) usage. [A]\n\nAutomatically enables `-q` (`--quiet`) mode.\n\n`inetd-ws:` - is of `ws-l:inetd:`\n\nExample of inetd.conf line that makes it listen for websocket\nconnections on port 1234 and redirect the data to local SSH server.\n\n    1234 stream tcp nowait myuser  /opt/websocat websocat inetd-ws: tcp:127.0.0.1:22\n\"#\n);\n\n#[derive(Clone, Debug)]\npub struct OpenAsync(pub PathBuf);\nimpl Specifier for OpenAsync {\n    fn construct(&self, _: ConstructParams) -> PeerConstructor {\n        let ret = get_file_peer(&self.0);\n        once(ret)\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\nspecifier_class!(\n    name = OpenAsyncClass,\n    target = OpenAsync,\n    prefixes = [\"open-async:\"],\n    arg_handling = into,\n    overlay = false,\n    MessageOriented, // ?\n    SingleConnect,\n    help = r#\"\nOpen file for read and write and use it like a socket. [A]\nNot for regular files, see readfile/writefile instead.\n  \nExample: Serve big blobs of random data to clients\n\n    websocat -U ws-l:127.0.0.1:8088 open-async:/dev/urandom\n\n\"#\n);\n\n#[derive(Clone, Debug)]\npub struct OpenFdAsync(pub i32);\nimpl Specifier for OpenFdAsync {\n    fn construct(&self, _: ConstructParams) -> PeerConstructor {\n        let ret = get_fd_peer(self.0);\n        once(ret)\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\nspecifier_class!(\n    name = OpenFdAsyncClass,\n    target = OpenFdAsync,\n    prefixes = [\"open-fd:\"],\n    arg_handling = parse,\n    overlay = false,\n    MessageOriented, // ?\n    SingleConnect,\n    help = r#\"\nUse specified file descriptor like a socket. [A]\n\nExample: Serve random data to clients v2\n\n    websocat -U ws-l:127.0.0.1:8088 reuse:open-fd:55   55< /dev/urandom\n\"#\n);\n\nfn get_stdio_peer_impl(s: &mut GlobalState) -> Result<Peer> {\n    let si;\n    let so;\n    {\n        if !UnixFile::raw_new(std::io::stdin()).get_nonblocking()? {\n            debug!(\"Setting stdin to nonblocking mode\");\n            s.need_to_restore_stdin_blocking_status = true;\n        }\n        let stdin = self::UnixFile::new_nb(std::io::stdin())?;\n\n        if !UnixFile::raw_new(std::io::stdout()).get_nonblocking()? {\n            debug!(\"Setting stdout to nonblocking mode\");\n            s.need_to_restore_stdout_blocking_status = true;\n        }\n        let stdout = self::UnixFile::new_nb(std::io::stdout())?;\n\n        si = stdin.into_reader(&tokio_reactor::Handle::default())?;\n        so = stdout.into_io(&tokio_reactor::Handle::default())?;\n\n        let s_clone = s.clone();\n\n        #[cfg(all(unix, feature = \"signal_handler\"))]\n        {\n            debug!(\"Installing signal handler\");\n            let ctrl_c = tokio_signal::ctrl_c().flatten_stream();\n            let prog = ctrl_c.for_each(move |()| {\n                restore_blocking_status(&s_clone);\n                ::std::process::exit(0);\n                #[allow(unreachable_code)]\n                Ok(())\n            });\n            spawn_hack(Box::new(prog.map_err(|_| ())));\n        }\n    }\n    Ok(Peer::new(si, so, None))\n}\n\npub fn get_stdio_peer(s: &mut GlobalState) -> BoxedNewPeerFuture {\n    debug!(\"get_stdio_peer (async)\");\n    Box::new(futures::future::result(get_stdio_peer_impl(s))) as BoxedNewPeerFuture\n}\n\n#[derive(Default, Clone)]\npub struct GlobalState {\n    need_to_restore_stdin_blocking_status: bool,\n    need_to_restore_stdout_blocking_status: bool,\n}\n\nimpl Drop for GlobalState {\n    fn drop(&mut self) {\n        restore_blocking_status(self);\n    }\n}\n\nfn restore_blocking_status(s: &GlobalState) {\n    {\n        debug!(\"restore_blocking_status\");\n        if s.need_to_restore_stdin_blocking_status {\n            debug!(\"Restoring blocking status for stdin\");\n            let _ = UnixFile::raw_new(std::io::stdin()).set_nonblocking(false);\n        }\n        if s.need_to_restore_stdout_blocking_status {\n            debug!(\"Restoring blocking status for stdout\");\n            let _ = UnixFile::raw_new(std::io::stdout()).set_nonblocking(false);\n        }\n    }\n}\n\ntype ImplPollEvented = ::tokio_reactor::PollEvented<UnixFile<std::fs::File>>;\n\n#[derive(Clone)]\nstruct FileWrapper(Rc<RefCell<ImplPollEvented>>);\n\nimpl AsyncRead for FileWrapper {}\nimpl Read for FileWrapper {\n    fn read(&mut self, buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {\n        self.0.borrow_mut().read(buf)\n    }\n}\n\nimpl AsyncWrite for FileWrapper {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        self.0.borrow_mut().shutdown()\n    }\n}\nimpl Write for FileWrapper {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        self.0.borrow_mut().write(buf)\n    }\n    fn flush(&mut self) -> IoResult<()> {\n        self.0.borrow_mut().flush()\n    }\n}\n\nfn get_file_peer_impl(p: &Path) -> Result<Peer> {\n    let oo = OpenOptions::new()\n        .read(true)\n        .write(true)\n        .create(false)\n        .open(p)?;\n    let f = self::UnixFile::new_nb(oo)?;\n\n    let s = f.into_io(&tokio_reactor::Handle::default())?;\n    let ss = FileWrapper(Rc::new(RefCell::new(s)));\n    Ok(Peer::new(ss.clone(), ss, None))\n}\n\npub fn get_file_peer(p: &Path) -> BoxedNewPeerFuture {\n    debug!(\"get_file_peer\");\n    Box::new(futures::future::result(get_file_peer_impl(p))) as BoxedNewPeerFuture\n}\n\nfn get_fd_peer_impl(fd: i32) -> Result<Peer> {\n    let ff: FsFile = unsafe { std::os::unix::io::FromRawFd::from_raw_fd(fd) };\n    let f = self::UnixFile::new_nb(ff)?;\n\n    let s = f.into_io(&tokio_reactor::Handle::default())?;\n    let ss = FileWrapper(Rc::new(RefCell::new(s)));\n    Ok(Peer::new(ss.clone(), ss, None))\n}\n\npub fn get_fd_peer(fd: i32) -> BoxedNewPeerFuture {\n    debug!(\"get_fd_peer\");\n    Box::new(futures::future::result(get_fd_peer_impl(fd))) as BoxedNewPeerFuture\n}\n"
  },
  {
    "path": "src/stdio_threaded_peer.rs",
    "content": "extern crate tokio_stdin_stdout;\n\nuse super::{BoxedNewPeerFuture, Peer};\n\nuse super::{once, ConstructParams, PeerConstructor, Specifier};\n\nuse std::rc::Rc;\n\n#[derive(Debug, Clone)]\npub struct ThreadedStdio;\nimpl Specifier for ThreadedStdio {\n    fn construct(&self, _: ConstructParams) -> PeerConstructor {\n        once(get_stdio_peer())\n    }\n    specifier_boilerplate!(globalstate singleconnect no_subspec);\n}\n\nspecifier_class!(\n    name = ThreadedStdioClass,\n    target = ThreadedStdio,\n    prefixes = [\"threadedstdio:\"],\n    arg_handling = noarg,\n    overlay = false,\n    StreamOriented,\n    SingleConnect,\n    help = r#\"\n[A] Stdin/stdout, spawning a thread (threaded version).\n\nLike `-`, but forces threaded mode instead of async mode\n\nUse when standard input is not `epoll(7)`-able or you want to avoid setting it to nonblocking mode.\n\"#\n);\n\nspecifier_class!(\n    name = StdioClass,\n    target = ThreadedStdio,\n    prefixes = [\"-\", \"stdio:\"],\n    arg_handling = noarg,\n    overlay = false,\n    StreamOriented,\n    SingleConnect,\n    help = r#\"\nRead input from console, print to console. Uses threaded implementation even on UNIX unless requested by `--async-stdio` CLI option.\n\nTypically this specifier can be specified only one time.\n    \nExample: simulate `cat(1)`. This is an exception from \"only one time\" rule above:\n\n    websocat - -\n\nExample: SSH transport\n\n    ssh -c ProxyCommand='websocat - ws://myserver/mywebsocket' user@myserver\n\"#\n);\n\n\n#[cfg(not(all(unix, feature = \"unix_stdio\")))]\nspecifier_class!(\n    name = InetdClass,\n    target = ThreadedStdio,\n    prefixes = [\"inetd:\"],\n    arg_handling = noarg,\n    overlay = false,\n    StreamOriented,\n    SingleConnect,\n    help = r#\"\nAlias of stdio: (threaded version).\n\"#\n);\n\npub fn get_stdio_peer() -> BoxedNewPeerFuture {\n    info!(\"get_stdio_peer (threaded)\");\n    Box::new(::futures::future::ok(Peer::new(\n        tokio_stdin_stdout::stdin(0),\n        tokio_stdin_stdout::stdout(0),\n        None,\n    ))) as BoxedNewPeerFuture\n}\n"
  },
  {
    "path": "src/timestamp_peer.rs",
    "content": "use futures::future::ok;\n\nuse std::rc::Rc;\n\nuse super::{BoxedNewPeerFuture, Peer};\nuse super::{ConstructParams, PeerConstructor, Specifier};\nuse std::time::{SystemTime, UNIX_EPOCH, Instant};\n\nuse std::io::Read;\nuse tokio_io::AsyncRead;\n\nuse std::io::Error as IoError;\n\n#[derive(Debug)]\npub struct TimestampPeer<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for TimestampPeer<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(cp.clone());\n        inner.map(move |p, _| timestamp_peer(p, cp.program_options.timestamp_monotonic))\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = TimestampClass,\n    target = TimestampPeer,\n    prefixes = [\"timestamp:\"],\n    arg_handling = subspec,\n    overlay = true,\n    MessageOriented,\n    MulticonnectnessDependsOnInnerType,\n    help = r#\"\n[A] Prepend timestamp to each incoming message.\n\nExample: TODO\n\"#\n);\n\npub fn timestamp_peer(inner_peer: Peer, monotonic: bool) -> BoxedNewPeerFuture {\n    let instant = if monotonic { Some(Instant::now() )} else { None };\n    let filtered = TimestampWrapper(inner_peer.0, instant);\n    let thepeer = Peer::new(filtered, inner_peer.1, inner_peer.2);\n    Box::new(ok(thepeer)) as BoxedNewPeerFuture\n}\nstruct TimestampWrapper(Box<dyn AsyncRead>, Option<Instant>);\n\nimpl Read for TimestampWrapper {\n    fn read(&mut self, b: &mut [u8]) -> Result<usize, IoError> {\n        let l = b.len();\n        assert!(l > 1);\n        let n = self.0.read(&mut b[..l])?;\n        if n == 0 {\n            return Ok(0);\n        }\n\n        let mut v: Vec<u8> = Vec::with_capacity(n + 50);\n        {\n            let mut vv = ::std::io::Cursor::new(&mut v);\n            use std::io::Write;\n            let x = if let Some(basetime) = self.1 {\n                Instant::now().duration_since(basetime).as_secs_f64()\n            } else {\n                (SystemTime::now().duration_since(UNIX_EPOCH).expect(\"Time went backwards\")).as_secs_f64()\n            };\n            let _ = write!(vv, \"{} \", x);\n            let _ = vv.write_all(&b[..n]);\n        }\n        \n        if v.len() > l {\n            warn!(\"Buffer too small, timstamp-prepended message may be truncated.\");\n        }\n        let ll = v.len().min(l);\n        b[..ll].copy_from_slice(&v[..ll]);\n        Ok(ll)\n    }\n}\nimpl AsyncRead for TimestampWrapper {}\n"
  },
  {
    "path": "src/trivial_peer.rs",
    "content": "use super::{BoxedNewPeerFuture, Peer};\n\nuse futures;\nuse rand::RngCore;\nuse std;\nuse std::io::Result as IoResult;\nuse std::io::{Read, Write};\n\nuse futures::Async::Ready;\n\nuse std::rc::Rc;\nuse tokio_io::{AsyncRead, AsyncWrite};\n\nuse super::readdebt::{DebtHandling, ReadDebt, ZeroMessagesHandling};\nuse super::wouldblock;\n\nuse super::{once, simple_err, ConstructParams, PeerConstructor, Specifier};\n\n#[derive(Clone)]\npub struct Literal(pub Vec<u8>);\nimpl Specifier for Literal {\n    fn construct(&self, _: ConstructParams) -> PeerConstructor {\n        once(get_literal_peer(self.0.clone()))\n    }\n    specifier_boilerplate!(singleconnect no_subspec noglobalstate);\n}\nimpl std::fmt::Debug for Literal {\n    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {\n        write!(f, \"Literal\")\n    }\n}\nspecifier_class!(\n    name = LiteralClass,\n    target = Literal,\n    prefixes = [\"literal:\"],\n    arg_handling = into,\n    overlay = false,\n    MessageOriented,\n    SingleConnect,\n    help = r#\"\nOutput a string, discard input.\n\nExample:\n\n    websocat ws-l:127.0.0.1:8080 literal:'{ \"hello\":\"world\"} '\n\"#\n);\n// TODO: better doc\n\n#[derive(Clone)]\npub struct Assert(pub Vec<u8>);\nimpl Specifier for Assert {\n    fn construct(&self, _: ConstructParams) -> PeerConstructor {\n        once(get_assert_peer(self.0.clone()))\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\nimpl std::fmt::Debug for Assert {\n    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {\n        write!(f, \"Assert\")\n    }\n}\nspecifier_class!(\n    name = AssertClass,\n    target = Assert,\n    prefixes = [\"assert:\"],\n    arg_handling = into,\n    overlay = false,\n    MessageOriented,\n    SingleConnect,\n    help = r#\"\nCheck the input.  [A]\n\nRead entire input and panic the program if the input is not equal\nto the specified string. Used in tests.\n\"#\n);\n\n#[derive(Clone)]\npub struct Assert2(pub Vec<u8>);\nimpl Specifier for Assert2 {\n    fn construct(&self, _: ConstructParams) -> PeerConstructor {\n        once(get_assert2_peer(self.0.clone()))\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\nimpl std::fmt::Debug for Assert2 {\n    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {\n        write!(f, \"Assert2\")\n    }\n}\nspecifier_class!(\n    name = Assert2Class,\n    target = Assert2,\n    prefixes = [\"assert2:\"],\n    arg_handling = into,\n    overlay = false,\n    MessageOriented,\n    SingleConnect,\n    help = r#\"\nCheck the input. [A]\n\nRead entire input and emit an error if the input is not equal\nto the specified string.\n\"#\n);\n\n#[derive(Debug, Clone)]\npub struct Clogged;\nimpl Specifier for Clogged {\n    fn construct(&self, _: ConstructParams) -> PeerConstructor {\n        once(get_clogged_peer())\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\nspecifier_class!(\n    name = CloggedClass,\n    target = Clogged,\n    prefixes = [\"clogged:\"],\n    arg_handling = noarg,\n    overlay = false,\n    MessageOriented,\n    SingleConnect,\n    help = r#\"\nDo nothing. Don't read or write any bytes. Keep connections in \"hung\" state. [A]\n\"#\n);\n\npub struct LiteralPeer {\n    debt: ReadDebt,\n}\n\npub fn get_literal_peer_now(b: Vec<u8>) -> LiteralPeer {\n    LiteralPeer {\n        debt: ReadDebt(Some(b), DebtHandling::Silent, ZeroMessagesHandling::Deliver),\n    }\n}\n\npub fn get_literal_peer(b: Vec<u8>) -> BoxedNewPeerFuture {\n    let r = get_literal_peer_now(b);\n    let w = DevNull;\n    let p = Peer::new(r, w, None);\n    Box::new(futures::future::ok(p)) as BoxedNewPeerFuture\n}\npub fn get_assert_peer(b: Vec<u8>) -> BoxedNewPeerFuture {\n    let r = DevNull;\n    let w = AssertPeer(vec![], b, true);\n    let p = Peer::new(r, w, None);\n    Box::new(futures::future::ok(p)) as BoxedNewPeerFuture\n}\npub fn get_assert2_peer(b: Vec<u8>) -> BoxedNewPeerFuture {\n    let r = DevNull;\n    let w = AssertPeer(vec![], b, false);\n    let p = Peer::new(r, w, None);\n    Box::new(futures::future::ok(p)) as BoxedNewPeerFuture\n}\n/// A special peer that returns NotReady without registering for any wakeup, deliberately hanging all connections forever.\npub fn get_clogged_peer() -> BoxedNewPeerFuture {\n    let r = CloggedPeer;\n    let w = CloggedPeer;\n    let p = Peer::new(r, w, None);\n    Box::new(futures::future::ok(p)) as BoxedNewPeerFuture\n}\n\nimpl AsyncRead for LiteralPeer {}\n\nimpl Read for LiteralPeer {\n    fn read(&mut self, buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {\n        if let Some(ret) = self.debt.check_debt(buf) {\n            debug!(\"LiteralPeer debt\");\n            return ret;\n        }\n        debug!(\"LiteralPeer finished\");\n        Ok(0)\n    }\n}\n\npub struct DevNull;\n\nimpl AsyncWrite for DevNull {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        Ok(Ready(()))\n    }\n}\nimpl Write for DevNull {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        Ok(buf.len())\n    }\n    fn flush(&mut self) -> IoResult<()> {\n        Ok(())\n    }\n}\nimpl AsyncRead for DevNull {}\nimpl Read for DevNull {\n    fn read(&mut self, _buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {\n        Ok(0)\n    }\n}\n\nstruct AssertPeer(Vec<u8>, Vec<u8>, bool);\nimpl AsyncWrite for AssertPeer {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        if self.2 {\n            assert_eq!(self.0, self.1);\n        } else if self.0 != self.1 {\n            error!(\"Assertion failed\");\n            return Err(simple_err(\"Assertion failed\".into()));\n        }\n        info!(\"Assertion succeed\");\n        Ok(Ready(()))\n    }\n}\n\nimpl Write for AssertPeer {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        self.0.extend_from_slice(buf);\n        Ok(buf.len())\n    }\n    fn flush(&mut self) -> IoResult<()> {\n        Ok(())\n    }\n}\n\npub struct CloggedPeer;\nimpl AsyncWrite for CloggedPeer {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        wouldblock()\n    }\n}\nimpl Write for CloggedPeer {\n    fn write(&mut self, _buf: &[u8]) -> IoResult<usize> {\n        wouldblock()\n    }\n    fn flush(&mut self) -> IoResult<()> {\n        wouldblock()\n    }\n}\nimpl AsyncRead for CloggedPeer {}\nimpl Read for CloggedPeer {\n    fn read(&mut self, _buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {\n        wouldblock()\n    }\n}\n\n\n// TODO: make Prepend{Read,Write} available from command line\n\n/// First read content of `header`, then start relaying from `inner`.\npub struct PrependRead {\n    pub header: Vec<u8>,\n    pub remaining: usize,\n    pub inner: Box<dyn AsyncRead>,\n}\n\nimpl AsyncRead for PrependRead {}\n\nimpl Read for PrependRead {\n    fn read(&mut self, buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {\n        if self.remaining == 0 {\n            trace!(\"PrependRead relay\");\n            return self.inner.read(buf);\n        }\n        let l = buf.len().min(self.remaining);\n        debug!(\"PrependRead read debt {}\", l);\n        let offset = self.header.len() - self.remaining;\n        buf[..l].copy_from_slice(&self.header[offset..(offset+l)]);\n        let ret = l;\n        self.remaining -= ret;\n        if self.remaining == 0 {\n            self.header.clear();\n            self.header.shrink_to_fit();\n        }\n        Ok(l)\n    }\n}\n\n/// First write `header` to `inner`, then start copying data directly to it.\npub struct PrependWrite {\n    pub header: Vec<u8>,\n    pub remaining: usize,\n    pub inner: Box<dyn AsyncWrite>,\n}\n\nimpl AsyncWrite for PrependWrite {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        self.inner.shutdown()\n    }\n}\nimpl Write for PrependWrite {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        loop {\n            if self.remaining == 0 {\n                return self.inner.write(buf);\n            }\n            let offset = self.header.len() - self.remaining;\n            let ret = self.inner.write(&self.header[offset..])?;\n            self.remaining -= ret;\n            if self.remaining == 0 {\n                self.header.clear();\n                self.header.shrink_to_fit();\n            }\n        }\n    }\n    fn flush(&mut self) -> IoResult<()> {\n        self.inner.flush()\n    }\n}\n\n#[derive(Debug)]\npub struct Log<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for Log<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(cp.clone());\n        inner.map(move |p, _l2r| {\n            Box::new(futures::future::ok(Peer(Box::new(LogRead(p.0)), Box::new(LogWrite(p.1)), p.2)))\n        })\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = LogClass,\n    target = Log,\n    prefixes = [\"log:\"],\n    arg_handling = subspec,\n    overlay = true,\n    StreamOriented,\n    MulticonnectnessDependsOnInnerType,\n    help = r#\"\nLog each buffer as it pass though the underlying connector.\n\nIf you increase the logging level, you will also see hex buffers.\n\nExample: view WebSocket handshake and traffic on the way to echo.websocket.org\n\n    websocat -t - ws-c:log:tcp:127.0.0.1:1080 --ws-c-uri ws://echo.websocket.org\n\n\"#\n);\n\npub struct LogRead (pub Box<dyn AsyncRead>);\n\nfn log_buffer(tag: &'static str, buf: &[u8]) {\n    let mut s = String::with_capacity(buf.len()*2);\n    for x in buf.iter().cloned().map(std::ascii::escape_default) {\n        s.push_str(String::from_utf8_lossy(&x.collect::<Vec<u8>>()).as_ref() );\n    }\n    eprintln!(\"{} {} \\\"{}\\\"\", tag, buf.len(), s );\n    debug!(\"{}\", hex::encode(buf));\n}\n\n\nimpl AsyncRead for LogRead {}\n\nimpl Read for LogRead {\n    fn read(&mut self, buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {\n        let ret = self.0.read(buf);\n\n        if let Ok(ref sz) = ret {\n            let buf = &buf[..*sz];\n            log_buffer(\"READ\", buf);\n        } else {\n            //eprintln!(\"FAILED_READ\");\n        }\n\n        ret\n    }\n}\n\npub struct LogWrite(pub Box<dyn AsyncWrite>);\n\nimpl AsyncWrite for LogWrite {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        self.0.shutdown()\n    }\n}\nimpl Write for LogWrite {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        \n        let ret = self.0.write(buf);\n\n        if let Ok(ref sz) = ret {\n            let buf = &buf[..*sz];\n            log_buffer(\"WRITE\", buf);\n        } else {\n            //eprintln!(\"FAILED_WRITE\");\n        }\n\n        ret\n    }\n    fn flush(&mut self) -> IoResult<()> {\n        self.0.flush()\n    }\n}\n\n\n#[derive(Debug)]\npub struct Random;\nimpl Specifier for Random {\n    fn construct(&self, _cp: ConstructParams) -> PeerConstructor {\n        let r = RandomReader();\n        let w = DevNull;\n        let p = Peer::new(r, w, None);\n        once(Box::new(futures::future::ok(p)) as BoxedNewPeerFuture)\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\nspecifier_class!(\n    name = RandomClass,\n    target = Random,\n    prefixes = [\"random:\"],\n    arg_handling = noarg,\n    overlay = false,\n    MessageOriented,\n    SingleConnect,\n    help = r#\"\nGenerate random bytes when being read from, discard written bytes.\n\n    websocat -b random: ws://127.0.0.1/flood\n\n\"#\n);\n\n\npub struct RandomReader ();\n\n\nimpl AsyncRead for RandomReader {}\n\nimpl Read for RandomReader {\n    fn read(&mut self, buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {\n        rand::thread_rng().fill_bytes(buf);\n        Ok(buf.len())\n    }\n}\n\n\n#[derive(Debug)]\npub struct ExitOnSpecificByte<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for ExitOnSpecificByte<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(cp.clone());\n        inner.map(move |p, _l2r| {\n            Box::new(futures::future::ok(Peer(Box::new(ExitOnSpecificByteReader { \n                inner: p.0,\n                the_byte: cp.program_options.byte_to_exit_on,\n                eof_triggered: false,\n            }), p.1, p.2)))\n        })\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = ExitOnSpecificByteClass,\n    target = ExitOnSpecificByte,\n    prefixes = [\"exit_on_specific_byte:\"],\n    arg_handling = subspec,\n    overlay = true,\n    StreamOriented,\n    MulticonnectnessDependsOnInnerType,\n    help = r#\"\n[A] Turn specific byte into a EOF, allowing user to escape interactive Websocat session\nwhen terminal is set to raw mode. Works only bytes read from the overlay, not on the written bytes.\n\nDefault byte is 1C which is typically triggered by Ctrl+\\.\n\nExample: `(stty raw -echo; websocat -b exit_on_specific_byte:stdio tcp:127.0.0.1:23; stty sane)`\n\n\"#\n);\n\npub struct ExitOnSpecificByteReader { \n    inner: Box<dyn AsyncRead>,\n    the_byte: u8,\n    eof_triggered: bool,\n}\n\n\nimpl AsyncRead for ExitOnSpecificByteReader {}\n\nimpl Read for ExitOnSpecificByteReader {\n    fn read(&mut self, buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {\n        if self.eof_triggered {\n            return Ok(0);\n        }\n        let ret = self.inner.read(buf);\n\n        if let Ok(ref sz) = ret {\n            let buf = &buf[..*sz];\n            if let Some((pos,_)) = buf.iter().enumerate().find(|x|*x.1==self.the_byte) {\n                log::info!(\"Special byte detected. Triggering EOF.\");\n                self.eof_triggered = true;\n                return Ok(pos);\n            }   \n        }\n\n        ret\n    }\n}\n\n#[derive(Debug)]\npub struct DropOnBackpressure<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for DropOnBackpressure<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(cp.clone());\n        inner.map(move |p, _l2r| {\n            Box::new(futures::future::ok(Peer(p.0, Box::new(DropOnBackpressureWriter { \n                inner: p.1\n            }), p.2)))\n        })\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = DropOnBackpressureClass,\n    target = DropOnBackpressure,\n    prefixes = [\"drop_on_backpressure:\"],\n    arg_handling = subspec,\n    overlay = true,\n    MessageBoundaryStatusDependsOnInnerType,\n    MulticonnectnessDependsOnInnerType,\n    help = r#\"\n[A] Prevent writing from ever blocking, drop writes instead.\n\nDoes not affect reading part.\nwhen terminal is set to raw mode. Works only bytes read from the overlay, not on the written bytes.\n\nDefault byte is 1C which is typically triggered by Ctrl+\\.\n\nExample (attachable log observer):\n\n    some_program | websocat -b -u asyncstdio: drop_on_backpressure:autoreconnect:ws-l:127.0.0.1:1234\n\n\"#\n);\npub struct DropOnBackpressureWriter { \n    inner: Box<dyn AsyncWrite>,\n}\n\nimpl Write for DropOnBackpressureWriter {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        match self.inner.write(buf) {\n            Ok(n) => Ok(n),\n            Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {\n                log::debug!(\"Dropping {} bytes due to backpressure\", buf.len());\n                Ok(buf.len())\n            }\n            Err(e) => Err(e),\n        }\n    }\n\n    fn flush(&mut self) -> IoResult<()> {\n        match self.inner.flush() {\n            Ok(()) => Ok(()),\n            Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {\n                log::debug!(\"Dropping flush due to backpressure\");\n                Ok(())\n            }\n            Err(e) => Err(e),\n        }\n    }\n}\n\nimpl AsyncWrite for DropOnBackpressureWriter {\n    fn shutdown(&mut self) -> tokio::prelude::Poll<(), std::io::Error> {\n        self.inner.shutdown()\n    }\n}\n"
  },
  {
    "path": "src/unix_peer.rs",
    "content": "extern crate tokio_reactor;\nextern crate tokio_uds;\n\nextern crate libc;\n\nuse futures;\nuse futures::stream::Stream;\nuse std;\nuse std::io::Result as IoResult;\nuse std::io::{Read, Write};\nuse tokio_io::{AsyncRead, AsyncWrite};\n\nuse std::cell::RefCell;\nuse std::rc::Rc;\n\nuse std::path::{Path, PathBuf};\n\nuse self::tokio_uds::{UnixDatagram, UnixListener, UnixStream};\n\n//#[cfg_attr(feature=\"cargo-clippy\",allow(unused_imports))]\n#[allow(unused_imports)]\nuse super::simple_err;\nuse super::{box_up_err, peer_err_s, util::peer_err_sb, BoxedNewPeerFuture, BoxedNewPeerStream, Peer};\nuse super::{multi, once, ConstructParams, Options, PeerConstructor, Specifier};\n\n#[derive(Debug, Clone)]\npub struct UnixConnect(pub PathBuf);\nimpl Specifier for UnixConnect {\n    fn construct(&self, _: ConstructParams) -> PeerConstructor {\n        once(unix_connect_peer(&self.0))\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\nspecifier_class!(\n    name = UnixConnectClass,\n    target = UnixConnect,\n    prefixes = [\n        \"unix:\",\n        \"unix-connect:\",\n        \"connect-unix:\",\n        \"unix-c:\",\n        \"c-unix:\"\n    ],\n    arg_handling = into,\n    overlay = false,\n    StreamOriented,\n    SingleConnect,\n    help = r#\"\nConnect to UNIX socket. Argument is filesystem path. [A]\n\nExample: forward connections from websockets to a UNIX stream socket\n\n    websocat ws-l:127.0.0.1:8088 unix:the_socket\n\"#\n);\n\n#[derive(Debug, Clone)]\npub struct UnixListen(pub PathBuf);\nimpl Specifier for UnixListen {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        multi(unix_listen_peer(&self.0, &p.program_options))\n    }\n    specifier_boilerplate!(noglobalstate multiconnect no_subspec);\n}\nspecifier_class!(\n    name = UnixListenClass,\n    target = UnixListen,\n    prefixes = [\"unix-listen:\", \"listen-unix:\", \"unix-l:\", \"l-unix:\"],\n    arg_handling = into,\n    overlay = false,\n    StreamOriented,\n    MultiConnect,\n    help = r#\"\nListen for connections on a specified UNIX socket [A]\n\nExample: forward connections from a UNIX socket to a WebSocket\n\n    websocat --unlink unix-l:the_socket ws://127.0.0.1:8089\n\nExample: Accept forwarded WebSocket connections from Nginx\n\n    umask 0000\n    websocat --unlink -b -E ws-u:unix-l:/tmp/wstest tcp:[::]:22\n\nNginx config:\n\n    location /ws {\n        proxy_read_timeout 7d;\n        proxy_send_timeout 7d;\n        #proxy_pass http://localhost:3012;\n        proxy_pass http://unix:/tmp/wstest;\n        proxy_http_version 1.1;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection \\\"upgrade\\\";\n    }\n\nThis configuration allows to make Nginx responsible for\nSSL and also it can choose which connections to forward\nto websocat based on URLs.\n\nObviously, Nginx can also redirect to TCP-listening\nwebsocat just as well - UNIX sockets are not a requirement for this feature.\n\nSee `moreexamples.md` for SystemD usage (untested).\n\nTODO: --chmod option?\n\"#\n);\n\n#[derive(Debug, Clone)]\npub struct UnixDgram(pub PathBuf, pub PathBuf);\nimpl Specifier for UnixDgram {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        once(dgram_peer(&self.0, &self.1, &p.program_options))\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\nspecifier_class!(\n    name = UnixDgramClass,\n    target = UnixDgram,\n    prefixes = [\"unix-dgram:\"],\n    arg_handling = {\n        fn construct(self: &UnixDgramClass, just_arg: &str) -> super::Result<Rc<dyn Specifier>> {\n            let splits: Vec<&str> = just_arg.split(':').collect();\n            if splits.len() != 2 {\n                Err(\"Expected two colon-separated paths\")?;\n            }\n            Ok(Rc::new(UnixDgram(splits[0].into(), splits[1].into())))\n        }\n        fn construct_overlay(\n            self: &UnixDgramClass,\n            _inner: Rc<dyn Specifier>,\n        ) -> super::Result<Rc<dyn Specifier>> {\n            panic!(\"Error: construct_overlay called on non-overlay specifier class\")\n        }\n    },\n    overlay = false,\n    MessageOriented,\n    SingleConnect,\n    help = r#\"\nSend packets to one path, receive from the other. [A]\nA socket for sending must be already opened.\n\nI don't know if this mode has any use, it is here just for completeness.\n\nExample:\n\n    socat unix-recv:./sender -&\n    websocat - unix-dgram:./receiver:./sender\n\"#\n);\n\nfn to_abstract(x: &str) -> PathBuf {\n    format!(\"\\x00{}\", x).into()\n}\n\n#[derive(Debug, Clone)]\npub struct AbstractConnect(pub String);\nimpl Specifier for AbstractConnect {\n    fn construct(&self, _: ConstructParams) -> PeerConstructor {\n        once(unix_connect_peer(&to_abstract(&self.0)))\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\nspecifier_class!(\n    name = AbstractConnectClass,\n    target = AbstractConnect,\n    prefixes = [\n        \"abstract:\",\n        \"abstract-connect:\",\n        \"connect-abstract:\",\n        \"abstract-c:\",\n        \"c-abstract:\"\n    ],\n    arg_handling = into,\n    overlay = false,\n    StreamOriented,\n    SingleConnect,\n    help = r#\"\nConnect to UNIX abstract-namespaced socket. Argument is some string used as address. [A]\n\nToo long addresses may be silently chopped off.\n\nExample: forward connections from websockets to an abstract stream socket\n\n    websocat ws-l:127.0.0.1:8088 abstract:the_socket\n\nNote that abstract-namespaced Linux sockets may not be normally supported by Rust,\nso non-prebuilt versions may have problems with them.\n\"#\n);\n\n#[derive(Debug, Clone)]\npub struct AbstractListen(pub String);\nimpl Specifier for AbstractListen {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        multi(unix_listen_peer(\n            &to_abstract(&self.0),\n            &cp.program_options,\n        ))\n    }\n    specifier_boilerplate!(noglobalstate multiconnect no_subspec);\n}\nspecifier_class!(\n    name = AbstractListenClass,\n    target = AbstractListen,\n    prefixes = [\n        \"abstract-listen:\",\n        \"listen-abstract:\",\n        \"abstract-l:\",\n        \"l-abstract:\"\n    ],\n    arg_handling = into,\n    overlay = false,\n    StreamOriented,\n    MultiConnect,\n    help = r#\"\nListen for connections on a specified abstract UNIX socket [A]\n\nExample: forward connections from an abstract UNIX socket to a WebSocket\n\n    websocat abstract-l:the_socket ws://127.0.0.1:8089\n\nNote that abstract-namespaced Linux sockets may not be normally supported by Rust,\nso non-prebuilt versions may have problems with them.\n\"#\n);\n\n#[derive(Debug, Clone)]\npub struct AbstractDgram(pub String, pub String);\nimpl Specifier for AbstractDgram {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        #[cfg(not(all(target_os = \"linux\", feature = \"workaround1\")))]\n        {\n            once(dgram_peer(\n                &to_abstract(&self.0),\n                &to_abstract(&self.1),\n                &p.program_options,\n            ))\n        }\n        #[cfg(all(target_os = \"linux\", feature = \"workaround1\"))]\n        {\n            once(dgram_peer_workaround(\n                &to_abstract(&self.0),\n                &to_abstract(&self.1),\n                &p.program_options,\n            ))\n        }\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\nspecifier_class!(\n    name = AbstractDgramClass,\n    target = AbstractDgram,\n    prefixes = [\"abstract-dgram:\"],\n    arg_handling = {\n        fn construct(self: &AbstractDgramClass, just_arg: &str) -> super::Result<Rc<dyn Specifier>> {\n            let splits: Vec<&str> = just_arg.split(':').collect();\n            if splits.len() != 2 {\n                Err(\"Expected two colon-separated addresses\")?;\n            }\n            Ok(Rc::new(UnixDgram(splits[0].into(), splits[1].into())))\n        }\n        fn construct_overlay(\n            self: &AbstractDgramClass,\n            _inner: Rc<dyn Specifier>,\n        ) -> super::Result<Rc<dyn Specifier>> {\n            panic!(\"Error: construct_overlay called on non-overlay specifier class\")\n        }\n    },\n    overlay = false,\n    MessageOriented,\n    SingleConnect,\n    help = r#\"\nSend packets to one address, receive from the other. [A]\nA socket for sending must be already opened.\n\nI don't know if this mode has any use, it is here just for completeness.\n\nExample (untested):\n\n    websocat - abstract-dgram:receiver_addr:sender_addr\n\nNote that abstract-namespaced Linux sockets may not be normally supported by Rust,\nso non-prebuilt versions may have problems with them. In particular, this mode\nmay fail to work without `workaround1` Cargo feature.\n\"#\n);\n\n#[cfg(all(target_os = \"linux\", feature = \"seqpacket\"))]\n#[path = \"unix_seqpacket_peer.rs\"]\npub mod unix_seqpacket_peer;\n\n// based on https://github.com/tokio-rs/tokio-core/blob/master/examples/proxy.rs\n#[derive(Clone)]\nstruct MyUnixStream(Rc<UnixStream>, bool);\n\nimpl Read for MyUnixStream {\n    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {\n        (&*self.0).read(buf)\n    }\n}\n\nimpl Write for MyUnixStream {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        (&*self.0).write(buf)\n    }\n\n    fn flush(&mut self) -> IoResult<()> {\n        Ok(())\n    }\n}\n\nimpl AsyncRead for MyUnixStream {}\n\nimpl AsyncWrite for MyUnixStream {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        self.0.shutdown(std::net::Shutdown::Write)?;\n        Ok(().into())\n    }\n}\n\nimpl Drop for MyUnixStream {\n    fn drop(&mut self) {\n        let i_am_read_part = self.1;\n        if i_am_read_part {\n            let _ = self.0.shutdown(std::net::Shutdown::Read);\n        }\n    }\n}\n\npub fn unix_connect_peer(addr: &Path) -> BoxedNewPeerFuture {\n    use futures::Future;\n    Box::new(\n        UnixStream::connect(addr)\n            .map(|x| {\n                info!(\"Connected to a unix socket\");\n                let x = Rc::new(x);\n                Peer::new(\n                    MyUnixStream(x.clone(), true),\n                    MyUnixStream(x.clone(), false),\n                    None /* TODO */,\n                )\n            })\n            .map_err(box_up_err),\n    ) as BoxedNewPeerFuture\n}\n\npub fn unix_listen_peer(addr: &Path, opts: &Rc<Options>) -> BoxedNewPeerStream {\n    let bound = if opts.unix_socket_accept_from_fd {\n        // Special mode for SystemD (untested yet)\n        let fdnum: libc::c_int = match addr.to_str().map(|x|x.parse()) {\n            Some(Ok(x)) => x,\n            _ => {\n                let e: Box<dyn std::error::Error> = From::from(\"Specify numeric argument instead of path in --accept-from-fd mode\");\n                return peer_err_sb(e);\n            }\n        };\n        use std::os::unix::io::FromRawFd;\n        let l = unsafe { std::os::unix::net::UnixListener::from_raw_fd(fdnum) } ;\n        let _ = l.set_nonblocking(true);\n\n        UnixListener::from_std(l, &tokio_reactor::Handle::default())\n    } else {\n        if opts.unlink_unix_socket {\n            let _ = ::std::fs::remove_file(addr);\n        };\n        let bound = UnixListener::bind(addr);\n        if opts.announce_listens {\n            let poss = addr.as_os_str();\n            use std::os::unix::ffi::OsStrExt;\n            if !poss.is_empty() && poss.as_bytes()[0] == b'\\0' {\n                println!(\"LISTEN proto=abstract,path_hex={}\", hex::encode(&poss.as_bytes()[1..]));\n            } else {\n                println!(\"LISTEN proto=unix,path={:?}\", addr);\n            }\n        }\n        bound\n    };\n    let bound = match bound {\n        Ok(x) => x,\n        Err(e) => return peer_err_s(e),\n    };\n    debug!(\"UNIX listening socket should be ready\");\n    // TODO: chmod\n    use tk_listen::ListenExt;\n    Box::new(\n        bound\n            .incoming()\n            .sleep_on_error(::std::time::Duration::from_millis(500))\n            .map(|x| {\n                info!(\"Incoming unix socket connection\");\n                let x = Rc::new(x);\n                Peer::new(\n                    MyUnixStream(x.clone(), true),\n                    MyUnixStream(x.clone(), false),\n                    None /* TODO */,\n                )\n            })\n            .map_err(|()| crate::simple_err2(\"unreachable error?\")),\n    ) as BoxedNewPeerStream\n}\n\nstruct DgramPeer {\n    s: UnixDatagram,\n    #[allow(unused)]\n    oneshot_mode: bool, // TODO\n}\n\n#[derive(Clone)]\nstruct DgramPeerHandle(Rc<RefCell<DgramPeer>>);\n\npub fn dgram_peer(bindaddr: &Path, connectaddr: &Path, opts: &Rc<Options>) -> BoxedNewPeerFuture {\n    Box::new(futures::future::result(\n        UnixDatagram::bind(bindaddr)\n            .and_then(|x| {\n                x.connect(connectaddr)?;\n\n                let h1 = DgramPeerHandle(Rc::new(RefCell::new(DgramPeer {\n                    s: x,\n                    oneshot_mode: opts.udp_oneshot_mode,\n                })));\n                let h2 = h1.clone();\n                Ok(Peer::new(h1, h2, None))\n            })\n            .map_err(box_up_err),\n    )) as BoxedNewPeerFuture\n}\n\n#[cfg(all(target_os = \"linux\", feature = \"workaround1\"))]\npub fn dgram_peer_workaround(\n    bindaddr: &Path,\n    connectaddr: &Path,\n    opts: &Rc<Options>,\n) -> BoxedNewPeerFuture {\n    info!(\"Workaround method for getting abstract datagram socket\");\n    fn getfd(bindaddr: &Path, connectaddr: &Path) -> Option<i32> {\n        use self::libc::{\n            bind, c_char, close, connect, sa_family_t, sockaddr_un, socket, socklen_t, AF_UNIX,\n            SOCK_DGRAM,\n        };\n        use std::mem::size_of;\n        use std::os::unix::ffi::OsStrExt;\n        unsafe {\n            let s = socket(AF_UNIX, SOCK_DGRAM, 0);\n            if s == -1 {\n                return None;\n            }\n            {\n                let mut sa = sockaddr_un {\n                    sun_family: AF_UNIX as sa_family_t,\n                    sun_path: [0; 108],\n                };\n                let bp: &[c_char] =\n                    &*(bindaddr.as_os_str().as_bytes() as *const [u8] as *const [c_char]);\n                let l = 108.min(bp.len());\n                sa.sun_path[..l].copy_from_slice(&bp[..l]);\n                let sa_len = l + size_of::<sa_family_t>();\n                let sa_ = &sa as *const self::libc::sockaddr_un as *const self::libc::sockaddr;\n                let ret = bind(s, sa_, sa_len as socklen_t);\n                if ret == -1 {\n                    close(s);\n                    return None;\n                }\n            }\n            {\n                let mut sa = sockaddr_un {\n                    sun_family: AF_UNIX as sa_family_t,\n                    sun_path: [0; 108],\n                };\n                let bp: &[c_char] =\n                    &*(connectaddr.as_os_str().as_bytes() as *const [u8] as *const [c_char]);\n                let l = 108.min(bp.len());\n                sa.sun_path[..l].copy_from_slice(&bp[..l]);\n                let sa_len = l + size_of::<sa_family_t>();\n                let sa_ = &sa as *const self::libc::sockaddr_un as *const self::libc::sockaddr;\n                let ret = connect(s, sa_, sa_len as socklen_t);\n                if ret == -1 {\n                    close(s);\n                    return None;\n                }\n            }\n            Some(s)\n        }\n    }\n    fn getpeer(\n        bindaddr: &Path,\n        connectaddr: &Path,\n        opts: &Rc<Options>,\n    ) -> Result<Peer, Box<dyn (::std::error::Error)>> {\n        if let Some(fd) = getfd(bindaddr, connectaddr) {\n            let s: ::std::os::unix::net::UnixDatagram =\n                unsafe { ::std::os::unix::io::FromRawFd::from_raw_fd(fd) };\n            let ss = UnixDatagram::from_std(s, &tokio_reactor::Handle::default())?;\n            let h1 = DgramPeerHandle(Rc::new(RefCell::new(DgramPeer {\n                s: ss,\n                oneshot_mode: opts.udp_oneshot_mode,\n            })));\n            let h2 = h1.clone();\n            Ok(Peer::new(h1, h2, None))\n        } else {\n            Err(\"Failed to get, bind or connect socket\")?\n        }\n    }\n    Box::new(futures::future::result({\n        getpeer(bindaddr, connectaddr, opts)\n    })) as BoxedNewPeerFuture\n}\n\nimpl Read for DgramPeerHandle {\n    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {\n        let p = self.0.borrow_mut();\n        p.s.recv(buf)\n    }\n}\n\nimpl Write for DgramPeerHandle {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        let p = self.0.borrow_mut();\n        p.s.send(buf)\n    }\n\n    fn flush(&mut self) -> IoResult<()> {\n        Ok(())\n    }\n}\n\nimpl AsyncRead for DgramPeerHandle {}\n\nimpl AsyncWrite for DgramPeerHandle {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        Ok(().into())\n    }\n}\n\ntrait HacksForMigratingFromTokioCore {\n    fn recv(&self, buf: &mut [u8]) -> std::io::Result<usize>;\n    fn send(&self, buf: &[u8]) -> std::io::Result<usize>;\n}\n\nimpl HacksForMigratingFromTokioCore for UnixDatagram {\n    fn recv(&self, buf: &mut [u8]) -> std::io::Result<usize> {\n        match self.poll_recv(buf)? {\n            futures::Async::Ready(n) => Ok(n),\n            futures::Async::NotReady => Err(std::io::ErrorKind::WouldBlock.into()),\n        }\n    }\n\n    fn send(&self, buf: &[u8]) -> std::io::Result<usize> {\n        match self.poll_send(buf)? {\n            futures::Async::Ready(n) => Ok(n),\n            futures::Async::NotReady => Err(std::io::ErrorKind::WouldBlock.into()),\n        }\n    }\n}\n"
  },
  {
    "path": "src/unix_seqpacket_peer.rs",
    "content": "extern crate tokio_reactor;\n\nuse super::{\n    futures, libc, multi, once, peer_err_s, simple_err, BoxedNewPeerFuture, BoxedNewPeerStream,\n    ConstructParams, MyUnixStream, Options, Peer, PeerConstructor, Specifier, UnixListener,\n    UnixStream,\n};\nuse futures::Stream;\nuse std::path::{Path, PathBuf};\nuse std::rc::Rc;\n\n#[derive(Debug, Clone)]\npub struct SeqpacketConnect(pub PathBuf);\nimpl Specifier for SeqpacketConnect {\n    fn construct(&self, _: ConstructParams) -> PeerConstructor {\n        once(seqpacket_connect_peer(&self.0))\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\nspecifier_class!(\n    name = SeqpacketConnectClass,\n    target = SeqpacketConnect,\n    prefixes = [\n        \"seqpacket:\",\n        \"seqpacket-connect:\",\n        \"connect-seqpacket:\",\n        \"seqpacket-c:\",\n        \"c-seqpacket:\"\n    ],\n    arg_handling = into,\n    overlay = false,\n    MessageOriented,\n    SingleConnect,\n    help = r#\"\nConnect to AF_UNIX SOCK_SEQPACKET socket. Argument is a filesystem path. [A]\n\nStart the path with `@` character to make it connect to abstract-namespaced socket instead.\n\nToo long paths are silently truncated.\n\nExample: forward connections from websockets to a UNIX seqpacket abstract socket\n\n    websocat ws-l:127.0.0.1:1234 seqpacket:@test\n\"#\n);\n\n#[derive(Debug, Clone)]\npub struct SeqpacketListen(pub PathBuf);\nimpl Specifier for SeqpacketListen {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        multi(seqpacket_listen_peer(&self.0, &p.program_options))\n    }\n    specifier_boilerplate!(noglobalstate multiconnect no_subspec);\n}\nspecifier_class!(\n    name = SeqpacketListenClass,\n    target = SeqpacketListen,\n    prefixes = [\n        \"seqpacket-listen:\",\n        \"listen-seqpacket:\",\n        \"seqpacket-l:\",\n        \"l-seqpacket:\"\n    ],\n    arg_handling = into,\n    overlay = false,\n    MessageOriented,\n    MultiConnect,\n    help = r#\"\nListen for connections on a specified AF_UNIX SOCK_SEQPACKET socket [A]\n\nStart the path with `@` character to make it connect to abstract-namespaced socket instead.\n\nToo long (>=108 bytes) paths are silently truncated.\n\nExample: forward connections from a UNIX seqpacket socket to a WebSocket\n\n    websocat --unlink seqpacket-l:the_socket ws://127.0.0.1:8089\n\"#\n);\n\npub fn seqpacket_connect_peer(addr: &Path) -> BoxedNewPeerFuture {\n    fn getfd(addr: &Path) -> Option<i32> {\n        use self::libc::{\n            c_char, close, connect, sa_family_t, sockaddr_un, socket, socklen_t, AF_UNIX,\n            SOCK_SEQPACKET,\n        };\n        use std::mem::size_of;\n        use std::os::unix::ffi::OsStrExt;\n        unsafe {\n            let s = socket(AF_UNIX, SOCK_SEQPACKET, 0);\n            if s == -1 {\n                return None;\n            }\n            {\n                let mut sa = sockaddr_un {\n                    sun_family: AF_UNIX as sa_family_t,\n                    sun_path: [0; 108],\n                };\n                let bp: &[c_char] =\n                    &*(addr.as_os_str().as_bytes() as *const [u8] as *const [c_char]);\n                let l = 108.min(bp.len());\n                sa.sun_path[..l].copy_from_slice(&bp[..l]);\n                if sa.sun_path[0] == b'@' as c_char {\n                    sa.sun_path[0] = b'\\x00' as c_char;\n                }\n                let sa_len = l + size_of::<sa_family_t>();\n                let sa_ = &sa as *const self::libc::sockaddr_un as *const self::libc::sockaddr;\n                let ret = connect(s, sa_, sa_len as socklen_t);\n                if ret == -1 {\n                    close(s);\n                    return None;\n                }\n            }\n            Some(s)\n        }\n    }\n    fn getpeer(addr: &Path) -> Result<Peer, Box<dyn (::std::error::Error)>> {\n        if let Some(fd) = getfd(addr) {\n            let s: ::std::os::unix::net::UnixStream =\n                unsafe { ::std::os::unix::io::FromRawFd::from_raw_fd(fd) };\n            let ss = UnixStream::from_std(s, &tokio_reactor::Handle::default())?;\n            let x = Rc::new(ss);\n            Ok(Peer::new(\n                MyUnixStream(x.clone(), true),\n                MyUnixStream(x.clone(), false),\n                None /* TODO*/ ,\n            ))\n        } else {\n            Err(\"Failed to get or connect socket\")?\n        }\n    }\n    Box::new(futures::future::result(getpeer(addr))) as BoxedNewPeerFuture\n}\n\npub fn seqpacket_listen_peer(addr: &Path, opts: &Rc<Options>) -> BoxedNewPeerStream {\n    fn getfd(addr: &Path, opts: &Rc<Options>) -> Option<i32> {\n        use self::libc::{\n            bind, c_char, close, listen, sa_family_t, sockaddr_un, socket, socklen_t, unlink,\n            AF_UNIX, SOCK_SEQPACKET,\n        };\n        use std::mem::size_of;\n        use std::os::unix::ffi::OsStrExt;\n        unsafe {\n            let s = socket(AF_UNIX, SOCK_SEQPACKET, 0);\n            if s == -1 {\n                return None;\n            }\n            {\n                let mut sa = sockaddr_un {\n                    sun_family: AF_UNIX as sa_family_t,\n                    sun_path: [0; 108],\n                };\n                let bp: &[c_char] =\n                    &*(addr.as_os_str().as_bytes() as *const [u8] as *const [c_char]);\n\n                let l = 108.min(bp.len());\n                sa.sun_path[..l].copy_from_slice(&bp[..l]);\n                if sa.sun_path[0] == b'@' as c_char {\n                    sa.sun_path[0] = b'\\x00' as c_char;\n                } else if opts.unlink_unix_socket {\n                    sa.sun_path[107] = 0;\n                    unlink(&sa.sun_path as *const c_char);\n                }\n                let sa_len = l + size_of::<sa_family_t>();\n                let sa_ = &sa as *const self::libc::sockaddr_un as *const self::libc::sockaddr;\n                let ret = bind(s, sa_, sa_len as socklen_t);\n                if ret == -1 {\n                    close(s);\n                    return None;\n                }\n            }\n            {\n                let ret = listen(s, 50);\n                if ret == -1 {\n                    close(s);\n                    return None;\n                }\n            }\n            if opts.announce_listens {\n                // too lazy to actually handle '\"@'  vs '@\"' here - is seqpacket even used by somebody around?\n                let s = format!(\"LISTEN proto=unix_seqpacket,path={:?}\", addr);\n                if s.contains(\"path=\\\"@\") {\n                    warn!(\"that particular LISTEN line format should be changed in future Websocat version\");\n                }\n                println!(\"{}\", s);\n            }\n            Some(s)\n        }\n    }\n    let fd = match getfd(addr, opts) {\n        Some(x) => x,\n        None => return peer_err_s(simple_err(\"Failed to get or bind socket\".into())),\n    };\n    let l1: ::std::os::unix::net::UnixListener =\n        unsafe { ::std::os::unix::io::FromRawFd::from_raw_fd(fd) };\n    let bound = match UnixListener::from_std(l1, &tokio_reactor::Handle::default()) {\n        Ok(x) => x,\n        Err(e) => return peer_err_s(e),\n    };\n    use tk_listen::ListenExt;\n    Box::new(\n        bound\n            .incoming()\n            .sleep_on_error(::std::time::Duration::from_millis(500))\n            .map(|x| {\n                info!(\"Incoming unix socket connection\");\n                let x = Rc::new(x);\n                Peer::new(\n                    MyUnixStream(x.clone(), true),\n                    MyUnixStream(x.clone(), false),\n                    None /* TODO*/ ,\n                )\n            })\n            .map_err(|()| crate::simple_err2(\"unreachable error?\")),\n    ) as BoxedNewPeerStream\n}\n"
  },
  {
    "path": "src/util.rs",
    "content": "use super::{\n    futures, AsyncRead, AsyncWrite, BoxedNewPeerFuture, BoxedNewPeerStream, L2rUser, Peer,\n    PeerConstructor, Rc, HupToken,\n};\nuse super::{Future, Stream};\n\npub fn wouldblock<T>() -> std::io::Result<T> {\n    Err(std::io::Error::new(std::io::ErrorKind::WouldBlock, \"\"))\n}\npub fn brokenpipe<T>() -> std::io::Result<T> {\n    Err(std::io::Error::new(std::io::ErrorKind::BrokenPipe, \"\"))\n}\npub fn io_other_error<E: std::error::Error + Send + Sync + 'static>(e: E) -> std::io::Error {\n    std::io::Error::new(std::io::ErrorKind::Other, e)\n}\n\n#[allow(clippy::redundant_closure)]\nimpl PeerConstructor {\n    pub fn map<F>(self, func: F) -> Self\n    where\n        F: Fn(Peer, L2rUser) -> BoxedNewPeerFuture + 'static,\n    {\n        let f = Rc::new(func);\n        use crate::PeerConstructor::*;\n        match self {\n            Error(e) => Error(e),\n            ServeOnce(x) => Overlay1(x, f),\n            ServeMultipleTimes(s) => OverlayM(s, f),\n            Overlay1(x, mapper) => Overlay1(\n                x,\n                Rc::new(move |p, l2r| {\n                    let ff = f.clone();\n                    let l2rc = l2r.clone();\n                    Box::new(mapper(p, l2r).and_then(move |x| ff(x, l2rc)))\n                }),\n            ),\n            OverlayM(x, mapper) => OverlayM(\n                x,\n                Rc::new(move |p, l2r| {\n                    let ff = f.clone();\n                    let l2rc = l2r.clone();\n                    Box::new(mapper(p, l2r).and_then(move |x| ff(x, l2rc)))\n                }),\n            ), // This implementation (without Overlay{1,M} cases)\n               // causes task to be spawned too late (before establishing ws upgrade)\n               // when serving clients:\n\n               //ServeOnce(x) => ServeOnce(Box::new(x.and_then(f)) as BoxedNewPeerFuture),\n               //ServeMultipleTimes(s) => {\n               //    ServeMultipleTimes(Box::new(s.and_then(f)) as BoxedNewPeerStream)\n               //}\n        }\n    }\n\n    pub fn get_only_first_conn(self, l2r: L2rUser) -> BoxedNewPeerFuture {\n        use crate::PeerConstructor::*;\n        match self {\n            Error(e) => Box::new(futures::future::err(e)) as BoxedNewPeerFuture,\n            ServeMultipleTimes(stre) => Box::new(\n                stre.into_future()\n                    .map(move |(std_peer, _)| std_peer.expect(\"Nowhere to connect it\"))\n                    .map_err(|(e, _)| e),\n            ) as BoxedNewPeerFuture,\n            ServeOnce(futur) => futur,\n            Overlay1(futur, mapper) => {\n                Box::new(futur.and_then(move |p| mapper(p, l2r))) as BoxedNewPeerFuture\n            }\n            OverlayM(stre, mapper) => Box::new(\n                stre.into_future()\n                    .map(move |(std_peer, _)| std_peer.expect(\"Nowhere to connect it\"))\n                    .map_err(|(e, _)| e)\n                    .and_then(move |p| mapper(p, l2r)),\n            ) as BoxedNewPeerFuture,\n        }\n    }\n}\n\npub fn once(x: BoxedNewPeerFuture) -> PeerConstructor {\n    PeerConstructor::ServeOnce(x)\n}\npub fn multi(x: BoxedNewPeerStream) -> PeerConstructor {\n    PeerConstructor::ServeMultipleTimes(x)\n}\n\npub fn peer_err<E: std::error::Error + 'static>(e: E) -> BoxedNewPeerFuture {\n    Box::new(futures::future::err(\n        Box::new(e) as Box<dyn std::error::Error>\n    )) as BoxedNewPeerFuture\n}\npub fn peer_err2(e: Box<dyn std::error::Error>) -> BoxedNewPeerFuture {\n    Box::new(futures::future::err(\n        e\n    )) as BoxedNewPeerFuture\n}\npub fn peer_err_s<E: std::error::Error + 'static>(e: E) -> BoxedNewPeerStream {\n    Box::new(futures::stream::iter_result(vec![Err(\n        Box::new(e) as Box<dyn std::error::Error>\n    )])) as BoxedNewPeerStream\n}\npub fn peer_err_sb(e: Box<dyn std::error::Error + 'static>) -> BoxedNewPeerStream {\n    Box::new(futures::stream::iter_result(vec![Err(\n        e\n    )])) as BoxedNewPeerStream\n}\npub fn peer_strerr(e: &str) -> BoxedNewPeerFuture {\n    let q: Box<dyn std::error::Error> = From::from(e);\n    Box::new(futures::future::err(q)) as BoxedNewPeerFuture\n}\npub fn simple_err(e: String) -> std::io::Error {\n    let e1: Box<dyn std::error::Error + Send + Sync> = e.into();\n    ::std::io::Error::new(::std::io::ErrorKind::Other, e1)\n}\npub fn simple_err2(e: &'static str) -> Box<dyn std::error::Error> {\n    let e1: Box<dyn std::error::Error + Send + Sync> = e.to_string().into();\n    e1 as Box<dyn std::error::Error>\n}\npub fn box_up_err<E: std::error::Error + 'static>(e: E) -> Box<dyn std::error::Error> {\n    Box::new(e) as Box<dyn std::error::Error>\n}\n\nimpl Peer {\n    pub fn new<R: AsyncRead + 'static, W: AsyncWrite + 'static>(r: R, w: W, hup: Option<HupToken>) -> Self {\n        Peer(\n            Box::new(r) as Box<dyn AsyncRead>,\n            Box::new(w) as Box<dyn AsyncWrite>,\n            hup,\n        )\n    }\n}\n"
  },
  {
    "path": "src/windows_np_peer.rs",
    "content": "extern crate tokio_named_pipes;\n\nuse futures;\nuse std;\nuse std::io::Result as IoResult;\nuse std::io::{Read, Write};\nuse tokio_io::{AsyncRead, AsyncWrite};\nuse std::path::{Path, PathBuf};\n\n//use super::{L2rUser, LeftSpecToRightSpec};\n\nuse std::cell::RefCell;\nuse std::rc::Rc;\n\nuse tokio_named_pipes::NamedPipe;\n\n\nuse super::{once, ConstructParams, PeerConstructor, Specifier};\nuse super::{BoxedNewPeerFuture, Peer, Result};\n\n#[derive(Debug, Clone)]\npub struct NamedPipeConnect(pub PathBuf);\nimpl Specifier for NamedPipeConnect {\n    fn construct(&self, _p: ConstructParams) -> PeerConstructor {\n        once(Box::new(futures::future::result(named_pipe_connect_peer(&self.0))) as BoxedNewPeerFuture)\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec );\n}\nspecifier_class!(\n    name = NamedPipeConnectClass,\n    target = NamedPipeConnect,\n    prefixes = [\"namedpipeconnect:\"],\n    arg_handling = into,\n    overlay = false,\n    StreamOriented,\n    SingleConnect,\n    help = r#\"\nConnect to a named pipe on Windows\n\nExample:\n\n    websocat ws-l:127.0.0.1:8000 namedpipeconnect:\\\\.\\pipe\\Pipe\n\n\"#\n);\n\nfn named_pipe_connect_peer(\n    path: &Path,\n) -> Result<Peer> {\n    let pipe = NamedPipe::new(path, &tokio::reactor::Handle::default())?;\n    let ph = NamedPipeConnectPeer(Rc::new(RefCell::new(pipe)));\n    Ok(Peer::new(ph.clone(), ph, None))   \n}\n\n#[derive(Clone)]\nstruct NamedPipeConnectPeer(Rc<RefCell<NamedPipe>>);\n\nimpl Read for NamedPipeConnectPeer {\n    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {\n        self.0\n            .borrow_mut()\n            .read(buf)\n    }\n}\n\nimpl Write for NamedPipeConnectPeer {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        self.0\n            .borrow_mut()\n            .write(buf)\n    }\n\n    fn flush(&mut self) -> IoResult<()> {\n        self.0\n            .borrow_mut()\n            .flush()\n    }\n}\n\nimpl AsyncRead for NamedPipeConnectPeer {}\n\nimpl AsyncWrite for NamedPipeConnectPeer {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        self\n            .0\n            .borrow_mut()\n            .shutdown()\n    }\n}\n"
  },
  {
    "path": "src/ws_client_peer.rs",
    "content": "extern crate hyper;\nextern crate websocket;\n\nuse self::websocket::client::r#async::ClientNew;\nuse self::websocket::stream::r#async::Stream as WsStream;\nuse self::websocket::ClientBuilder;\nuse futures::future::Future;\n\nuse std::net::SocketAddr;\nuse std::rc::Rc;\n\nuse self::websocket::client::Url;\n\nuse super::{box_up_err, peer_err, peer_strerr, BoxedNewPeerFuture, Peer, Result};\n\nuse super::ws_peer::PeerForWs;\nuse super::{once, ConstructParams, Options, PeerConstructor, Specifier};\n\nuse self::hyper::header::Headers;\n\n#[derive(Debug, Clone)]\npub struct WsClient(pub Url);\nimpl Specifier for WsClient {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        let url = self.0.clone();\n        once(get_ws_client_peer(&url, p.program_options))\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\nspecifier_class!(\n    name = WsClientClass,\n    target = WsClient,\n    prefixes = [\"ws://\"],\n    arg_handling = {\n        fn construct(self: &WsClientClass, arg: &str) -> super::Result<Rc<dyn Specifier>> {\n            Ok(Rc::new(WsClient(format!(\"ws:{}\", arg).parse()?)))\n        }\n        fn construct_overlay(\n            self: &WsClientClass,\n            _inner: Rc<dyn Specifier>,\n        ) -> super::Result<Rc<dyn Specifier>> {\n            panic!(\"Error: construct_overlay called on non-overlay specifier class\")\n        }\n    },\n    overlay = false,\n    MessageOriented,\n    SingleConnect,\n    help = r#\"\nInsecure (ws://) WebSocket client. Argument is host and URL.\n\nExample: connect to public WebSocket loopback and copy binary chunks from stdin to the websocket.\n\n    websocat - ws://echo.websocket.org/\n\"#\n);\n\n#[cfg(feature = \"ssl\")]\n#[derive(Debug, Clone)]\npub struct WsClientSecure(pub Url);\n#[cfg(feature = \"ssl\")]\nimpl Specifier for WsClientSecure {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        let url = self.0.clone();\n        once(get_ws_client_peer(&url, p.program_options))\n    }\n    specifier_boilerplate!(noglobalstate singleconnect no_subspec);\n}\n#[cfg(feature = \"ssl\")]\nspecifier_class!(\n    name = WsClientSecureClass,\n    target = WsClientSecure,\n    prefixes = [\"wss://\"],\n    arg_handling = {\n        fn construct(self: &WsClientSecureClass, arg: &str) -> super::Result<Rc<dyn Specifier>> {\n            Ok(Rc::new(WsClient(format!(\"wss:{}\", arg).parse()?)))\n        }\n        fn construct_overlay(\n            self: &WsClientSecureClass,\n            _inner: Rc<dyn Specifier>,\n        ) -> super::Result<Rc<dyn Specifier>> {\n            panic!(\"Error: construct_overlay called on non-overlay specifier class\")\n        }\n    },\n    overlay = false,\n    MessageOriented,\n    SingleConnect,\n    help = r#\"\nSecure (wss://) WebSocket client. Argument is host and URL.\n\nExample: forward TCP port 4554 to a websocket\n\n    websocat tcp-l:127.0.0.1:4554 wss://127.0.0.1/some_websocket\"#\n);\n\n#[derive(Debug)]\npub struct WsConnect<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for WsConnect<T> {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(p.clone());\n\n        let url: Url = match p.program_options.ws_c_uri.parse() {\n            Ok(x) => x,\n            Err(e) => return PeerConstructor::ServeOnce(peer_err(e)),\n        };\n\n        let opts = p.program_options;\n\n        inner.map(move |q, _| get_ws_client_peer_wrapped(&url, q, opts.clone()))\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = WsConnectClass,\n    target = WsConnect,\n    prefixes = [\"ws-c:\", \"c-ws:\", \"ws-connect:\", \"connect-ws:\"],\n    arg_handling = subspec,\n    overlay = true,\n    MessageOriented,\n    MulticonnectnessDependsOnInnerType,\n    help = r#\"\nLow-level WebSocket connector. Argument is a some another address. [A]\n\nURL and Host: header being sent are independent from the underlying connection.\n\nExample: connect to echo server in more explicit way\n\n    websocat --ws-c-uri=ws://echo.websocket.org/ - ws-c:tcp:174.129.224.73:80\n\nExample: connect to echo server, observing WebSocket TCP packet exchange\n\n    websocat --ws-c-uri=ws://echo.websocket.org/ - ws-c:cmd:\"socat -v -x - tcp:174.129.224.73:80\"\n\n\"#\n);\n\nfn get_ws_client_peer_impl<S, F>(uri: &Url, opts: Rc<Options>, f: F) -> BoxedNewPeerFuture\nwhere\n    S: WsStream + Send + 'static,\n    F: FnOnce(ClientBuilder) -> Result<ClientNew<S>>,\n{\n    let stage1 = ClientBuilder::from_url(uri);\n    let stage2 = if opts.custom_headers.is_empty() {\n        stage1\n    } else {\n        let mut h = Headers::new();\n        for (hn, hv) in opts.custom_headers.clone() {\n            h.append_raw(hn, hv);\n        }\n        stage1.custom_headers(&h)\n    };\n    let stage3 = if let Some(ref x) = opts.origin {\n        stage2.origin(x.clone())\n    } else {\n        stage2\n    };\n    let stage4 = if let Some(ref p) = opts.websocket_protocol {\n        stage3.add_protocol(p.to_owned())\n    } else {\n        stage3\n    };\n    let stage5 = if let Some(ref v) = opts.websocket_version {\n        stage4.version(websocket::header::WebSocketVersion::Unknown(v.clone()))\n    } else {\n        stage4\n    };\n    let stage6 = stage5.max_dataframe_size(opts.max_ws_frame_length).max_message_size(opts.max_ws_message_length);\n    let after_connect = match f(stage6) {\n        Ok(x) => x,\n        Err(_) => return peer_strerr(\"Failed to make TLS connector\"),\n    };\n    Box::new(\n        after_connect\n            .map(move |(duplex, headers)| {\n                info!(\"Connected to ws, response headers: {:?}\", headers);\n                let close_on_shutdown = !opts.websocket_dont_close;\n                super::ws_peer::finish_building_ws_peer(&opts, duplex, close_on_shutdown, None)\n            })\n            .map_err(box_up_err),\n    ) as BoxedNewPeerFuture\n}\n\npub fn get_ws_client_peer(uri: &Url, opts: Rc<Options>) -> BoxedNewPeerFuture {\n    info!(\"get_ws_client_peer\");\n\n    #[allow(unused)]\n    let tls_insecure = opts.tls_insecure;\n    #[allow(unused)]\n    let client_ident = opts.client_pkcs12_der.clone();\n    #[allow(unused)]\n    let client_ident_passwd = opts.client_pkcs12_passwd.clone();\n\n    get_ws_client_peer_impl(uri, opts, |before_connect| {\n        #[cfg(feature = \"ssl\")]\n        let mut builder_ = super::ssl_peer::native_tls::TlsConnector::builder();\n        #[cfg(feature = \"ssl\")]\n        let builder = builder_\n            .danger_accept_invalid_certs(tls_insecure)\n            .danger_accept_invalid_hostnames(tls_insecure);\n\n        #[cfg(feature = \"ssl\")]\n        let after_connect = {\n            let identity = if let Some(client_ident) = client_ident {\n                super::ssl_peer::native_tls::Identity::from_pkcs12(\n                    &client_ident,\n                    &client_ident_passwd.unwrap_or(\"\".to_string()),\n                )\n                .map_err(|e| {\n                    error!(\n                        \"Unable to parse client identity: {}\\nContinuing without a client identity\",\n                        e\n                    )\n                })\n                .ok()\n            } else {\n                None\n            };\n\n            let tls_opts = if let Some(client_ident) = identity {\n                debug!(\"Adding client identity to the TLS connection\");\n                Some(builder.identity(client_ident).build()?)\n            } else {\n                Some(builder.build()?)\n            };\n            before_connect.async_connect_with_cb(tls_opts, |addrs| {\n                let addrs : Vec<SocketAddr> = addrs.collect();\n                let r = crate::net_peer::tcp_race(&addrs);\n                let r = r.map_err(|e|{\n                    websocket::WebSocketError::Other(e)\n                });\n                Box::new(r)\n            })\n        };\n        /// FIXME: happy eyeballs without TLS support\n        #[cfg(not(feature = \"ssl\"))]\n        let after_connect = before_connect.async_connect_insecure();\n        Ok(after_connect)\n    })\n}\n\nunsafe impl Send for PeerForWs {\n    //! https://github.com/cyderize/rust-websocket/issues/168\n}\n\npub fn get_ws_client_peer_wrapped(uri: &Url, inner: Peer, opts: Rc<Options>) -> BoxedNewPeerFuture {\n    info!(\"get_ws_client_peer_wrapped\");\n    get_ws_client_peer_impl(uri, opts, |before_connect| {\n        Ok(before_connect.async_connect_on(PeerForWs(inner)))\n    })\n}\n"
  },
  {
    "path": "src/ws_lowlevel_peer.rs",
    "content": "#![allow(unused)]\n\nextern crate websocket_base;\n\nuse futures::future::Future;\nuse futures::stream::Stream;\n\nuse std::cell::RefCell;\nuse std::rc::Rc;\n\nuse super::{box_up_err, peer_err, peer_strerr, BoxedNewPeerFuture, Peer, Result};\n\nuse super::ws_peer::{Mode1, PeerForWs, WsReadWrapper, WsWriteWrapper};\nuse super::{once, ConstructParams, Options, PeerConstructor, Specifier};\n\nuse self::websocket_base::codec::ws::Context as WsLlContext;\n\n#[derive(Debug, Clone)]\npub struct WsLlClient<T: Specifier>(pub T);\nimpl<T:Specifier> Specifier for WsLlClient<T> {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(p.clone());\n        let opts = p.program_options;\n        inner.map(move |q, _| get_ws_lowlevel_peer(\n            WsLlContext::Client,\n            q,\n            opts.clone(),\n        ))\n    }\n    specifier_boilerplate!(noglobalstate singleconnect has_subspec);\n}\nspecifier_class!(\n    name = WsLlClientClass,\n    target = WsLlClient,\n    prefixes = [\"ws-lowlevel-client:\",\"ws-ll-client:\",\"ws-ll-c:\"],\n    arg_handling = subspec,\n    overlay = false,\n    MessageOriented,\n    SingleConnect,\n    help = r#\"\n[A] Low-level HTTP-independent WebSocket client connection without associated HTTP upgrade.\n\nExample: TODO\n\"#\n);\n\n#[derive(Debug, Clone)]\npub struct WsLlServer<T: Specifier>(pub T);\nimpl<T:Specifier> Specifier for WsLlServer<T> {\n    fn construct(&self, p: ConstructParams) -> PeerConstructor {\n        let inner = self.0.construct(p.clone());\n        let opts = p.program_options;\n        inner.map(move |q, _| get_ws_lowlevel_peer(\n            WsLlContext::Server,\n            q,\n            opts.clone(),\n        ))\n    }\n    specifier_boilerplate!(noglobalstate singleconnect has_subspec);\n}\nspecifier_class!(\n    name = WsLlServerClass,\n    target = WsLlServer,\n    prefixes = [\"ws-lowlevel-server:\",\"ws-ll-server:\",\"ws-ll-s:\"],\n    arg_handling = subspec,\n    overlay = false,\n    MessageOriented,\n    SingleConnect,\n    help = r#\"\n[A] Low-level HTTP-independent WebSocket server connection without associated HTTP upgrade.\n\nExample: TODO\n\"#\n);\n\npub fn get_ws_lowlevel_peer(mode: WsLlContext, mut inner: Peer, opts: Rc<Options>) -> BoxedNewPeerFuture {\n    info!(\"get_ws_lowlevel_peer\");\n    \n    use ::tokio_codec::Decoder;\n\n    let c = websocket_base::codec::ws::MessageCodec::new_with_limits(mode, opts.max_ws_frame_length, opts.max_ws_message_length);\n    let hup = inner.2;\n    inner.2 = None;\n    let duplex = c.framed(PeerForWs(inner));\n\n    let close_on_shutdown =  !opts.websocket_dont_close;\n    let p = super::ws_peer::finish_building_ws_peer(&opts, duplex, close_on_shutdown, hup);\n\n    Box::new(\n        ::futures::future::ok(p)\n    ) as BoxedNewPeerFuture\n}\n"
  },
  {
    "path": "src/ws_peer.rs",
    "content": "extern crate tokio_codec;\nextern crate websocket;\nextern crate base64;\n\nuse self::websocket::stream::r#async::Stream as WsStream;\nuse self::websocket::OwnedMessage;\nuse futures;\nuse futures::sink::Sink;\nuse futures::stream::Stream;\nuse std;\nuse std::io::Result as IoResult;\nuse std::io::{Read, Write};\nuse tokio_io::{AsyncRead, AsyncWrite};\n\nuse std::cell::RefCell;\nuse std::rc::Rc;\n\nuse futures::Async::{NotReady, Ready};\n\nuse super::{brokenpipe, io_other_error, wouldblock, Peer, HupToken};\n\nuse super::readdebt::{ProcessMessageResult, ReadDebt};\n\ntype MultiProducerWsSink<T> = Rc<\n    RefCell<\n        WsSinkWithOneBufferedMessage<T>,\n    >,\n>;\ntype WsSource<T> = futures::stream::SplitStream<\n    tokio_codec::Framed<T, websocket::r#async::MessageCodec<websocket::OwnedMessage>>,\n>;\npub struct WsSinkWithOneBufferedMessage<T> {\n    sink: futures::stream::SplitSink<tokio_codec::Framed<T, websocket::r#async::MessageCodec<websocket::OwnedMessage>>>,\n    pong_debt: Option<websocket_base::OwnedMessage>,\n    ping_debt: Option<websocket_base::OwnedMessage>,\n}\n\n#[derive(Copy,Clone,PartialEq, Eq)]\npub enum CompressionMethod {\n    None,\n    Deflate,\n    Zlib,\n    Gzip,\n}\n\nimpl CompressionMethod {\n    #[cfg(feature=\"compression\")]\n    fn uncompress(&self, x: Vec<u8>) -> Vec<u8> {\n        if self == &CompressionMethod::None {\n            return x;\n        }\n\n        let l = x.len();\n        let mut y = Vec::with_capacity(l*2);\n        match self {\n            CompressionMethod::None => unreachable!(),\n            CompressionMethod::Gzip => {\n                let mut t = flate2::read::GzDecoder::new(std::io::Cursor::new(x));\n                match t.read_to_end(&mut y) {\n                    Ok(_) => (),\n                    Err(e) => {\n                        error!(\"Error uncompressing data: {}\", e);\n                    }\n                }\n            }\n            CompressionMethod::Deflate => {\n                let mut t = flate2::read::DeflateDecoder::new(std::io::Cursor::new(x));\n                match t.read_to_end(&mut y) {\n                    Ok(_) => (),\n                    Err(e) => {\n                        error!(\"Error uncompressing data: {}\", e);\n                    }\n                }\n            }\n            CompressionMethod::Zlib =>{\n                let mut t = flate2::read::ZlibDecoder::new(std::io::Cursor::new(x));\n                match t.read_to_end(&mut y) {\n                    Ok(_) => (),\n                    Err(e) => {\n                        error!(\"Error uncompressing data: {}\", e);\n                    }\n                }\n            }\n        }\n        debug!(\"Uncompressed {} bytes into {} bytes\", l, y.len());\n        y\n    }\n\n\n    #[cfg(feature=\"compression\")]\n    fn compress(&self, x: Vec<u8>) -> Vec<u8> {\n        if self == &CompressionMethod::None {\n            return x;\n        }\n\n        let l = x.len();\n        let mut y = Vec::with_capacity(l+64);\n        let c = flate2::Compression::new(6);\n        match self {\n            CompressionMethod::None => unreachable!(),\n            CompressionMethod::Gzip => {\n                let mut t = flate2::read::GzEncoder::new(std::io::Cursor::new(x), c);\n                match t.read_to_end(&mut y) {\n                    Ok(_) => (),\n                    Err(e) => {\n                        error!(\"Error compressing data: {}\", e);\n                    }\n                }\n            }\n            CompressionMethod::Deflate => {\n                let mut t = flate2::read::DeflateEncoder::new(std::io::Cursor::new(x), c);\n                match t.read_to_end(&mut y) {\n                    Ok(_) => (),\n                    Err(e) => {\n                        error!(\"Error compressing data: {}\", e);\n                    }\n                }\n            }\n            CompressionMethod::Zlib =>{\n                let mut t = flate2::read::ZlibEncoder::new(std::io::Cursor::new(x), c);\n                match t.read_to_end(&mut y) {\n                    Ok(_) => (),\n                    Err(e) => {\n                        error!(\"Error compressing data: {}\", e);\n                    }\n                }\n            }\n        }\n        debug!(\"Compressed {} bytes into {} bytes\", l, y.len());\n        y\n    }\n\n    #[cfg(not(feature=\"compression\"))]\n    fn uncompress(&self, x: Vec<u8>) -> Vec<u8> {\n        if self == &CompressionMethod::None {\n            return x;\n        }\n\n        error!(\"Compression support is not selected during Websocat compilation\");\n        vec![]\n    }\n\n    #[cfg(not(feature=\"compression\"))]\n    fn compress(&self, x: Vec<u8>) -> Vec<u8> {\n        if self == &CompressionMethod::None {\n            return x;\n        }\n\n        error!(\"Compression support is not selected during Websocat compilation\");\n        vec![]\n    }\n}\n\npub struct WsReadWrapper<T: WsStream + 'static> {\n    pub s: WsSource<T>,\n    pub pingreply: MultiProducerWsSink<T>,\n    pub debt: ReadDebt,\n    pub pong_timeout: Option<(::tokio_timer::Delay, ::std::time::Duration)>,\n    pub ping_aborter: Option<::futures::unsync::oneshot::Sender<()>>,\n\n    pub text_prefix: Option<String>,\n    pub binary_prefix: Option<String>,\n    pub binary_base64: bool,\n    pub text_base64: bool,\n    pub creation_time: ::std::time::Instant, // for measuring ping RTTs\n    pub print_rtts: bool,\n    pub inhibit_pongs: Option<usize>,\n    pub uncompress : CompressionMethod,\n}\n\nimpl<T: WsStream + 'static> AsyncRead for WsReadWrapper<T> {}\n\nimpl<T: WsStream + 'static> Read for WsReadWrapper<T> {\n    fn read(&mut self, buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {\n        if let Some(ret) = self.debt.check_debt(buf) {\n            return ret;\n        }\n        macro_rules! abort_and_broken_pipe {\n            () => {{\n                if let Some(abt) = self.ping_aborter.take() {\n                    let _ = abt.send(());\n                }\n                brokenpipe()\n            }};\n        }\n        fn process_prefixes_and_base64<'a>(qbuf :&'a mut Vec<u8>, q: &mut &'a [u8], prefix: &Option<String>, base64: bool) {\n            match (prefix, base64) {\n                (None, false) => (),\n                (Some(pr), false) => {\n                    debug!(\"prepending prefix\");\n                    qbuf.reserve_exact(pr.len() + q.len());\n                    qbuf.extend_from_slice(pr.as_bytes());\n                    qbuf.extend_from_slice(q);\n                    *q = &mut qbuf[..];\n                }\n                (None, true) => {\n                    debug!(\"encoding to base64\");\n                    qbuf.resize(q.len() * 3 / 2 + 3, 0);\n                    let r = base64::encode_config_slice(q, base64::STANDARD, &mut qbuf[..]);\n                    qbuf.resize(r, 0);\n                    qbuf.push(b'\\n');\n                    *q = &mut qbuf[..];\n                },\n                (Some(pr), true) => {\n                    debug!(\"prepending prefix and encoding to base64\");\n                    qbuf.extend_from_slice(pr.as_bytes());\n                    qbuf.resize(pr.len() + q.len() * 3 / 2 + 3, 0);\n                    let r = base64::encode_config_slice(q, base64::STANDARD, &mut qbuf[pr.len()..]);\n                    qbuf.resize(pr.len()+r, 0);\n                    qbuf.push(b'\\n');\n                    *q = &mut qbuf[..];\n                },\n            }\n        }\n        loop {\n            return match self.s.poll().map_err(io_other_error)? {\n                Ready(Some(OwnedMessage::Close(x))) => {\n                    info!(\"Received WebSocket close message\");\n                    debug!(\"The close message is {:?}\", x);\n                    abort_and_broken_pipe!()\n                }\n                Ready(None) => {\n                    info!(\"incoming None\");\n                    abort_and_broken_pipe!()\n                }\n                Ready(Some(OwnedMessage::Ping(_x))) if self.inhibit_pongs == Some(0) => {\n                    info!(\"Received and ignored WebSocket ping\");\n                    continue;\n                }\n                Ready(Some(OwnedMessage::Ping(x))) => {\n                    info!(\"Received WebSocket ping\");\n                    if let Some(ref mut ip) = self.inhibit_pongs {\n                        *ip = ip.wrapping_sub(1);\n                    }\n                    let om = OwnedMessage::Pong(x);\n                    let mut sink = self.pingreply.borrow_mut();\n                    let mut proceed = false;\n                    // If case of when we cannot sing pong write away (send window full), we try to cache one of pong replies.\n                    // But this scheme is not foolproof - there can be cases where pongs would still get lost\n                    match sink.sink.start_send(om).map_err(io_other_error)? {\n                        futures::AsyncSink::NotReady(om) => {\n                            if sink.pong_debt.is_some() {\n                                warn!(\n                                    \"dropped a ping request from websocket due to channel contention\"\n                                );\n                            }\n                            debug!(\"WebSocket write contenction: buffering pong instead of sending immediately\");\n                            sink.pong_debt = Some(om);\n                        }\n                        futures::AsyncSink::Ready => {\n                            proceed = true;\n                        }\n                    }\n                    if proceed {\n                        let _ = sink.sink.poll_complete().map_err(io_other_error)?;\n                    }\n\n                    continue;\n                }\n                Ready(Some(OwnedMessage::Pong(buf))) => {\n                    if buf.len() == 12 {\n                        let (mut origts1, mut origts2) = ([0u8; 8], [0u8; 4]);\n                        origts1.copy_from_slice(&buf[0..8]);\n                        origts2.copy_from_slice(&buf[8..12]);\n                        let (origts1, origts2) = (u64::from_be_bytes(origts1), u32::from_be_bytes(origts2));\n                        let origts = ::std::time::Duration::new(origts1, origts2);\n                        let newts = ::std::time::Instant::now() - self.creation_time;\n                        let delta = newts.checked_sub(origts).unwrap_or_default();\n                        info!(\"Received a pong from websocket; RTT = {:?}\", delta);\n                        if self.print_rtts {\n                            eprintln!(\"RTT {}.{:06} s\", delta.as_secs(), delta.subsec_micros());\n                        }\n                    } else {\n                        warn!(\"Received a pong with a strange content from websocket\");\n                    }\n\n                    if let Some((de, intvl)) = self.pong_timeout.as_mut() {\n                        de.reset(::std::time::Instant::now() + *intvl);\n                    }\n                    continue;\n                }\n                Ready(Some(OwnedMessage::Text(x))) => {\n                    debug!(\"incoming text\");\n                    let mut qbuf : Vec<u8> = vec![];\n                    let mut q : &[u8] = x.as_bytes();\n                    process_prefixes_and_base64(&mut qbuf, &mut q, &self.text_prefix, self.text_base64);\n                    match self.debt.process_message(buf, q) {\n                        ProcessMessageResult::Return(x) => x,\n                        ProcessMessageResult::Recurse => continue,\n                    }\n                }\n                Ready(Some(OwnedMessage::Binary(mut x))) => {\n                    x = self.uncompress.uncompress(x);\n                    debug!(\"incoming binary\");\n                    let mut qbuf : Vec<u8> = vec![];\n                    let mut q : &[u8] = x.as_slice();\n                    process_prefixes_and_base64(&mut qbuf, &mut q, &self.binary_prefix, self.binary_base64);\n                    match self.debt.process_message(buf, q) {\n                        ProcessMessageResult::Return(x) => x,\n                        ProcessMessageResult::Recurse => continue,\n                    }\n                }\n                NotReady => {\n                    use futures::Async;\n                    use futures::Future;\n                    if let Some((de, _intvl)) = self.pong_timeout.as_mut() {\n                        match de.poll() {\n                            Err(e) => error!(\"tokio-timer's Delay: {}\", e),\n                            Ok(Async::NotReady) => (),\n                            Ok(Async::Ready(_inst)) => {\n                                warn!(\"Closing WebSocket connection due to ping timeout\");\n                                return abort_and_broken_pipe!();\n                            }\n                        }\n                    }\n                    wouldblock()\n                }\n            };\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone)]\npub enum Mode1 {\n    Text,\n    Binary,\n}\n\npub struct WsWriteWrapper<T: WsStream + 'static> {\n    pub sink: MultiProducerWsSink<T>,\n    pub mode: Mode1,\n    pub close_on_shutdown: bool,\n\n    pub text_prefix: Option<String>,\n    pub binary_prefix: Option<String>,\n    pub binary_base64: bool,\n    pub text_base64: bool,\n    pub close_status_code: Option<u16>,\n    pub close_reason: Option<String>,\n    pub compress : CompressionMethod,\n}\n\nimpl<T: WsStream + 'static> AsyncWrite for WsWriteWrapper<T> {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        if !self.close_on_shutdown {\n            return Ok(Ready(()));\n        }\n        let close_data = self.close_status_code.map(|code|\n            websocket::CloseData{\n                status_code: code,\n                reason: self.close_reason.clone().unwrap_or_default()\n            }\n        );\n        let mut sink = self.sink.borrow_mut();\n        match sink.sink\n            .start_send(OwnedMessage::Close(close_data))\n            .map_err(io_other_error)?\n        {\n            futures::AsyncSink::NotReady(_) => wouldblock(),\n            futures::AsyncSink::Ready => {\n                // Too lazy to implement a state machine here just for\n                // properly handling this.\n                // And shutdown result is ignored here anyway.\n                let _ = sink.sink.poll_complete().map_err(|_| ()).map(|_| ());\n                Ok(Ready(()))\n            }\n        }\n    }\n}\n\nimpl<T: WsStream + 'static> Write for WsWriteWrapper<T> {\n    fn write(&mut self, buf_: &[u8]) -> IoResult<usize> {\n        if self.sink.borrow().pong_debt.is_some() {\n            let mut sink = self.sink.borrow_mut();\n            let debt = sink.pong_debt.take().unwrap();\n            match sink.sink.start_send(debt).map_err(io_other_error)? {\n                futures::AsyncSink::NotReady(debt) => {\n                    sink.pong_debt = Some(debt);\n                    return wouldblock();\n                },\n                futures::AsyncSink::Ready => {\n                    debug!(\"Finished sending cached Pong reply message\");\n                }\n            }\n        }\n        if self.sink.borrow().ping_debt.is_some() {\n            let mut sink = self.sink.borrow_mut();\n            let debt = sink.ping_debt.take().unwrap();\n            match sink.sink.start_send(debt).map_err(io_other_error)? {\n                futures::AsyncSink::NotReady(debt) => {\n                    sink.ping_debt = Some(debt);\n                    return wouldblock();\n                },\n                futures::AsyncSink::Ready => {\n                    debug!(\"Finished sending cached Ping message\");\n                }\n            }\n        }\n\n\n        let bufv;\n        let mut effective_mode = self.mode;\n\n        let mut buf : &[u8] = buf_;\n\n        let origlen = buf.len();\n\n        if let Some(pr) = &self.text_prefix {\n            if buf.starts_with(pr.as_bytes()) {\n                effective_mode = Mode1::Text;\n                buf = &buf[pr.len()..];\n            }\n        }\n        if let Some(pr) = &self.binary_prefix {\n            if buf.starts_with(pr.as_bytes()) {\n                effective_mode = Mode1::Binary;\n                buf = &buf[pr.len()..];\n            }\n        }\n\n        let decode_base64 = match effective_mode {\n            Mode1::Binary => self.binary_base64,\n            Mode1::Text => self.text_base64,\n        };\n\n        if decode_base64 {\n            if buf.last() == Some(&b'\\n') {\n                buf = &buf[..(buf.len()-1)];\n            }\n            if buf.last() == Some(&b'\\r') {\n                buf = &buf[..(buf.len()-1)];\n            }\n            if let Ok(v) = base64::decode(buf) {\n                bufv = v;\n                buf = &bufv[..];\n            } else {\n                error!(\"Failed to decode user-supplised base64 buffer. Sending message as is.\");\n            }\n        }\n\n        let om = match effective_mode {\n            Mode1::Binary => {\n                let x = buf.to_vec();\n                let x = self.compress.compress(x);\n                OwnedMessage::Binary(x)\n            },\n            Mode1::Text => {\n                let text_tmp;\n                let text = match ::std::str::from_utf8(buf) {\n                    Ok(x) => x,\n                    Err(_) => {\n                        error!(\n                            \"Invalid UTF-8 in a text WebSocket message. Sending lossy data. May be \\\n                             caused by unlucky buffer splits.\"\n                        );\n                        text_tmp = String::from_utf8_lossy(buf);\n                        text_tmp.as_ref()\n                    }\n                };\n                OwnedMessage::Text(text.to_string())\n            }\n        };\n        match self.sink.borrow_mut().sink.start_send(om).map_err(io_other_error)? {\n            futures::AsyncSink::NotReady(_) => wouldblock(),\n            futures::AsyncSink::Ready => Ok(origlen),\n        }\n    }\n    fn flush(&mut self) -> IoResult<()> {\n        match self\n            .sink\n            .borrow_mut()\n            .sink\n            .poll_complete()\n            .map_err(io_other_error)?\n        {\n            NotReady => wouldblock(),\n            Ready(()) => Ok(()),\n        }\n    }\n}\n\nimpl<T: WsStream + 'static> Drop for WsWriteWrapper<T> {\n    fn drop(&mut self) {\n        debug!(\"drop WsWriteWrapper\",);\n        // moved to shutdown()\n    }\n}\n\npub struct PeerForWs(pub Peer);\n\n//implicit impl websocket::stream::async::Stream for PeerForWs {}\n\nimpl AsyncRead for PeerForWs {}\nimpl Read for PeerForWs {\n    fn read(&mut self, buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {\n        (self.0).0.read(buf)\n    }\n}\nimpl AsyncWrite for PeerForWs {\n    fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> {\n        (self.0).1.shutdown()\n    }\n}\nimpl Write for PeerForWs {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        (self.0).1.write(buf)\n    }\n    fn flush(&mut self) -> IoResult<()> {\n        (self.0).1.flush()\n    }\n}\n\nenum WsPingerState {\n    WaitingForTimer,\n    StartSend,\n    PollComplete,\n}\n\n/// Periodically sends WebSocket pings\npub struct WsPinger<T: WsStream + 'static> {\n    st: WsPingerState,\n    si: MultiProducerWsSink<T>,\n    t: ::tokio_timer::Interval,\n    origin: ::std::time::Instant,\n    aborter: ::futures::unsync::oneshot::Receiver<()>,\n    max_sent_pings: Option<usize>,\n}\n\nimpl<T: WsStream + 'static> WsPinger<T> {\n    pub fn new(\n        sink: MultiProducerWsSink<T>,\n        interval: ::std::time::Duration,\n        origin: ::std::time::Instant,\n        aborter: ::futures::unsync::oneshot::Receiver<()>,\n        max_sent_pings: Option<usize>,\n    ) -> Self {\n        WsPinger {\n            st: WsPingerState::WaitingForTimer,\n            t: ::tokio_timer::Interval::new_interval(interval),\n            si: sink,\n            origin,\n            aborter,\n            max_sent_pings,\n        }\n    }\n}\n\nimpl<T: WsStream + 'static> ::futures::Future for WsPinger<T> {\n    type Item = ();\n    type Error = ();\n\n    fn poll(&mut self) -> ::futures::Poll<(), ()> {\n        use self::WsPingerState::*;\n        use futures::Async;\n        use futures::AsyncSink;\n        loop {\n            match self.aborter.poll() {\n                Err(e) => warn!(\"unsync/oneshot: {}\", e),\n                Ok(Async::NotReady) => (),\n                Ok(Async::Ready(())) => {\n                    debug!(\"Pinger aborted\");\n                    return Ok(Async::Ready(()));\n                }\n            }\n            match self.st {\n                WaitingForTimer => match self.t.poll() {\n                    Err(e) => warn!(\"wspinger: {}\", e),\n                    Ok(Async::Ready(None)) => warn!(\"tokio-timer's interval stream ended?\"),\n                    Ok(Async::NotReady) => return Ok(Async::NotReady),\n                    Ok(Async::Ready(Some(_instant))) => {\n                        if let Some(ref mut maxnum) = self.max_sent_pings {\n                            if *maxnum > 0 {\n                                *maxnum -= 1;\n                            } else {\n                                info!(\"Not sending WebSocket pings anymore\");\n                                self.st = WaitingForTimer;\n                                continue;\n                            }\n                        }\n                        self.st = StartSend;\n                        info!(\"Sending WebSocket ping\");\n                        continue;\n                    }\n                },\n                StartSend => {\n                    let ts = ::std::time::Instant::now().duration_since(self.origin);\n                    let (ts1, ts2) = (ts.as_secs(), ts.subsec_nanos());\n                    let mut ts = [0; 12];\n                    ts[0..8].copy_from_slice(&ts1.to_be_bytes());\n                    ts[8..12].copy_from_slice(&ts2.to_be_bytes());\n                    let om = OwnedMessage::Ping(ts.to_vec());\n                    let mut sink = self.si.borrow_mut();\n                    match sink.sink.start_send(om) {\n                        Err(e) => info!(\"wsping: {}\", e),\n                        Ok(AsyncSink::NotReady(om)) => {\n                            if sink.ping_debt.is_some() {\n                                warn!(\n                                    \"dropped a ping request to websocket due to channel contention\"\n                                );\n                            } else {\n                                debug!(\"WebSocket write contenction: buffering ping instead of sending immediately\");\n                                sink.ping_debt = Some(om);\n                            }\n\n                            self.st = WaitingForTimer;\n                            continue;\n                        }\n                        Ok(AsyncSink::Ready) => {\n                            self.st = PollComplete;\n                            continue;\n                        }\n                    }\n                }\n                PollComplete => match self.si.borrow_mut().sink.poll_complete() {\n                    Err(e) => info!(\"wsping: {}\", e),\n                    Ok(Async::NotReady) => {\n                        self.st = WaitingForTimer;\n                        continue;\n                        //return Ok(Async::NotReady);\n                    }\n                    Ok(Async::Ready(())) => {\n                        self.st = WaitingForTimer;\n                        continue;\n                    }\n                },\n            }\n            return Ok(Async::Ready(()));\n        }\n    }\n}\n\n\npub type Duplex<S> = ::tokio_codec::Framed<S, websocket::r#async::MessageCodec<websocket::OwnedMessage>>;\n\npub fn finish_building_ws_peer<S>(opts: &super::Options, duplex: Duplex<S>, close_on_shutdown: bool, hup: Option<HupToken>) -> Peer\n    where S : tokio_io::AsyncRead + tokio_io::AsyncWrite + 'static + Send\n{\n    let (sink, stream) = duplex.split();\n    let wrappedsink = WsSinkWithOneBufferedMessage {\n        sink,\n        pong_debt: None,\n        ping_debt: None,\n    };\n    let mpsink = Rc::new(RefCell::new(wrappedsink));\n\n    let mode1 = if opts.websocket_text_mode {\n        Mode1::Text\n    } else {\n        Mode1::Binary\n    };\n\n    let now = ::std::time::Instant::now();\n    let ping_aborter = if let Some(d) = opts.ws_ping_interval {\n        debug!(\"Starting pinger\");\n\n        let (tx, rx) = ::futures::unsync::oneshot::channel();\n\n        let intv = ::std::time::Duration::from_secs(d);\n        let pinger = super::ws_peer::WsPinger::new(mpsink.clone(), intv,now, rx, opts.max_sent_pings);\n        ::tokio_current_thread::spawn(pinger);\n        Some(tx)\n    } else {\n        None\n    };\n\n    let pong_timeout = if let Some(d) = opts.ws_ping_timeout {\n        let to = ::std::time::Duration::from_secs(d);\n        let de = ::tokio_timer::Delay::new(std::time::Instant::now() + to);\n        Some((de, to))\n    } else {\n        None\n    };\n\n    let zmsgh = if opts.no_exit_on_zeromsg {\n        super::readdebt::ZeroMessagesHandling::Drop\n    } else {\n        super::readdebt::ZeroMessagesHandling::Deliver\n    };\n\n    let compress = match (opts.compress_deflate, opts.compress_gzip, opts.compress_zlib) {\n        (false, false, false) => CompressionMethod::None,\n        (true, false, false) => CompressionMethod::Deflate,\n        (false, true, false) => CompressionMethod::Gzip,\n        (false, false, true) => CompressionMethod::Zlib,\n        _ => {\n            error!(\"Multiple compression methods specified\");\n            CompressionMethod::None\n        }\n    };\n    let uncompress = match (opts.uncompress_deflate, opts.uncompress_gzip, opts.uncompress_zlib) {\n        (false, false, false) => CompressionMethod::None,\n        (true, false, false) => CompressionMethod::Deflate,\n        (false, true, false) => CompressionMethod::Gzip,\n        (false, false, true) => CompressionMethod::Zlib,\n        _ => {\n            error!(\"Multiple uncompression methos specified\");\n            CompressionMethod::None\n        }\n    };\n    \n    \n    let ws_str = WsReadWrapper {\n        s: stream,\n        pingreply: mpsink.clone(),\n        debt: super::readdebt::ReadDebt(Default::default(), opts.read_debt_handling, zmsgh),\n        pong_timeout,\n        ping_aborter,\n        text_prefix: opts.ws_text_prefix.clone(),\n        binary_prefix: opts.ws_binary_prefix.clone(),\n        binary_base64: opts.ws_binary_base64,\n        text_base64: opts.ws_text_base64,\n        creation_time: now,\n        print_rtts: opts.print_ping_rtts,\n        inhibit_pongs: opts.inhibit_pongs,\n        uncompress,\n    };\n    let ws_sin = WsWriteWrapper{\n        sink: mpsink,\n        mode: mode1,\n        close_on_shutdown,\n\n        text_prefix: opts.ws_text_prefix.clone(),\n        binary_prefix: opts.ws_binary_prefix.clone(),\n        binary_base64: opts.ws_binary_base64,\n        text_base64: opts.ws_text_base64,\n        close_status_code: opts.close_status_code,\n        close_reason: opts.close_reason.clone(),\n        compress,\n    };\n\n    Peer::new(ws_str, ws_sin, hup)\n}\n"
  },
  {
    "path": "src/ws_server_peer.rs",
    "content": "extern crate hyper;\nextern crate websocket;\n\nuse self::hyper::uri::RequestUri::AbsolutePath;\n\nuse self::websocket::WebSocketError;\nuse futures::future::{err, Future};\n\nuse std::rc::Rc;\n\nuse crate::options::StaticFile;\n\nuse self::websocket::server::upgrade::r#async::IntoWs;\n\nuse super::ws_peer::{PeerForWs};\nuse super::{box_up_err, io_other_error, BoxedNewPeerFuture, Peer};\nuse super::{ConstructParams, L2rUser, PeerConstructor, Specifier};\n\n#[derive(Debug)]\npub struct WsServer<T: Specifier>(pub T);\nimpl<T: Specifier> Specifier for WsServer<T> {\n    fn construct(&self, cp: ConstructParams) -> PeerConstructor {\n        let restrict_uri = Rc::new(cp.program_options.restrict_uri.clone());\n        let serve_static_files = Rc::new(cp.program_options.serve_static_files.clone());\n        let inner = self.0.construct(cp.clone());\n        //let l2r = cp.left_to_right;\n        inner.map(move |p, l2r| {\n            // FIXME: attack of `Vec::clone`s.\n            ws_upgrade_peer(\n                p,\n                restrict_uri.clone(),\n                serve_static_files.clone(),\n                cp.program_options.websocket_reply_protocol.clone(),\n                cp.program_options.custom_reply_headers.clone(),\n                cp.program_options.clone(),\n                l2r,\n            )\n        })\n    }\n    specifier_boilerplate!(noglobalstate has_subspec);\n    self_0_is_subspecifier!(proxy_is_multiconnect);\n}\nspecifier_class!(\n    name = WsServerClass,\n    target = WsServer,\n    prefixes = [\"ws-upgrade:\", \"upgrade-ws:\", \"ws-u:\", \"u-ws:\"],\n    arg_handling = subspec,\n    overlay = true,\n    MessageOriented,\n    MulticonnectnessDependsOnInnerType,\n    help = r#\"\nWebSocket upgrader / raw server. Specify your own protocol instead of usual TCP. [A]\n\nAll other WebSocket server modes actually use this overlay under the hood.\n\nExample: serve incoming connection from socat\n\n    socat tcp-l:1234,fork,reuseaddr exec:'websocat -t ws-u\\:stdio\\: mirror\\:'\n\"#\n);\n\nspecifier_alias!(\n    name = WsTcpServerClass,\n    prefixes = [\"ws-listen:\", \"ws-l:\", \"l-ws:\", \"listen-ws:\"],\n    alias = \"ws-u:tcp-l:\",\n    help = r#\"\nWebSocket server. Argument is host and port to listen.\n\nExample: Dump all incoming websocket data to console\n\n    websocat ws-l:127.0.0.1:8808 -\n\nExample: the same, but more verbose:\n\n    websocat ws-l:tcp-l:127.0.0.1:8808 reuse:-\n\"#\n);\n\nspecifier_alias!(\n    name = WsInetdServerClass,\n    prefixes = [\"inetd-ws:\", \"ws-inetd:\"],\n    alias = \"ws-u:inetd:\",\n    help = r#\"\nWebSocket inetd server. [A]\n\nTODO: transfer the example here\n\"#\n);\n\nspecifier_alias!(\n    name = WsUnixServerClass,\n    prefixes = [\"l-ws-unix:\"],\n    alias = \"ws-u:unix-l:\",\n    help = r#\"\nWebSocket UNIX socket-based server. [A]\n\"#\n);\n\nspecifier_alias!(\n    name = WsAbstractUnixServerClass,\n    prefixes = [\"l-ws-abstract:\"],\n    alias = \"ws-l:abstract-l:\",\n    help = r#\"\nWebSocket abstract-namespaced UNIX socket server. [A]\n\"#\n);\n\n#[path = \"http_serve.rs\"]\npub mod http_serve;\n\npub fn ws_upgrade_peer(\n    inner_peer: Peer,\n    restrict_uri: Rc<Option<String>>,\n    serve_static_files: Rc<Vec<StaticFile>>,\n    websocket_protocol: Option<String>,\n    custom_reply_headers: Vec<(String, Vec<u8>)>,\n    opts: Rc<super::Options>,\n    l2r: L2rUser,\n) -> BoxedNewPeerFuture {\n    let step1 = PeerForWs(inner_peer);\n    let step2: Box<\n        dyn Future<Item = self::websocket::server::upgrade::r#async::Upgrade<_>, Error = _>,\n    > = step1.into_ws();\n    let step3 = step2\n        .or_else(|(innerpeer, hyper_incoming, _bytesmut, e)| {\n            http_serve::http_serve(innerpeer.0, hyper_incoming, serve_static_files)\n            .then(|_|\n                err(WebSocketError::IoError(io_other_error(e)))\n            )\n        })\n        .and_then(\n            move |mut x| -> Box<dyn Future<Item = Peer, Error = websocket::WebSocketError>> {\n                info!(\"Incoming connection to websocket: {}\", x.request.subject.1);\n\n                use ::websocket::header::WebSocketProtocol;\n\n                let mut protocol_check = true;\n                {\n                    let pp : Option<&WebSocketProtocol> = x.request.headers.get();\n                    if let Some(rp) = websocket_protocol {\n                        // Unconditionally set this protocol\n                        x.headers.set_raw(\"Sec-WebSocket-Protocol\",\n                            vec![rp.as_bytes().to_vec()],\n                        );\n                        // Warn if not present in client protocols\n                        let mut present = false;\n                        if let Some(pp) = pp {\n                            if let Some(pp) = pp.iter().next() {\n                                if pp == &rp {\n                                    present = true;\n                                }\n                            }\n                        }\n                        if !present {\n                            if pp.is_none() {\n                                warn!(\"Client failed to specify Sec-WebSocket-Protocol header. Replying with it anyway, against the RFC.\");\n                            } else {\n                                protocol_check = false;\n                            }\n                        }\n                    } else {\n                        // No protocol specified, just choosing the first if any.\n                        if let Some(pp) = pp {\n                            if pp.len() > 1 {\n                                warn!(\"Multiple `Sec-WebSocket-Protocol`s specified in the request. Choosing the first one. Use --server-protocol to make it explicit.\")\n                            }\n                            if let Some(pp) = pp.iter().next() {\n                                x.headers.set_raw(\n                                    \"Sec-WebSocket-Protocol\",\n                                    vec![pp.as_bytes().to_vec()],\n                                );\n                            }\n                        }\n                    }\n                }\n\n                for (hn, hv) in custom_reply_headers {\n                    x.headers.append_raw(hn, hv);\n                }\n\n                debug!(\"{:?}\", x.request);\n                debug!(\"{:?}\", x.headers);\n\n                if !protocol_check {\n                    return Box::new(\n                            x.reject()\n                                .and_then(|_| {\n                                    warn!(\"Requested Sec-WebSocket-Protocol does not match --server-protocol option\");\n                                    ::futures::future::err(crate::util::simple_err(\n                                        \"Requested Sec-WebSocket-Protocol does not match --server-protocol option\"\n                                            .to_string(),\n                                    ))\n                                })\n                                .map_err(|e| websocket::WebSocketError::IoError(io_other_error(e))),\n                        )\n                            as Box<dyn Future<Item = Peer, Error = websocket::WebSocketError>>;\n                }\n                \n                \n                match l2r {\n                    L2rUser::FillIn(ref y) => {\n                        let uri = &x.request.subject.1;\n                        let mut z = y.borrow_mut();\n                        z.uri = Some(format!(\"{}\", uri));\n\n                        let h : &websocket::header::Headers = &x.request.headers;\n                        for q in opts.headers_to_env.iter() {\n                            if let Some(v) = h.get_raw(q) {\n                                if v.is_empty() { continue }\n                                if v.len() > 1 {\n                                    warn!(\"Extra request header for {} ignored\", q);\n                                }\n                                if let Ok(val) = String::from_utf8(v[0].clone()) {\n                                    z.headers.push((\n                                        q.clone(),\n                                        val,\n                                    ));\n                                } else {\n                                    warn!(\"Header {} value contains invalid UTF-8\", q);\n                                }\n                            } else {\n                                warn!(\"No request header {}, so no envvar H_{}\", q, q);\n                            }\n                        }\n                    },\n                    L2rUser::ReadFrom(_) => {},\n                }\n                \n                \n                if let Some(ref restrict_uri) = *restrict_uri {\n                    let check_passed = matches!(x.request.subject.1, AbsolutePath(ref x) if x == restrict_uri);\n                    if !check_passed {\n                        return Box::new(\n                            x.reject()\n                                .and_then(|_| {\n                                    warn!(\"Incoming request URI doesn't match the --restrict-uri value\");\n                                    ::futures::future::err(crate::util::simple_err(\n                                        \"Request URI doesn't match --restrict-uri parameter\"\n                                            .to_string(),\n                                    ))\n                                })\n                                .map_err(|e| websocket::WebSocketError::IoError(io_other_error(e))),\n                        )\n                            as Box<dyn Future<Item = Peer, Error = websocket::WebSocketError>>;\n                    }\n                };\n                Box::new(x.accept_with_limits(opts.max_ws_frame_length, opts.max_ws_message_length).map(move |(y, headers)| {\n                    debug!(\"{:?}\", headers);\n                    info!(\"Upgraded\");\n                    let close_on_shutdown =  !opts.websocket_dont_close;\n                    super::ws_peer::finish_building_ws_peer(&opts, y, close_on_shutdown, None)\n                })) as Box<dyn Future<Item = Peer, Error = websocket::WebSocketError>>\n            },\n        );\n    let step4 = step3.map_err(box_up_err);\n    Box::new(step4) as BoxedNewPeerFuture\n}\n"
  },
  {
    "path": "test.sh",
    "content": "#!/bin/bash\n\nif [ \"$TRAVIS_OS_NAME\" = \"osx\" ]; then\n    echo \"Not supported on Mac\";\n    exit 0\nfi\n\nset -ex\n\nPATH=target/debug:$PATH\n\nwebsocat -q -t ws-l:127.0.0.1:19923 mirror:&\nMIR=$!\n\ntrap 'kill $MIR' EXIT\ntrap 'echo test failed' ERR\n\n\nfunction ensurenonblock() {\n    perl -we 'use Fcntl qw(F_GETFL O_NONBLOCK); open F, \"<&=\", 0; my $flags = fcntl(F, F_GETFL, 0); if ($flags & O_NONBLOCK) { exit 1; } else { exit 0; }'\n}\n\nsleep 1\n\nensurenonblock\n\nC1=$(find /proc/$MIR/fd -type l -printf '\\n' | wc -l)\n\n{\n    echo 123\n    sleep 1\n    echo ABC\n} | websocat ws://127.0.0.1:19923 | {\n    TS1=$(date +%s.%N)\n    read A\n    TS2=$(date +%s.%N)\n    read B\n    TS3=$(date +%s.%N)\n\n    echo TS1=$TS1 A=$A TS2=$TS2 B=$B TS3=$TS3 > lol\n}\n\nperl -ne '\n    use POSIX;\n    if(m!TS1=(\\S+) A=123 TS2=(\\S+) B=ABC TS3=(\\S+)!) {\n        if ($2-$1 > 0.1) {\n            print STDERR \"Err 1\\n\";\n            exit 1;\n        }\n        if ($3-$2 < 0.7 || $3-$2 > 1.4) {\n            print STDERR \"Err 2\\n\";\n            exit 1;\n        }\n        print STDERR \"Timing OK\\n\";\n        POSIX::_exit 0;\n    }\n    END {\n        exit 1;\n    }\n' lol\n\nrm -f lol\n\nensurenonblock\n\nC2=$(find /proc/$MIR/fd -type l -printf '\\n' | wc -l)\ntest \"$C1\" -eq \"$C2\"\n\nwebsocat -b literal:qwe ws://127.0.0.1:19923\nwebsocat -b literal:qwe -u ws://127.0.0.1:19923\nwebsocat -b literal:qwe -1uU ws://127.0.0.1:19923\n\nC2=$(find /proc/$MIR/fd -type l -printf '\\n' | wc -l)\ntest \"$C1\" -eq \"$C2\"\n\n\necho Test OK\n"
  },
  {
    "path": "test_help.sh",
    "content": "#!/bin/bash\n\n# This test tracks how much of Websocat functionality is implemented by inspecting the help message\n\ntrue ${WEBSOCAT:=target/debug/websocat}\n\nT=$($WEBSOCAT --help=full)\n\nCTR=0\nSUCC=0\n\ntt() {\n   PAT=\"$1\"\n   true $((CTR+=1))\n   if echo \"$T\" | cut -c 1-50 | tr ' \\t' '\\n\\n' | grep -q -- \"^$PAT\\$\"; then\n      true $((SUCC+=1))\n      printf '%40s  [ OK ]\\n' \"$PAT\"\n   else\n      printf '%40s  [FAIL]\\n' \"$PAT\"\n   fi\n}\n\ntt --async-stdio\ntt --dump-spec\ntt --set-environment\ntt --exit-on-eof\ntt --foreachmsg-wait-read\ntt --jsonrpc\ntt --just-generate-key\ntt --linemode-strip-newlines\ntt --null-terminated\ntt --no-line\ntt --no-exit-on-zeromsg\ntt --no-fixups\ntt --no-async-stdio\ntt --one-message\ntt --oneshot\ntt --exec-sighup-on-stdin-close\ntt --exec-sighup-on-zero-msg\ntt --reuser-send-zero-msg-on-disconnect\ntt --server-mode\ntt --strict\ntt --insecure\ntt --udp-broadcast\ntt --udp-multicast-loop\ntt --udp-oneshot\ntt --udp-reuseaddr\ntt --unidirectional\ntt --unidirectional-reverse\ntt --accept-from-fd\ntt --unlink\ntt --version\ntt -v\ntt --binary\ntt --no-close\ntt --websocket-ignore-zeromsg\ntt --text\ntt --base64\ntt --base64-text\ntt --socks5\ntt --autoreconnect-delay-millis\ntt --basic-auth\ntt --queue-len\ntt --buffer-size\ntt --header\ntt --server-header\ntt --exec-args\ntt --header-to-env\ntt --help\ntt --just-generate-accept\ntt --max-messages\ntt --max-messages-rev\ntt --conncap\ntt --origin\ntt --pkcs12-der\ntt --pkcs12-passwd\ntt --request-header\ntt --request-method\ntt --request-uri\ntt --restrict-uri\ntt --static-file\ntt --socks5-bind-script\ntt --socks5-destination\ntt --tls-domain\ntt --udp-multicast\ntt --udp-multicast-iface-v4\ntt --udp-multicast-iface-v6\ntt --udp-ttl\ntt --protocol\ntt --server-protocol\ntt --websocket-version\ntt --binary-prefix\ntt --ws-c-uri\ntt --ping-interval\ntt --ping-timeout\ntt --text-prefix\ntt ws-listen:\ntt inetd-ws:\ntt l-ws-unix:\ntt l-ws-abstract:\ntt ws-lowlevel-client:\ntt ws-lowlevel-server:\ntt wss-listen:\ntt http:\ntt asyncstdio:\ntt inetd:\ntt tcp:\ntt tcp-listen:\ntt ssl-listen:\ntt sh-c:\ntt cmd:\ntt exec:\ntt readfile:\ntt writefile:\ntt appendfile:\ntt udp:\ntt udp-listen:\ntt open-async:\ntt open-fd:\ntt threadedstdio:\ntt unix:\ntt unix-listen:\ntt unix-dgram:\ntt abstract:\ntt abstract-listen:\ntt abstract-dgram:\ntt mirror:\ntt literalreply:\ntt clogged:\ntt literal:\ntt assert:\ntt assert2:\ntt seqpacket:\ntt seqpacket-listen:\ntt ws-upgrade:\ntt http-request:\ntt http-post-sse:\ntt ssl-connect:\ntt ssl-accept:\ntt reuse-raw:\ntt broadcast:\ntt autoreconnect:\ntt ws-c:\ntt msg2line:\ntt line2msg:\ntt foreachmsg:\ntt log:\ntt jsonrpc:\ntt socks5-connect:\ntt socks5-bind:\n\necho \"$SUCC of $CTR\"\n\n"
  },
  {
    "path": "tests/test.rs",
    "content": "extern crate websocat;\n\nextern crate env_logger;\nextern crate futures;\nextern crate tokio;\nextern crate tokio_timer;\n\nuse futures::future::Future;\n\nuse websocat::{spec, Options, WebsocatConfiguration3};\n\nfn dflt() -> Options {\n    Default::default()\n}\n\nmacro_rules! wt {\n    ($core:ident, $s1:expr, $s2:expr,delay = $ms:expr, $($rest:tt)*) => {{\n        let s1 = spec($s1).unwrap();\n        let s2 = spec($s2).unwrap();\n\n        let delay = tokio_timer::Delay::new(std::time::Instant::now() + std::time::Duration::new(0, $ms * 1_000_000)).map_err(|_|());\n\n        delay.and_then(|()| wt!(stage2, h2, s1, s2, $($rest)*) )\n    }};\n    ($core:ident, $s1:expr, $s2:expr,nodelay,$($rest:tt)*) => {{\n        let s1 = spec($s1).unwrap();\n        let s2 = spec($s2).unwrap();\n\n        wt!(stage2, h2, s1, s2, $($rest)*)\n    }};\n    (stage2, $h2:ident, $s1:ident, $s2:ident, noopts,$($rest:tt)*) => {\n        wt!(stage2, $h2, $s1, $s2, opts=Default::default(),$($rest)*)\n    };\n    (stage2, $h2:ident, $s1:ident, $s2:ident, opts=$opts:expr,$($rest:tt)*) => {{\n\n        let websocat = WebsocatConfiguration3 {\n            opts: $opts,\n            $s1,\n            $s2,\n        };\n\n        websocat.serve(\n            wt!(stage3, $($rest)*),\n        )\n    }};\n    (stage3, errpanic,) => {\n        std::rc::Rc::new(|e| {\n            eprintln!(\"{}\", e);\n            panic!();\n        })\n    };\n    (stage3, errignore,) => {\n        std::rc::Rc::new(|_| {\n\n        })\n    };\n}\n\nmacro_rules! prepare {\n    ($core:ident) => {\n        let _ = env_logger::try_init();\n        let mut $core = tokio::runtime::current_thread::Runtime::new().unwrap();\n    };\n}\nmacro_rules! run {\n    ($core:ident, $prog:expr) => {\n        $core.block_on($prog).map_err(|()| panic!()).unwrap();\n    };\n}\n\n#[test]\nfn trivial() {\n    prepare!(core);\n    let prog = wt!(\n        core,\n        \"literal:qwerty\",\n        \"assert:qwerty\",\n        nodelay,\n        noopts,\n        errpanic,\n    );\n    run!(core, prog);\n}\n\n#[test]\nfn tcp() {\n    prepare!(core);\n    let prog1 = wt!(\n        core,\n        \"literal:qwert2y\",\n        \"tcp-l:127.0.0.1:45912\",\n        nodelay,\n        noopts,\n        errpanic,\n    );\n    let prog2 = wt!(\n        core,\n        \"tcp:127.0.0.1:45912\",\n        \"assert:qwert2y\",\n        delay = 200,\n        noopts,\n        errpanic,\n    );\n\n    let prog = prog1.join(prog2);\n    run!(core, prog);\n}\n\n#[test]\nfn ws() {\n    prepare!(core);\n    let prog1 = wt!(\n        core,\n        \"literal:qwert3y\",\n        \"ws-l:127.0.0.1:45913\",\n        nodelay,\n        noopts,\n        errpanic,\n    );\n    let prog2 = wt!(\n        core,\n        \"ws://127.0.0.1:45913/ololo\",\n        \"assert:qwert3y\",\n        delay = 200,\n        noopts,\n        errpanic,\n    );\n\n    let prog = prog1.join(prog2);\n    run!(core, prog);\n}\n\n#[test]\nfn ws_ll() {\n    prepare!(core);\n    let prog1 = wt!(\n        core,\n        \"literal:qwert3y\",\n        \"ws-ll-s:tcp-l:127.0.0.1:45915\",\n        nodelay,\n        noopts,\n        errpanic,\n    );\n    let prog2 = wt!(\n        core,\n        \"ws-ll-c:tcp:127.0.0.1:45915\",\n        \"assert:qwert3y\",\n        delay = 200,\n        noopts,\n        errpanic,\n    );\n\n    let prog = prog1.join(prog2);\n    run!(core, prog);\n}\n\n\n#[test]\nfn ws_persist() {\n    prepare!(core);\n    let prog1 = wt!(\n        core,\n        \"ws-l:127.0.0.1:45914\",\n        \"literal:qwert4y\",\n        nodelay,\n        noopts,\n        errignore,\n    );\n    let prog2 = wt!(\n        core,\n        \"literal:invalid_connection_request\",\n        \"tcp:127.0.0.1:45914\",\n        delay = 200,\n        noopts,\n        errpanic,\n    );\n    let prog3 = wt!(\n        core,\n        \"ws://127.0.0.1:45914/ololo\",\n        \"assert:qwert4y\",\n        delay = 400,\n        noopts,\n        errpanic,\n    );\n\n    core.spawn(prog1);\n    core.spawn(prog2);\n    let prog = prog3;\n    run!(core, prog);\n}\n\n#[test]\n#[cfg(unix)]\nfn unix() {\n    prepare!(core);\n    let prog1 = wt!(\n        core,\n        \"literal:qwert3y\",\n        \"unix-l:zxc\",\n        nodelay,\n        opts = Options {\n            unlink_unix_socket: true,\n            ..dflt()\n        },\n        errpanic,\n    );\n    let prog2 = wt!(\n        core,\n        \"unix-c:zxc\",\n        \"assert:qwert3y\",\n        delay = 200,\n        noopts,\n        errpanic,\n    );\n\n    let prog = prog1.join(prog2);\n    run!(core, prog);\n    let _ = ::std::fs::remove_file(\"zxc\");\n}\n\n#[test]\n#[cfg(any(target_os = \"linux\", target_os = \"android\"))]\nfn abstract_() {\n    prepare!(core);\n    let prog1 = wt!(\n        core,\n        \"literal:qwert4y\",\n        \"abstract-l:zxc\",\n        nodelay,\n        noopts,\n        errpanic,\n    );\n    let prog2 = wt!(\n        core,\n        \"abstract-c:zxc\",\n        \"assert:qwert4y\",\n        delay = 200,\n        noopts,\n        errpanic,\n    );\n\n    let prog = prog1.join(prog2);\n    run!(core, prog);\n}\n"
  }
]