├── .aegir.cjs ├── .github ├── dependabot.yml └── workflows │ ├── automerge.yml │ ├── js-test-and-release.yml │ └── stale.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── package.json ├── src ├── constants.ts ├── index.ts ├── listener.browser.ts ├── listener.ts └── socket-to-conn.ts ├── test ├── browser.ts ├── compliance.ts ├── dial.ts ├── filter.spec.ts ├── instance.spec.ts ├── listen.ts └── node.ts └── tsconfig.json /.aegir.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const wrtc = require('wrtc') 4 | 5 | // TODO: Temporary fix per wrtc issue 6 | // https://github.com/node-webrtc/node-webrtc/issues/636#issuecomment-774171409 7 | process.on('beforeExit', (code) => process.exit(code)) 8 | 9 | const ECHO_PROTOCOL = '/echo/1.0.0' 10 | 11 | async function before () { 12 | const { webRTCDirect } = await import('./dist/src/index.js') 13 | const { pipe } = await import('it-pipe') 14 | const { multiaddr } = await import('@multiformats/multiaddr') 15 | const { mockUpgrader, mockRegistrar } = await import('@libp2p/interface-mocks') 16 | const { EventEmitter } = await import('@libp2p/interfaces/events') 17 | 18 | const REMOTE_MULTIADDR_IP4 = multiaddr('/ip4/127.0.0.1/tcp/12345/http/p2p-webrtc-direct') 19 | const REMOTE_MULTIADDR_IP6 = multiaddr('/ip6/::1/tcp/12346/http/p2p-webrtc-direct') 20 | 21 | const registrar = mockRegistrar() 22 | void registrar.handle(ECHO_PROTOCOL, ({ stream }) => { 23 | void pipe( 24 | stream, 25 | stream 26 | ).catch() 27 | }) 28 | const upgrader = mockUpgrader({ 29 | registrar, 30 | events: new EventEmitter() 31 | }) 32 | 33 | const wd = webRTCDirect({ 34 | wrtc 35 | })() 36 | 37 | const listeners = await Promise.all( 38 | [REMOTE_MULTIADDR_IP4, REMOTE_MULTIADDR_IP6].map(async ma => { 39 | const listener = wd.createListener({ 40 | upgrader 41 | }) 42 | await listener.listen(ma) 43 | 44 | return listener 45 | }) 46 | ) 47 | 48 | return { 49 | listeners 50 | } 51 | } 52 | 53 | async function after (testOptions, beforeReturn) { 54 | await Promise.all( 55 | beforeReturn.listeners.map(listener => listener.close()) 56 | ) 57 | } 58 | 59 | /** @type {import('aegir').PartialOptions} */ 60 | module.exports = { 61 | test: { 62 | before, 63 | after 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "10:00" 8 | open-pull-requests-limit: 10 9 | commit-message: 10 | prefix: "deps" 11 | prefix-development: "deps(dev)" 12 | -------------------------------------------------------------------------------- /.github/workflows/automerge.yml: -------------------------------------------------------------------------------- 1 | name: Automerge 2 | on: [ pull_request ] 3 | 4 | jobs: 5 | automerge: 6 | uses: protocol/.github/.github/workflows/automerge.yml@master 7 | with: 8 | job: 'automerge' 9 | -------------------------------------------------------------------------------- /.github/workflows/js-test-and-release.yml: -------------------------------------------------------------------------------- 1 | name: test & maybe release 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | 8 | jobs: 9 | 10 | check: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: lts/* 17 | - uses: ipfs/aegir/actions/cache-node-modules@master 18 | - run: npm run --if-present lint 19 | - run: npm run --if-present dep-check 20 | 21 | test-node: 22 | needs: check 23 | runs-on: ${{ matrix.os }} 24 | strategy: 25 | matrix: 26 | os: [windows-latest, ubuntu-latest, macos-latest] 27 | node: [lts/*] 28 | fail-fast: true 29 | steps: 30 | - uses: actions/checkout@v3 31 | - uses: actions/setup-node@v3 32 | with: 33 | node-version: ${{ matrix.node }} 34 | - uses: ipfs/aegir/actions/cache-node-modules@master 35 | - run: npm run --if-present test:node 36 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 37 | with: 38 | flags: node 39 | 40 | test-chrome: 41 | needs: check 42 | runs-on: ubuntu-latest 43 | steps: 44 | - uses: actions/checkout@v3 45 | - uses: actions/setup-node@v3 46 | with: 47 | node-version: lts/* 48 | - uses: ipfs/aegir/actions/cache-node-modules@master 49 | - run: npm run --if-present test:chrome 50 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 51 | with: 52 | flags: chrome 53 | 54 | test-chrome-webworker: 55 | needs: check 56 | runs-on: ubuntu-latest 57 | steps: 58 | - uses: actions/checkout@v3 59 | - uses: actions/setup-node@v3 60 | with: 61 | node-version: lts/* 62 | - uses: ipfs/aegir/actions/cache-node-modules@master 63 | - run: npm run --if-present test:chrome-webworker 64 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 65 | with: 66 | flags: chrome-webworker 67 | 68 | test-firefox: 69 | needs: check 70 | runs-on: ubuntu-latest 71 | steps: 72 | - uses: actions/checkout@v3 73 | - uses: actions/setup-node@v3 74 | with: 75 | node-version: lts/* 76 | - uses: ipfs/aegir/actions/cache-node-modules@master 77 | - run: npm run --if-present test:firefox 78 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 79 | with: 80 | flags: firefox 81 | 82 | test-firefox-webworker: 83 | needs: check 84 | runs-on: ubuntu-latest 85 | steps: 86 | - uses: actions/checkout@v3 87 | - uses: actions/setup-node@v3 88 | with: 89 | node-version: lts/* 90 | - uses: ipfs/aegir/actions/cache-node-modules@master 91 | - run: npm run --if-present test:firefox-webworker 92 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 93 | with: 94 | flags: firefox-webworker 95 | 96 | test-webkit: 97 | needs: check 98 | runs-on: ${{ matrix.os }} 99 | strategy: 100 | matrix: 101 | os: [ubuntu-latest, macos-latest] 102 | node: [lts/*] 103 | fail-fast: true 104 | steps: 105 | - uses: actions/checkout@v3 106 | - uses: actions/setup-node@v3 107 | with: 108 | node-version: lts/* 109 | - uses: ipfs/aegir/actions/cache-node-modules@master 110 | - run: npm run --if-present test:webkit 111 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 112 | with: 113 | flags: webkit 114 | 115 | test-webkit-webworker: 116 | needs: check 117 | runs-on: ${{ matrix.os }} 118 | strategy: 119 | matrix: 120 | os: [ubuntu-latest, macos-latest] 121 | node: [lts/*] 122 | fail-fast: true 123 | steps: 124 | - uses: actions/checkout@v3 125 | - uses: actions/setup-node@v3 126 | with: 127 | node-version: lts/* 128 | - uses: ipfs/aegir/actions/cache-node-modules@master 129 | - run: npm run --if-present test:webkit-webworker 130 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 131 | with: 132 | flags: webkit-webworker 133 | 134 | test-electron-main: 135 | needs: check 136 | runs-on: ubuntu-latest 137 | steps: 138 | - uses: actions/checkout@v3 139 | - uses: actions/setup-node@v3 140 | with: 141 | node-version: lts/* 142 | - uses: ipfs/aegir/actions/cache-node-modules@master 143 | - run: npx xvfb-maybe npm run --if-present test:electron-main 144 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 145 | with: 146 | flags: electron-main 147 | 148 | test-electron-renderer: 149 | needs: check 150 | runs-on: ubuntu-latest 151 | steps: 152 | - uses: actions/checkout@v3 153 | - uses: actions/setup-node@v3 154 | with: 155 | node-version: lts/* 156 | - uses: ipfs/aegir/actions/cache-node-modules@master 157 | - run: npx xvfb-maybe npm run --if-present test:electron-renderer 158 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 159 | with: 160 | flags: electron-renderer 161 | 162 | release: 163 | needs: [test-node, test-chrome, test-chrome-webworker, test-firefox, test-firefox-webworker, test-webkit, test-webkit-webworker, test-electron-main, test-electron-renderer] 164 | runs-on: ubuntu-latest 165 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 166 | steps: 167 | - uses: actions/checkout@v3 168 | with: 169 | fetch-depth: 0 170 | - uses: actions/setup-node@v3 171 | with: 172 | node-version: lts/* 173 | - uses: ipfs/aegir/actions/cache-node-modules@master 174 | - uses: ipfs/aegir/actions/docker-login@master 175 | with: 176 | docker-token: ${{ secrets.DOCKER_TOKEN }} 177 | docker-username: ${{ secrets.DOCKER_USERNAME }} 178 | - run: npm run --if-present release 179 | env: 180 | GITHUB_TOKEN: ${{ secrets.UCI_GITHUB_TOKEN || github.token }} 181 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 182 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Close and mark stale issue 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | 7 | jobs: 8 | stale: 9 | 10 | runs-on: ubuntu-latest 11 | permissions: 12 | issues: write 13 | pull-requests: write 14 | 15 | steps: 16 | - uses: actions/stale@v3 17 | with: 18 | repo-token: ${{ secrets.GITHUB_TOKEN }} 19 | stale-issue-message: 'Oops, seems like we needed more information for this issue, please comment with more details or this issue will be closed in 7 days.' 20 | close-issue-message: 'This issue was closed because it is missing author input.' 21 | stale-issue-label: 'kind/stale' 22 | any-of-labels: 'need/author-input' 23 | exempt-issue-labels: 'need/triage,need/community-input,need/maintainer-input,need/maintainers-input,need/analysis,status/blocked,status/in-progress,status/ready,status/deferred,status/inactive' 24 | days-before-issue-stale: 6 25 | days-before-issue-close: 7 26 | enable-statistics: true 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | dist 4 | .docs 5 | .coverage 6 | node_modules 7 | package-lock.json 8 | yarn.lock 9 | .vscode 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [6.0.0](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v5.0.0...v6.0.0) (2023-04-24) 2 | 3 | 4 | ### ⚠ BREAKING CHANGES 5 | 6 | * update all deps (#211) 7 | 8 | ### Dependencies 9 | 10 | * update all deps ([#211](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/211)) ([7718a34](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/7718a34d87abefce2bb63e6040e09b6cd467e63f)) 11 | 12 | ## [5.0.0](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v4.0.2...v5.0.0) (2023-01-06) 13 | 14 | 15 | ### ⚠ BREAKING CHANGES 16 | 17 | * update multiformats to v11 (#202) 18 | 19 | ### Bug Fixes 20 | 21 | * update multiformats to v11 ([#202](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/202)) ([4f69ed4](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/4f69ed44509046992428068833d732ae0e3dc441)) 22 | 23 | 24 | ### Trivial Changes 25 | 26 | * add archive note to README ([#199](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/199)) ([2d9fb2e](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/2d9fb2eae5fc6c6b33558d43549e1114e4789ee8)) 27 | 28 | ## [4.0.2](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v4.0.1...v4.0.2) (2022-11-15) 29 | 30 | 31 | ### Documentation 32 | 33 | * fixed wrong import name ([#197](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/197)) ([281fee0](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/281fee0c99710f2be718d652a0fa0f7c772dba58)) 34 | 35 | ## [4.0.1](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v4.0.0...v4.0.1) (2022-10-17) 36 | 37 | 38 | ### Dependencies 39 | 40 | * **dev:** bump it-all from 1.0.6 to 2.0.0 ([#195](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/195)) ([c81c9b6](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/c81c9b69a4c21d06d8ed135f4dc508ad48d23c3a)) 41 | 42 | ## [4.0.0](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v3.0.0...v4.0.0) (2022-10-12) 43 | 44 | 45 | ### ⚠ BREAKING CHANGES 46 | 47 | * modules no longer implement `Initializable` instead switching to constructor injection 48 | 49 | ### Bug Fixes 50 | 51 | * remove @libp2p/components ([#194](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/194)) ([fc53a59](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/fc53a59e52c7d4111944008c08e183546706b823)) 52 | 53 | 54 | ### Dependencies 55 | 56 | * **dev:** bump @libp2p/interface-mocks from 4.0.3 to 6.0.1 ([#192](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/192)) ([5cf8ccc](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/5cf8ccc73782f40da643d0a646f33561a1e7cc83)) 57 | 58 | ## [3.0.0](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v2.0.3...v3.0.0) (2022-10-07) 59 | 60 | 61 | ### ⚠ BREAKING CHANGES 62 | 63 | * bump @libp2p/interface-transport from 1.0.4 to 2.0.0 (#190) 64 | 65 | ### Dependencies 66 | 67 | * bump @libp2p/interface-transport from 1.0.4 to 2.0.0 ([#190](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/190)) ([a2cb1e4](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/a2cb1e479a250e5ef063bad98bda86ac75e42db8)) 68 | 69 | ## [2.0.3](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v2.0.2...v2.0.3) (2022-09-21) 70 | 71 | 72 | ### Trivial Changes 73 | 74 | * Update .github/workflows/stale.yml [skip ci] ([217d1df](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/217d1df6a662c8d669db23f193c5db42144aeec2)) 75 | 76 | 77 | ### Dependencies 78 | 79 | * update @multiformats/multiaddr to 11.0.0 ([#188](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/188)) ([d56fa4b](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/d56fa4b9bc2d0ba132d7878c4f286e6ec1b5d3d0)) 80 | 81 | ## [2.0.2](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v2.0.1...v2.0.2) (2022-08-10) 82 | 83 | 84 | ### Bug Fixes 85 | 86 | * update all deps ([#184](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/184)) ([3585146](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/35851466f34294aa2a38f1fa276b5c624aa484ac)) 87 | 88 | ## [2.0.1](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v2.0.0...v2.0.1) (2022-06-17) 89 | 90 | 91 | ### Trivial Changes 92 | 93 | * update deps ([#176](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/176)) ([5b19a56](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/5b19a56c61865dec611ea4accaa6a9254987ba49)) 94 | 95 | ## [2.0.0](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v1.0.2...v2.0.0) (2022-06-15) 96 | 97 | 98 | ### ⚠ BREAKING CHANGES 99 | 100 | * uses new single-issue libp2p interface modules 101 | 102 | ### Features 103 | 104 | * update libp2p interfaces ([#172](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/172)) ([1d55fba](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/1d55fbaef8f57b44930401b91f830b5656c65edc)) 105 | 106 | ### [1.0.2](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v1.0.1...v1.0.2) (2022-05-23) 107 | 108 | 109 | ### Bug Fixes 110 | 111 | * update interfaces ([#168](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/168)) ([b5c4763](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/b5c47633396be382c88ae079b3a1f29efd3bf831)) 112 | 113 | 114 | ### Trivial Changes 115 | 116 | * **deps:** bump undici from 4.16.0 to 5.2.0 ([#164](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/164)) ([689122c](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/689122cce110afa706ade91a806b654ba7b294dc)) 117 | 118 | ### [1.0.1](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v1.0.0...v1.0.1) (2022-05-04) 119 | 120 | 121 | ### Bug Fixes 122 | 123 | * update interfaces ([#163](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/163)) ([c4a2c4d](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/c4a2c4d7fb60a7f5c05358810057e02c33427be5)) 124 | 125 | ## [1.0.0](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v0.7.1...v1.0.0) (2022-03-28) 126 | 127 | 128 | ### ⚠ BREAKING CHANGES 129 | 130 | * this module is now ESM-only 131 | 132 | Co-authored-by: achingbrain 133 | 134 | ### Features 135 | 136 | * convert to typescript ([#151](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/151)) ([85ce5cf](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/85ce5cf49afcdb788212e250673e8e4f8609055d)) 137 | 138 | 139 | ### Bug Fixes 140 | 141 | * add 'node-pre-gyp' installation to 'check' and 'test-node' actions ([#152](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/152)) ([bf4a68b](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/bf4a68b9e4a159bb6a4d73a92b11a6cfdb140178)) 142 | 143 | 144 | ### Trivial Changes 145 | 146 | * add note about `node-pre-gyp` to readme.md ([#141](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/141)) ([ab4cc82](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/ab4cc825d75801972b8c33e37ffca28796b9a1aa)), closes [#140](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/140) 147 | * **deps-dev:** bump aegir from 35.2.1 to 36.0.0 ([#139](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/139)) ([720cfad](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/720cfadaea310c84feb3fe9eecc005ed53e1b95f)) 148 | * replace Travis with Github Actions ([#150](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/150)) ([a73735b](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/a73735b80bb32d4f4618e5071abfd17f90679ee1)) 149 | * update project config ([13ab340](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/13ab340c9733c8ac0d134e4735a177722780bd27)) 150 | * update Readme ([#148](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/148)) ([ba9facb](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/ba9facb708d5f802d0c9010fa3d1351f9a6a11f8)) 151 | 152 | ## [0.7.1](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v0.7.0...v0.7.1) (2021-09-20) 153 | 154 | 155 | 156 | # [0.7.0](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v0.6.0...v0.7.0) (2021-07-09) 157 | 158 | 159 | ### chore 160 | 161 | * update deps ([#128](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/128)) ([d574b7d](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/d574b7df1fa40250ef7ead448d362fd55a6eb974)) 162 | 163 | 164 | ### BREAKING CHANGES 165 | 166 | * uses new peer id, multiaddr, etc 167 | 168 | 169 | 170 | # [0.6.0](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v0.5.1...v0.6.0) (2021-04-13) 171 | 172 | 173 | 174 | ## [0.5.1](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v0.5.0...v0.5.1) (2021-02-05) 175 | 176 | 177 | ### Bug Fixes 178 | 179 | * lint ([3490d36](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/3490d3635c0167f3090925a68cc0b3cb78acb062)) 180 | 181 | 182 | 183 | # [0.5.0](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v0.4.1...v0.5.0) (2021-01-22) 184 | 185 | 186 | ### Bug Fixes 187 | 188 | * add remote addr on listener connection ([#97](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/97)) ([c562a0d](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/c562a0d47288bcbb3cdb0fb1118a0899903f811b)) 189 | 190 | 191 | 192 | 193 | ## [0.4.1](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v0.4.0...v0.4.1) (2020-12-11) 194 | 195 | 196 | ### Bug Fixes 197 | 198 | * transport should not handle connection if upgradeInbound throws ([#32](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/32)) ([1711a7d](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/1711a7d)) 199 | 200 | 201 | 202 | 203 | # [0.4.0](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v0.3.1...v0.4.0) (2019-10-02) 204 | 205 | 206 | ### Code Refactoring 207 | 208 | * switch to async iterators ([#30](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/30)) ([4def4aa](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/4def4aa)) 209 | 210 | 211 | ### BREAKING CHANGES 212 | 213 | * Switch to using async/await and async iterators. The transport and connection interfaces have changed. 214 | 215 | 216 | 217 | 218 | ## [0.3.1](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v0.3.0...v0.3.1) (2018-09-26) 219 | 220 | 221 | ### Bug Fixes 222 | 223 | * options no longer ignored in createListener ([#23](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/23)) ([6dd29ed](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/6dd29ed)) 224 | 225 | 226 | 227 | 228 | # [0.3.0](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v0.2.0...v0.3.0) (2018-04-05) 229 | 230 | 231 | ### Features 232 | 233 | * add class-is module ([78c70ff](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/78c70ff)) 234 | * skip p2p-circuit addresses ([#15](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/15)) ([914f006](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/914f006)) 235 | 236 | 237 | 238 | 239 | # [0.2.0](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v0.1.1...v0.2.0) (2017-09-03) 240 | 241 | 242 | ### Features 243 | 244 | * p2p addrs situation ([#14](https://github.com/libp2p/js-libp2p-webrtc-direct/issues/14)) ([f5f8a21](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/f5f8a21)) 245 | 246 | 247 | 248 | 249 | ## [0.1.1](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/v0.1.0...v0.1.1) (2017-01-16) 250 | 251 | 252 | 253 | 254 | # [0.1.0](https://github.com/libp2p/js-libp2p-webrtc-direct/compare/8a5d975...v0.1.0) (2017-01-16) 255 | 256 | 257 | ### Features 258 | 259 | * v0.1.0 ([8a5d975](https://github.com/libp2p/js-libp2p-webrtc-direct/commit/8a5d975)) 260 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This project is dual licensed under MIT and Apache-2.0. 2 | 3 | MIT: https://www.opensource.org/licenses/mit 4 | Apache-2.0: https://www.apache.org/licenses/license-2.0 5 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 2 | 3 | http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 6 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⚠️⚠️⚠️⚠️⚠️⚠️ 2 | **Status:** 3 | 4 | [Archived](https://github.com/libp2p/github-mgmt/pull/80) and not maintained 5 | 6 | **Alternatives:** 7 | 8 | WebRTC Browser-to-Server is being implemented in js-libp2p and tracked here https://github.com/libp2p/js-libp2p/issues/1478 per the specification: https://github.com/libp2p/specs/pull/412 9 | 10 | WebRTC Browser-to-Browser is being tracked here: https://github.com/libp2p/js-libp2p/issues/1462 11 | 12 | **Questions:** 13 | 14 | Please direct any questions about the specification to: https://github.com/libp2p/specs/issues 15 | 16 | Please direct any questions about the js-libp2p WebRTC implementations to: 17 | https://github.com/libp2p/js-libp2p/issues/1478 or 18 | https://github.com/libp2p/js-libp2p/issues/1462 19 | # ⚠️⚠️⚠️⚠️⚠️⚠️ 20 | 21 | # @libp2p/webrtc-direct 22 | 23 | [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) 24 | [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) 25 | [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p-webrtc-direct.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-webrtc-direct) 26 | [![CI](https://img.shields.io/github/actions/workflow/status/libp2p/js-libp2p-webrtc-direct/js-test-and-release.yml?branch=master\&style=flat-square)](https://github.com/libp2p/js-libp2p-webrtc-direct/actions/workflows/js-test-and-release.yml?query=branch%3Amaster) 27 | 28 | > Dial using WebRTC without the need to set up any Signalling Rendezvous Point! 29 | 30 | ## Table of contents 31 | 32 | - [Install](#install) 33 | - [Browser ` 54 | ``` 55 | 56 | ![](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/packages/libp2p-interfaces/src/connection/img/badge.png) 57 | ![](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/packages/libp2p-interfaces/src/transport/img/badge.png) 58 | 59 | **NOTE:** To run build scripts `node-pre-gyp` is required. You can install it by running `npm install -g node-pre-gyp`. 60 | 61 | ## Usage 62 | 63 | ```js 64 | import { createLibp2p } from 'libp2p' 65 | import { webRTCDirect } from '@libp2p/webrtc-direct' 66 | 67 | const node = await createLibp2p({ 68 | transports: [ 69 | webRTCDirect() 70 | ] 71 | //... other config 72 | }) 73 | await node.start() 74 | await node.dial('/ip4/127.0.0.1/tcp/9090/http/p2p-webrtc-direct') 75 | ``` 76 | 77 | ## API 78 | 79 | ### Transport 80 | 81 | [![](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/packages/libp2p-interfaces/src/transport/img/badge.png)](https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/libp2p-interfaces/src/transport) 82 | 83 | ### Connection 84 | 85 | [![](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/packages/libp2p-interfaces/src/connection/img/badge.png)](https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/libp2p-interfaces/src/connection) 86 | 87 | ## Contribute 88 | 89 | The libp2p implementation in JavaScript is a work in progress. As such, there are a few things you can do right now to help out: 90 | 91 | - Go through the modules and **check out existing issues**. This would be especially useful for modules in active development. Some knowledge of IPFS/libp2p may be required, as well as the infrastructure behind it - for instance, you may need to read up on p2p and more complex operations like muxing to be able to help technically. 92 | - **Perform code reviews**. 93 | - **Add tests**. There can never be enough tests. 94 | 95 | ## License 96 | 97 | Licensed under either of 98 | 99 | - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) 100 | - MIT ([LICENSE-MIT](LICENSE-MIT) / ) 101 | 102 | ## Contribution 103 | 104 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 105 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@libp2p/webrtc-direct", 3 | "version": "6.0.0", 4 | "description": "Dial using WebRTC without the need to set up any Signalling Rendezvous Point! ", 5 | "license": "Apache-2.0 OR MIT", 6 | "homepage": "https://github.com/libp2p/js-libp2p-webrtc-direct#readme", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/libp2p/js-libp2p-webrtc-direct.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/libp2p/js-libp2p-webrtc-direct/issues" 13 | }, 14 | "keywords": [ 15 | "connection", 16 | "dial", 17 | "libp2p", 18 | "stream", 19 | "webrtc" 20 | ], 21 | "engines": { 22 | "node": ">=16.0.0", 23 | "npm": ">=7.0.0" 24 | }, 25 | "type": "module", 26 | "types": "./dist/src/index.d.ts", 27 | "files": [ 28 | "src", 29 | "dist", 30 | "!dist/test", 31 | "!**/*.tsbuildinfo" 32 | ], 33 | "exports": { 34 | ".": { 35 | "types": "./src/index.d.ts", 36 | "import": "./dist/src/index.js" 37 | } 38 | }, 39 | "eslintConfig": { 40 | "extends": "ipfs", 41 | "parserOptions": { 42 | "sourceType": "module" 43 | } 44 | }, 45 | "release": { 46 | "branches": [ 47 | "master" 48 | ], 49 | "plugins": [ 50 | [ 51 | "@semantic-release/commit-analyzer", 52 | { 53 | "preset": "conventionalcommits", 54 | "releaseRules": [ 55 | { 56 | "breaking": true, 57 | "release": "major" 58 | }, 59 | { 60 | "revert": true, 61 | "release": "patch" 62 | }, 63 | { 64 | "type": "feat", 65 | "release": "minor" 66 | }, 67 | { 68 | "type": "fix", 69 | "release": "patch" 70 | }, 71 | { 72 | "type": "docs", 73 | "release": "patch" 74 | }, 75 | { 76 | "type": "test", 77 | "release": "patch" 78 | }, 79 | { 80 | "type": "deps", 81 | "release": "patch" 82 | }, 83 | { 84 | "scope": "no-release", 85 | "release": false 86 | } 87 | ] 88 | } 89 | ], 90 | [ 91 | "@semantic-release/release-notes-generator", 92 | { 93 | "preset": "conventionalcommits", 94 | "presetConfig": { 95 | "types": [ 96 | { 97 | "type": "feat", 98 | "section": "Features" 99 | }, 100 | { 101 | "type": "fix", 102 | "section": "Bug Fixes" 103 | }, 104 | { 105 | "type": "chore", 106 | "section": "Trivial Changes" 107 | }, 108 | { 109 | "type": "docs", 110 | "section": "Documentation" 111 | }, 112 | { 113 | "type": "deps", 114 | "section": "Dependencies" 115 | }, 116 | { 117 | "type": "test", 118 | "section": "Tests" 119 | } 120 | ] 121 | } 122 | } 123 | ], 124 | "@semantic-release/changelog", 125 | "@semantic-release/npm", 126 | "@semantic-release/github", 127 | "@semantic-release/git" 128 | ] 129 | }, 130 | "scripts": { 131 | "clean": "aegir clean", 132 | "lint": "aegir lint", 133 | "dep-check": "aegir dep-check", 134 | "build": "aegir build", 135 | "test": "aegir test -t node -t browser", 136 | "test:node": "aegir test -t node", 137 | "test:chrome": "aegir test -t browser", 138 | "test:firefox": "aegir test -t browser -- --browser firefox", 139 | "release": "aegir release" 140 | }, 141 | "dependencies": { 142 | "@libp2p/interface-connection": "^5.0.1", 143 | "@libp2p/interface-transport": "^4.0.0", 144 | "@libp2p/logger": "^2.0.1", 145 | "@libp2p/utils": "^3.0.2", 146 | "@libp2p/webrtc-peer": "^2.0.0", 147 | "@multiformats/mafmt": "^12.1.0", 148 | "@multiformats/multiaddr": "^12.1.2", 149 | "abortable-iterator": "^5.0.1", 150 | "err-code": "^3.0.0", 151 | "multiformats": "^11.0.0", 152 | "native-fetch": "^4.0.2", 153 | "p-event": "^5.0.1", 154 | "uint8arrays": "^4.0.2", 155 | "undici": "^5.2.0", 156 | "wherearewe": "^2.0.1" 157 | }, 158 | "devDependencies": { 159 | "@libp2p/interface-mocks": "^11.0.0", 160 | "@libp2p/interface-transport-compliance-tests": "^4.0.0", 161 | "@libp2p/interfaces": "^3.3.1", 162 | "@mapbox/node-pre-gyp": "^1.0.8", 163 | "aegir": "^38.1.8", 164 | "it-all": "^3.0.1", 165 | "it-pipe": "^3.0.1", 166 | "it-stream-types": "^2.0.1", 167 | "multiaddr": "^10.0.0", 168 | "uint8arraylist": "^2.3.2", 169 | "wrtc": "^0.4.6" 170 | }, 171 | "browser": { 172 | "./dist/src/listener.js": "./dist/src/listener.browser.js" 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | 2 | // p2p multi-address code 3 | export const CODE_P2P = 421 4 | export const CODE_CIRCUIT = 290 5 | 6 | // Time to wait for a connection to close gracefully before destroying it 7 | // manually 8 | export const CLOSE_TIMEOUT = 2000 9 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { logger } from '@libp2p/logger' 2 | import * as mafmt from '@multiformats/mafmt' 3 | import { base58btc } from 'multiformats/bases/base58' 4 | import { fetch } from 'native-fetch' 5 | import { AbortError } from 'abortable-iterator' 6 | import { toString } from 'uint8arrays/to-string' 7 | import { fromString } from 'uint8arrays/from-string' 8 | import { CODE_CIRCUIT, CODE_P2P } from './constants.js' 9 | import { toMultiaddrConnection } from './socket-to-conn.js' 10 | import { createListener } from './listener.js' 11 | import { Signal, WebRTCInitiator, WebRTCInitiatorInit, WebRTCReceiverInit, WRTC } from '@libp2p/webrtc-peer' 12 | import { symbol } from '@libp2p/interface-transport' 13 | import type { CreateListenerOptions, DialOptions, Listener, Transport } from '@libp2p/interface-transport' 14 | import type { Multiaddr } from '@multiformats/multiaddr' 15 | import type { Connection } from '@libp2p/interface-connection' 16 | 17 | const log = logger('libp2p:webrtc-direct') 18 | 19 | export interface WebRTCDirectInit { 20 | wrtc?: WRTC 21 | initiatorOptions?: WebRTCInitiatorInit 22 | recieverOptions?: WebRTCReceiverInit 23 | } 24 | 25 | class WebRTCDirect implements Transport { 26 | private readonly initiatorOptions?: WebRTCInitiatorInit 27 | private readonly recieverOptions?: WebRTCReceiverInit 28 | public wrtc?: WRTC 29 | 30 | constructor (init?: WebRTCDirectInit) { 31 | this.initiatorOptions = init?.initiatorOptions 32 | this.recieverOptions = init?.recieverOptions 33 | this.wrtc = init?.wrtc 34 | } 35 | 36 | get [symbol] (): true { 37 | return true 38 | } 39 | 40 | get [Symbol.toStringTag] (): string { 41 | return '@libp2p/webrtc-direct' 42 | } 43 | 44 | async dial (ma: Multiaddr, options: DialOptions): Promise { 45 | const socket = await this._connect(ma, options) 46 | const maConn = toMultiaddrConnection(socket, { remoteAddr: ma, signal: options.signal }) 47 | log('new outbound connection %s', maConn.remoteAddr) 48 | const conn = await options.upgrader.upgradeOutbound(maConn) 49 | log('outbound connection %s upgraded', maConn.remoteAddr) 50 | return conn 51 | } 52 | 53 | async _connect (ma: Multiaddr, options: DialOptions): Promise { 54 | if (options.signal?.aborted === true) { 55 | throw new AbortError() 56 | } 57 | 58 | const channelOptions = { 59 | initiator: true, 60 | trickle: false, 61 | ...this.initiatorOptions 62 | } 63 | 64 | // Use custom WebRTC implementation 65 | if (this.wrtc != null) { 66 | channelOptions.wrtc = this.wrtc 67 | } 68 | 69 | return await new Promise((resolve, reject) => { 70 | let connected: boolean 71 | 72 | const cOpts = ma.toOptions() 73 | log('Dialing %s:%s', cOpts.host, cOpts.port) 74 | 75 | const channel = new WebRTCInitiator(channelOptions) 76 | 77 | const onError = (evt: CustomEvent): void => { 78 | const err = evt.detail 79 | 80 | if (!connected) { 81 | const msg = `connection error ${cOpts.host}:${cOpts.port}: ${err.message}` 82 | 83 | log.error(msg) 84 | err.message = msg 85 | done(err) 86 | } 87 | } 88 | 89 | const onReady = (): void => { 90 | connected = true 91 | 92 | log('connection opened %s:%s', cOpts.host, cOpts.port) 93 | done() 94 | } 95 | 96 | const onAbort = (): void => { 97 | log.error('connection aborted %s:%s', cOpts.host, cOpts.port) 98 | void channel.close().finally(() => { 99 | done(new AbortError()) 100 | }) 101 | } 102 | 103 | const done = (err?: Error): void => { 104 | channel.removeEventListener('error', onError) 105 | channel.removeEventListener('ready', onReady) 106 | options.signal?.removeEventListener('abort', onAbort) 107 | 108 | if (err != null) { 109 | reject(err) 110 | } else { 111 | resolve(channel) 112 | } 113 | } 114 | 115 | channel.addEventListener('error', onError, { 116 | once: true 117 | }) 118 | channel.addEventListener('ready', onReady, { 119 | once: true 120 | }) 121 | channel.addEventListener('close', () => { 122 | channel.removeEventListener('error', onError) 123 | }) 124 | options.signal?.addEventListener('abort', onAbort) 125 | 126 | const onSignal = async (signal: Signal): Promise => { 127 | if (signal.type !== 'offer') { 128 | // skip candidates, just send the offer as it includes the candidates 129 | return 130 | } 131 | 132 | const signalStr = JSON.stringify(signal) 133 | 134 | let host = cOpts.host 135 | 136 | if (cOpts.family === 6 && !host.startsWith('[')) { 137 | host = `[${host}]` 138 | } 139 | 140 | const url = `http://${host}:${cOpts.port}` 141 | const path = `/?signal=${base58btc.encode(fromString(signalStr))}` 142 | const uri = url + path 143 | 144 | try { 145 | const res = await fetch(uri) 146 | const body = await res.text() 147 | 148 | if (body.trim() === '') { 149 | // no response to this signal 150 | return 151 | } 152 | 153 | const incSignalBuf = base58btc.decode(body) 154 | const incSignalStr = toString(incSignalBuf) 155 | const incSignal = JSON.parse(incSignalStr) 156 | 157 | channel.handleSignal(incSignal) 158 | } catch (err: any) { 159 | await channel.close(err) 160 | reject(err) 161 | } 162 | } 163 | 164 | channel.addEventListener('signal', (evt) => { 165 | const signal = evt.detail 166 | 167 | void onSignal(signal).catch(async err => { 168 | await channel.close(err) 169 | }) 170 | }) 171 | }) 172 | } 173 | 174 | /** 175 | * Creates a WebrtcDirect listener. The provided `handler` function will be called 176 | * anytime a new incoming Connection has been successfully upgraded via 177 | * `upgrader.upgradeInbound`. 178 | */ 179 | createListener (options: CreateListenerOptions): Listener { 180 | return createListener({ 181 | ...options, 182 | receiverOptions: this.recieverOptions, 183 | wrtc: this.wrtc 184 | }) 185 | } 186 | 187 | /** 188 | * Takes a list of `Multiaddr`s and returns only valid addresses 189 | */ 190 | filter (multiaddrs: Multiaddr[]): Multiaddr[] { 191 | multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs] 192 | 193 | return multiaddrs.filter((ma) => { 194 | if (ma.protoCodes().includes(CODE_CIRCUIT)) { 195 | return false 196 | } 197 | 198 | return mafmt.P2PWebRTCDirect.matches(ma.decapsulateCode(CODE_P2P)) 199 | }) 200 | } 201 | } 202 | 203 | export function webRTCDirect (init: WebRTCDirectInit = {}): () => Transport { 204 | return () => new WebRTCDirect(init) 205 | } 206 | -------------------------------------------------------------------------------- /src/listener.browser.ts: -------------------------------------------------------------------------------- 1 | import type { Listener } from '@libp2p/interface-transport' 2 | 3 | export function createListener (): Listener { 4 | throw new Error('WebRTCDirect Servers can not be created in the browser!') 5 | } 6 | -------------------------------------------------------------------------------- /src/listener.ts: -------------------------------------------------------------------------------- 1 | import http from 'http' 2 | import { logger } from '@libp2p/logger' 3 | import { base58btc } from 'multiformats/bases/base58' 4 | import { toString } from 'uint8arrays/to-string' 5 | import { fromString } from 'uint8arrays/from-string' 6 | import type { Multiaddr } from '@multiformats/multiaddr' 7 | import type { IncomingMessage, ServerResponse } from 'http' 8 | import { EventEmitter, CustomEvent } from '@libp2p/interfaces/events' 9 | import type { MultiaddrConnection, Connection } from '@libp2p/interface-connection' 10 | import type { Listener, CreateListenerOptions, ConnectionHandler, ListenerEvents, Upgrader } from '@libp2p/interface-transport' 11 | import { ipPortToMultiaddr } from '@libp2p/utils/ip-port-to-multiaddr' 12 | import { toMultiaddrConnection } from './socket-to-conn.js' 13 | import { Signal, WebRTCReceiver, WebRTCReceiverInit, WRTC } from '@libp2p/webrtc-peer' 14 | import errCode from 'err-code' 15 | import { pEvent } from 'p-event' 16 | 17 | const log = logger('libp2p:webrtc-direct:listener') 18 | 19 | interface WebRTCDirectListenerOptions extends CreateListenerOptions { 20 | receiverOptions?: WebRTCReceiverInit 21 | wrtc?: WRTC 22 | } 23 | 24 | interface WebRTCDirectServerEvents { 25 | 'error': CustomEvent 26 | 'listening': CustomEvent 27 | 'connection': CustomEvent 28 | } 29 | 30 | class WebRTCDirectServer extends EventEmitter { 31 | private readonly server: http.Server 32 | private readonly wrtc?: WRTC 33 | private readonly receiverOptions?: WebRTCReceiverInit 34 | private connections: MultiaddrConnection[] 35 | private channels: WebRTCReceiver[] 36 | 37 | constructor (multiaddr: Multiaddr, wrtc?: WRTC, receiverOptions?: WebRTCReceiverInit) { 38 | super() 39 | 40 | this.connections = [] 41 | this.channels = [] 42 | this.wrtc = wrtc 43 | this.receiverOptions = receiverOptions 44 | this.server = http.createServer() 45 | 46 | this.server.on('request', (req: IncomingMessage, res: ServerResponse) => { 47 | void this.processRequest(req, res).catch(err => { 48 | log.error(err) 49 | }) 50 | }) 51 | 52 | this.server.on('error', (err) => this.dispatchEvent(new CustomEvent('error', { detail: err }))) 53 | 54 | const lOpts = multiaddr.toOptions() 55 | 56 | this.server.on('listening', (err: Error) => { 57 | if (err != null) { 58 | this.dispatchEvent(new CustomEvent('error', { detail: err })) 59 | 60 | return 61 | } 62 | 63 | this.dispatchEvent(new CustomEvent('listening')) 64 | log('Listening on %s %s', lOpts.port, lOpts.host) 65 | }) 66 | 67 | this.server.listen(lOpts) 68 | } 69 | 70 | async processRequest (req: IncomingMessage, res: ServerResponse): Promise { 71 | const remoteAddress = req?.socket?.remoteAddress 72 | const remotePort = req?.socket.remotePort 73 | const remoteHost = req.headers.host 74 | const requestUrl = req.url 75 | 76 | if (remoteAddress == null || remotePort == null || requestUrl == null || remoteHost == null) { 77 | const err = new Error('Invalid listener request. Specify request\'s url, remoteAddress, remotePort.') 78 | log.error(err) 79 | res.writeHead(500) 80 | res.end(err) 81 | return 82 | } 83 | res.setHeader('Content-Type', 'text/plain') 84 | res.setHeader('Access-Control-Allow-Origin', '*') 85 | 86 | const url = new URL(requestUrl, `http://${remoteHost}`) 87 | const incSignalStr = url.searchParams.get('signal') 88 | 89 | if (incSignalStr == null) { 90 | const err = new Error('Invalid listener request. Signal not found.') 91 | log.error(err) 92 | res.writeHead(500) 93 | res.end(err) 94 | return 95 | } 96 | 97 | const incSignalBuf = base58btc.decode(incSignalStr) 98 | const incSignal: Signal = JSON.parse(toString(incSignalBuf)) 99 | 100 | if (incSignal.type !== 'offer') { 101 | // offers contain candidates so only respond to the offer 102 | res.end() 103 | return 104 | } 105 | 106 | const channel = new WebRTCReceiver({ 107 | wrtc: this.wrtc, 108 | ...this.receiverOptions 109 | }) 110 | this.channels.push(channel) 111 | 112 | channel.addEventListener('signal', (evt) => { 113 | const signal = evt.detail 114 | const signalStr = JSON.stringify(signal) 115 | const signalEncoded = base58btc.encode(fromString(signalStr)) 116 | 117 | res.end(signalEncoded) 118 | }) 119 | channel.addEventListener('error', (evt) => { 120 | const err = evt.detail 121 | 122 | log.error('incoming connection errored with', err) 123 | res.end() 124 | void channel.close().catch(err => { 125 | log.error(err) 126 | }) 127 | }) 128 | channel.addEventListener('ready', () => { 129 | const maConn = toMultiaddrConnection(channel, { 130 | remoteAddr: ipPortToMultiaddr(remoteAddress, remotePort) 131 | }) 132 | log('new inbound connection %s', maConn.remoteAddr) 133 | 134 | this.connections.push(maConn) 135 | 136 | const untrackConn = (): void => { 137 | this.connections = this.connections.filter(c => c !== maConn) 138 | this.channels = this.channels.filter(c => c !== channel) 139 | } 140 | 141 | channel.addEventListener('close', untrackConn, { 142 | once: true 143 | }) 144 | 145 | this.dispatchEvent(new CustomEvent('connection', { detail: maConn })) 146 | }) 147 | 148 | channel.handleSignal(incSignal) 149 | } 150 | 151 | async close (): Promise { 152 | await Promise.all( 153 | this.channels.map(async channel => { await channel.close() }) 154 | ) 155 | 156 | await new Promise((resolve, reject) => { 157 | this.server.close((err) => { 158 | if (err != null) { 159 | reject(err); return 160 | } 161 | 162 | resolve() 163 | }) 164 | }) 165 | } 166 | } 167 | 168 | class WebRTCDirectListener extends EventEmitter implements Listener { 169 | private server?: WebRTCDirectServer 170 | private multiaddr?: Multiaddr 171 | private readonly wrtc?: WRTC 172 | private readonly receiverOptions?: WebRTCReceiverInit 173 | private readonly handler?: ConnectionHandler 174 | private readonly upgrader: Upgrader 175 | 176 | constructor (upgrader: Upgrader, wrtc?: WRTC, receiverOptions?: WebRTCReceiverInit, handler?: ConnectionHandler) { 177 | super() 178 | 179 | this.upgrader = upgrader 180 | this.wrtc = wrtc 181 | this.receiverOptions = receiverOptions 182 | this.handler = handler 183 | } 184 | 185 | async listen (multiaddr: Multiaddr): Promise { 186 | // Should only be used if not already listening 187 | if (this.multiaddr != null) { 188 | throw errCode(new Error('listener already in use'), 'ERR_ALREADY_LISTENING') 189 | } 190 | 191 | this.multiaddr = multiaddr 192 | const server = new WebRTCDirectServer(multiaddr, this.wrtc, this.receiverOptions) 193 | this.server = server 194 | 195 | this.server.addEventListener('connection', (evt) => { 196 | void this.onConnection(evt.detail).catch(err => { 197 | log.error(err) 198 | }) 199 | }) 200 | 201 | await pEvent(server, 'listening') 202 | 203 | this.dispatchEvent(new CustomEvent('listening')) 204 | } 205 | 206 | async onConnection (maConn: MultiaddrConnection): Promise { 207 | let connection: Connection 208 | 209 | try { 210 | connection = await this.upgrader.upgradeInbound(maConn) 211 | } catch (err) { 212 | log.error('inbound connection failed to upgrade', err) 213 | await maConn.close(); return 214 | } 215 | log('inbound connection %s upgraded', maConn.remoteAddr) 216 | 217 | if (this.handler != null) { 218 | this.handler(connection) 219 | } 220 | 221 | this.dispatchEvent(new CustomEvent('connection', { detail: connection })) 222 | } 223 | 224 | async close (): Promise { 225 | if (this.server != null) { 226 | await this.server.close() 227 | } 228 | 229 | this.dispatchEvent(new CustomEvent('close')) 230 | } 231 | 232 | getAddrs (): Multiaddr[] { 233 | if (this.multiaddr != null) { 234 | return [this.multiaddr] 235 | } 236 | 237 | return [] 238 | } 239 | } 240 | 241 | export function createListener (options: WebRTCDirectListenerOptions): Listener { 242 | return new WebRTCDirectListener(options.upgrader, options.wrtc, options.receiverOptions, options.handler) 243 | } 244 | -------------------------------------------------------------------------------- /src/socket-to-conn.ts: -------------------------------------------------------------------------------- 1 | import { abortableSource } from 'abortable-iterator' 2 | import { CLOSE_TIMEOUT } from './constants.js' 3 | import { logger } from '@libp2p/logger' 4 | import type { MultiaddrConnection } from '@libp2p/interface-connection' 5 | import type { WebRTCPeer } from '@libp2p/webrtc-peer' 6 | import type { AbortOptions } from '@libp2p/interfaces' 7 | import type { Multiaddr } from '@multiformats/multiaddr' 8 | 9 | const log = logger('libp2p:webrtc-direct:socket') 10 | 11 | export interface ToMultiaddrConnectionOptions extends AbortOptions { 12 | remoteAddr: Multiaddr 13 | } 14 | 15 | export function toMultiaddrConnection (socket: WebRTCPeer, options: ToMultiaddrConnectionOptions): MultiaddrConnection { 16 | const { sink, source } = socket 17 | 18 | const maConn: MultiaddrConnection = { 19 | remoteAddr: options.remoteAddr, 20 | 21 | async sink (source) { 22 | if (options.signal != null) { 23 | source = abortableSource(source, options.signal) 24 | } 25 | 26 | try { 27 | await sink(source) 28 | } catch (err: any) { 29 | // If aborted we can safely ignore 30 | if (err.type !== 'aborted') { 31 | // If the source errored the socket will already have been destroyed by 32 | // toIterable.duplex(). If the socket errored it will already be 33 | // destroyed. There's nothing to do here except log the error & return. 34 | log.error(err) 35 | } 36 | } 37 | }, 38 | 39 | source: (options.signal != null) ? abortableSource(source, options.signal) : source, 40 | 41 | timeline: { open: Date.now() }, 42 | 43 | async close () { 44 | if (socket.closed) { 45 | return 46 | } 47 | 48 | const start = Date.now() 49 | 50 | // Attempt to end the socket. If it takes longer to close than the 51 | // timeout, destroy it manually. 52 | const timeout = setTimeout(() => { 53 | if (maConn.remoteAddr != null) { 54 | const { host, port } = maConn.remoteAddr.toOptions() 55 | log('timeout closing socket to %s:%s after %dms, destroying it manually', 56 | host, port, Date.now() - start) 57 | } 58 | 59 | if (!socket.closed) { 60 | socket.close().catch(err => { 61 | log.error('could not close socket', err) 62 | }) 63 | } 64 | }, CLOSE_TIMEOUT) 65 | 66 | try { 67 | await socket.close() 68 | } finally { 69 | clearTimeout(timeout) 70 | } 71 | } 72 | } 73 | 74 | socket.addEventListener('close', () => { 75 | // In instances where `close` was not explicitly called, 76 | // such as an iterable stream ending, ensure we have set the close 77 | // timeline 78 | if (maConn.timeline.close == null) { 79 | maConn.timeline.close = Date.now() 80 | } 81 | }, { 82 | once: true 83 | }) 84 | 85 | return maConn 86 | } 87 | -------------------------------------------------------------------------------- /test/browser.ts: -------------------------------------------------------------------------------- 1 | 2 | import listenTests from './listen.js' 3 | import dialTests from './dial.js' 4 | import { webRTCDirect } from '../src/index.js' 5 | import type { Transport } from '@libp2p/interface-transport' 6 | 7 | describe('browser RTC', () => { 8 | const create = async (): Promise => { 9 | const ws = webRTCDirect()() 10 | 11 | return ws 12 | } 13 | 14 | dialTests(create) 15 | listenTests(create) 16 | }) 17 | -------------------------------------------------------------------------------- /test/compliance.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import type { Transport } from '@libp2p/interface-transport' 4 | import tests from '@libp2p/interface-transport-compliance-tests' 5 | import { multiaddr } from '@multiformats/multiaddr' 6 | 7 | export default (create: () => Promise): void => { 8 | describe('interface-transport compliance', function (): void { 9 | this.timeout(20 * 1000) 10 | 11 | tests({ 12 | async setup () { 13 | const ws = await create() 14 | 15 | const addrs = [ 16 | multiaddr('/ip4/127.0.0.1/tcp/22222/http/p2p-webrtc-direct'), 17 | multiaddr('/ip4/127.0.0.1/tcp/33333/http/p2p-webrtc-direct'), 18 | multiaddr('/ip4/127.0.0.1/tcp/44444/http/p2p-webrtc-direct'), 19 | multiaddr('/ip4/127.0.0.1/tcp/55555/http/p2p-webrtc-direct') 20 | ] 21 | 22 | // Used by the dial tests to simulate a delayed connect 23 | const connector = { 24 | delay () {}, 25 | restore () {} 26 | } 27 | 28 | return { transport: ws, addrs, connector } 29 | }, 30 | async teardown () {} 31 | }) 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /test/dial.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import { expect } from 'aegir/chai' 4 | import { multiaddr } from '@multiformats/multiaddr' 5 | import { pipe } from 'it-pipe' 6 | import all from 'it-all' 7 | import { fromString } from 'uint8arrays/from-string' 8 | import { mockRegistrar, mockUpgrader } from '@libp2p/interface-mocks' 9 | import type { Transport, Upgrader } from '@libp2p/interface-transport' 10 | import type { Uint8ArrayList } from 'uint8arraylist' 11 | import type { Source } from 'it-stream-types' 12 | import { EventEmitter } from '@libp2p/interfaces/events' 13 | 14 | // this node is started in aegir.cjs before the test run 15 | const REMOTE_MULTIADDR_IP4 = multiaddr('/ip4/127.0.0.1/tcp/12345/http/p2p-webrtc-direct') 16 | const REMOTE_MULTIADDR_IP6 = multiaddr('/ip6/::1/tcp/12346/http/p2p-webrtc-direct') 17 | const ECHO_PROTOCOL = '/echo/1.0.0' 18 | 19 | async function * toBytes (source: Source): AsyncGenerator { 20 | for await (const list of source) { 21 | yield * list 22 | } 23 | } 24 | 25 | export default (create: () => Promise): void => { 26 | describe('dial', function () { 27 | this.timeout(20 * 1000) 28 | 29 | let upgrader: Upgrader 30 | 31 | beforeEach(() => { 32 | const protocol = '/echo/1.0.0' 33 | const registrar = mockRegistrar() 34 | void registrar.handle(protocol, ({ stream }) => { 35 | void pipe( 36 | stream, 37 | stream 38 | ) 39 | }) 40 | upgrader = mockUpgrader({ 41 | registrar, 42 | events: new EventEmitter() 43 | }) 44 | }) 45 | 46 | it('dial on IPv4', async () => { 47 | const wd = await create() 48 | const conn = await wd.dial(REMOTE_MULTIADDR_IP4, { upgrader }) 49 | const stream = await conn.newStream(ECHO_PROTOCOL) 50 | const data = fromString('some data') 51 | 52 | const values = await pipe( 53 | [data], 54 | stream, 55 | toBytes, 56 | async (source) => await all(source) 57 | ) 58 | 59 | expect(values).to.deep.equal([data]) 60 | await conn.close() 61 | }) 62 | 63 | it('dials the same server twice', async () => { 64 | const wd = await create() 65 | const conn1 = await wd.dial(REMOTE_MULTIADDR_IP4, { upgrader }) 66 | const conn2 = await wd.dial(REMOTE_MULTIADDR_IP4, { upgrader }) 67 | 68 | const values = await Promise.all( 69 | [conn1, conn2].map(async conn => { 70 | const stream = await conn1.newStream(ECHO_PROTOCOL) 71 | const data = fromString('some data ' + conn.id) 72 | 73 | const values = await pipe( 74 | [data], 75 | stream, 76 | toBytes, 77 | async (source) => await all(source) 78 | ) 79 | 80 | return values 81 | }) 82 | ) 83 | 84 | expect(values).to.deep.equal([[ 85 | fromString('some data ' + conn1.id) 86 | ], [ 87 | fromString('some data ' + conn2.id) 88 | ]]) 89 | 90 | await conn1.close() 91 | await conn2.close() 92 | }) 93 | 94 | it('dial offline / non-existent node on IPv4, check callback', async () => { 95 | const wd = await create() 96 | const maOffline = multiaddr('/ip4/127.0.0.1/tcp/55555/http/p2p-webrtc-direct') 97 | 98 | await expect(wd.dial(maOffline, { upgrader })).to.eventually.be.rejected() 99 | }) 100 | 101 | it('dial on IPv6', async () => { 102 | const wd = await create() 103 | const conn = await wd.dial(REMOTE_MULTIADDR_IP6, { upgrader }) 104 | const stream = await conn.newStream(['/echo/1.0.0']) 105 | const data = fromString('some data') 106 | 107 | const values = await pipe( 108 | [data], 109 | stream, 110 | toBytes, 111 | async (source) => await all(source) 112 | ) 113 | 114 | expect(values).to.deep.equal([data]) 115 | await conn.close() 116 | }) 117 | }) 118 | } 119 | -------------------------------------------------------------------------------- /test/filter.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import { expect } from 'aegir/chai' 4 | import { multiaddr } from '@multiformats/multiaddr' 5 | import { webRTCDirect } from '../src/index.js' 6 | 7 | describe('filter', () => { 8 | it('filters non valid webrtc-direct multiaddrs', () => { 9 | const wd = webRTCDirect()() 10 | const maArr = [ 11 | multiaddr('/ip4/1.2.3.4/tcp/3456/http/p2p-webrtc-direct'), 12 | multiaddr('/ip4/127.0.0.1/tcp/9090/ws'), 13 | multiaddr('/ip4/127.0.0.1/tcp/9090/ws/p2p-webrtc-direct/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSoooo2'), 14 | multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSoooo4'), 15 | multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSoooo4' + 16 | '/p2p-circuit/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSoooo5') 17 | ] 18 | 19 | const filtered = wd.filter(maArr) 20 | expect(filtered.length).to.equal(1) 21 | }) 22 | 23 | it('filter a single addr for this transport', () => { 24 | const wd = webRTCDirect()() 25 | const ma = multiaddr('/ip4/127.0.0.1/tcp/9090/http/p2p-webrtc-direct') 26 | 27 | const filtered = wd.filter([ma]) 28 | expect(filtered.length).to.equal(1) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /test/instance.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import { expect } from 'aegir/chai' 4 | import { webRTCDirect } from '../src/index.js' 5 | 6 | describe('instances', () => { 7 | it('create', (done) => { 8 | const wdirect = webRTCDirect()() 9 | expect(wdirect).to.exist() 10 | done() 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /test/listen.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import { expect } from 'aegir/chai' 4 | import { multiaddr } from '@multiformats/multiaddr' 5 | import { mockRegistrar, mockUpgrader } from '@libp2p/interface-mocks' 6 | import { isBrowser } from 'wherearewe' 7 | import { pipe } from 'it-pipe' 8 | import { pEvent } from 'p-event' 9 | import type { Transport } from '@libp2p/interface-transport' 10 | import { EventEmitter } from '@libp2p/interfaces/events' 11 | 12 | const ECHO_PROTOCOL = '/echo/1.0.0' 13 | 14 | export default (create: () => Promise): void => { 15 | describe('listen', function () { 16 | this.timeout(20 * 1000) 17 | 18 | if (isBrowser) { 19 | return 20 | } 21 | 22 | let wd: Transport 23 | 24 | const ma = multiaddr('/ip4/127.0.0.1/tcp/20123/http/p2p-webrtc-direct') 25 | 26 | before(async () => { 27 | wd = await create() 28 | }) 29 | 30 | it('listen, check for promise', async () => { 31 | const listener = wd.createListener({ 32 | upgrader: mockUpgrader({ 33 | events: new EventEmitter() 34 | }) 35 | }) 36 | 37 | await listener.listen(ma) 38 | await listener.close() 39 | }) 40 | 41 | it('listen, check for listening event', (done) => { 42 | const listener = wd.createListener({ 43 | upgrader: mockUpgrader({ 44 | events: new EventEmitter() 45 | }) 46 | }) 47 | 48 | listener.addEventListener('listening', () => { 49 | void listener.close() 50 | .then(done, done) 51 | }, { 52 | once: true 53 | }) 54 | void listener.listen(ma) 55 | }) 56 | 57 | it('listen, check for the close event', (done) => { 58 | const listener = wd.createListener({ 59 | upgrader: mockUpgrader({ 60 | events: new EventEmitter() 61 | }) 62 | }) 63 | void listener.listen(ma).then(async () => { 64 | listener.addEventListener('close', () => { done() }, { 65 | once: true 66 | }) 67 | 68 | await listener.close() 69 | }) 70 | }) 71 | 72 | it('listen in 0.0.0.0', async () => { 73 | const listener = wd.createListener({ 74 | upgrader: mockUpgrader({ 75 | events: new EventEmitter() 76 | }) 77 | }) 78 | 79 | await listener.listen(multiaddr('/ip4/0.0.0.0/tcp/48322')) 80 | await listener.close() 81 | }) 82 | 83 | it('listen in port 0', async () => { 84 | const listener = wd.createListener({ 85 | upgrader: mockUpgrader({ 86 | events: new EventEmitter() 87 | }) 88 | }) 89 | 90 | await listener.listen(multiaddr('/ip4/127.0.0.1/tcp/0')) 91 | await listener.close() 92 | }) 93 | 94 | it('listen on IPv6 addr', async () => { 95 | const listener = wd.createListener({ 96 | upgrader: mockUpgrader({ 97 | events: new EventEmitter() 98 | }) 99 | }) 100 | 101 | await listener.listen(multiaddr('/ip6/::1/tcp/48322')) 102 | await listener.close() 103 | }) 104 | 105 | it('getAddrs', async () => { 106 | const listener = wd.createListener({ 107 | upgrader: mockUpgrader({ 108 | events: new EventEmitter() 109 | }) 110 | }) 111 | 112 | await listener.listen(ma) 113 | 114 | const addrs = listener.getAddrs() 115 | expect(addrs[0]).to.deep.equal(ma) 116 | 117 | await listener.close() 118 | }) 119 | 120 | it('should untrack conn after being closed', async function () { 121 | const ma1 = multiaddr('/ip4/127.0.0.1/tcp/12346/http/p2p-webrtc-direct') 122 | const registrar = mockRegistrar() 123 | void registrar.handle(ECHO_PROTOCOL, ({ stream }) => { 124 | void pipe( 125 | stream, 126 | stream 127 | ) 128 | }) 129 | const upgrader = mockUpgrader({ 130 | registrar, 131 | events: new EventEmitter() 132 | }) 133 | 134 | const wd1 = await create() 135 | const listener1 = wd1.createListener({ 136 | upgrader, 137 | handler: (conn) => { 138 | void conn.newStream([ECHO_PROTOCOL]) 139 | .then((stream) => { 140 | void pipe(stream, stream) 141 | }) 142 | } 143 | }) 144 | 145 | await listener1.listen(ma1) 146 | expect(listener1).to.have.nested.property('server.connections').that.has.lengthOf(0) 147 | 148 | const conn = await wd.dial(ma1, { 149 | upgrader 150 | }) 151 | 152 | // wait for listener to know of the connect 153 | await pEvent(listener1, 'connection') 154 | 155 | expect(listener1).to.have.nested.property('server.connections').that.has.lengthOf(1) 156 | 157 | await conn.close() 158 | 159 | // wait for listener to know of the disconnect 160 | await new Promise((resolve) => { 161 | setTimeout(resolve, 1000) 162 | }) 163 | 164 | expect(listener1).to.have.nested.property('server.connections').that.has.lengthOf(0) 165 | 166 | await listener1.close() 167 | }) 168 | 169 | it('should have remoteAddress in listener connection', async function () { 170 | const ma1 = multiaddr('/ip4/127.0.0.1/tcp/12346/http/p2p-webrtc-direct') 171 | const registrar = mockRegistrar() 172 | void registrar.handle(ECHO_PROTOCOL, ({ stream }) => { 173 | void pipe( 174 | stream, 175 | stream 176 | ) 177 | }) 178 | const upgrader = mockUpgrader({ 179 | registrar, 180 | events: new EventEmitter() 181 | }) 182 | 183 | const wd1 = await create() 184 | const listener1 = wd1.createListener({ 185 | handler: (conn) => { 186 | expect(conn.remoteAddr).to.exist() 187 | 188 | void conn.newStream([ECHO_PROTOCOL]) 189 | .then((stream) => { 190 | void pipe(stream, stream) 191 | }) 192 | }, 193 | upgrader 194 | }) 195 | 196 | await listener1.listen(ma1) 197 | const conn = await wd.dial(ma1, { 198 | upgrader 199 | }) 200 | expect(conn.remoteAddr).to.exist() 201 | 202 | await conn.close() 203 | await listener1.close() 204 | }) 205 | }) 206 | } 207 | -------------------------------------------------------------------------------- /test/node.ts: -------------------------------------------------------------------------------- 1 | 2 | import listenTests from './listen.js' 3 | import complianceTests from './compliance.js' 4 | import dialTests from './dial.js' 5 | // @ts-expect-error no types 6 | import wrtc from 'wrtc' 7 | import { webRTCDirect } from '../src/index.js' 8 | import type { Transport } from '@libp2p/interface-transport' 9 | 10 | // TODO: Temporary fix per wrtc issue 11 | // https://github.com/node-webrtc/node-webrtc/issues/636#issuecomment-774171409 12 | process.on('beforeExit', (code) => process.exit(code)) 13 | 14 | describe('transport: with wrtc', () => { 15 | const create = async (): Promise => { 16 | const ws = webRTCDirect({ 17 | wrtc 18 | })() 19 | 20 | return ws 21 | } 22 | 23 | dialTests(create) 24 | listenTests(create) 25 | complianceTests(create) 26 | }) 27 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "aegir/src/config/tsconfig.aegir.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "emitDeclarationOnly": false, 6 | "module": "ES2020" 7 | }, 8 | "include": [ 9 | "src", 10 | "test" 11 | ] 12 | } 13 | --------------------------------------------------------------------------------