├── .github └── workflows │ ├── build.yml │ ├── codeql-analysis.yml │ └── mattermost-ziti-webhook.yml ├── .gitignore ├── .gitmodules ├── .npmignore ├── API_REFERENCE.hbs ├── API_REFERENCE.md ├── CMakeLists.txt ├── CMakePresets.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.hbs ├── README.md ├── SECURITY.md ├── binding.gyp ├── cmake └── variables.cmake ├── configure ├── configure.cmd ├── deps ├── CMakeLists.txt ├── common-ziti.gypi └── tlsuv-libuv.patch ├── jsdoc.json ├── lib ├── close.js ├── dial.js ├── enroll.js ├── express-listener.js ├── express.js ├── httpRequest.js ├── httpRequestData.js ├── httpRequestEnd.js ├── index.js ├── init.js ├── listen.js ├── serviceAvailable.js ├── servicesRefresh.js ├── setLogLevel.js ├── write.js ├── ziti-socket.js └── ziti.js ├── package-lock.json ├── package.json ├── scripts ├── build-appveyor.bat ├── install_node.sh └── validate_tag.sh ├── src ├── Ziti_https_request.c ├── Ziti_https_request_data.c ├── Ziti_https_request_end.c ├── build_config.h.in ├── stack_traces.c ├── utils.c ├── utils.h ├── ziti-add-on.c ├── ziti-nodejs.h ├── ziti_close.c ├── ziti_dial.c ├── ziti_enroll.c ├── ziti_init.c ├── ziti_listen.c ├── ziti_sdk_version.c ├── ziti_service_available.c ├── ziti_services_refresh.c ├── ziti_set_log_level.c ├── ziti_shutdown.c ├── ziti_websocket_connect.c ├── ziti_websocket_write.c └── ziti_write.c ├── tests ├── enroll-test.js ├── hello.js ├── https-test.js ├── mattermost-test.js └── websocket-test.js ├── toolchains ├── Linux-arm.cmake ├── Linux-arm64.cmake ├── Windows-arm64-msvc.cmake ├── Windows-x86.cmake ├── Windows-x86_64.cmake ├── linux-embedded.cmake ├── macOS-arm64.cmake └── macOS-x86_64.cmake ├── vcpkg.json ├── ziti.js └── ziti.png /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Build 3 | 4 | on: 5 | release: 6 | types: [ published ] 7 | 8 | push: 9 | branches: [ main ] 10 | paths-ignore: 11 | - 'package.json' 12 | - 'CHANGELOG.md' 13 | pull_request: 14 | branches: [ main ] 15 | workflow_dispatch: 16 | inputs: 17 | tags: 18 | required: false 19 | description: 'Misc tags' 20 | 21 | # cancel older, redundant runs of same workflow on same branch 22 | concurrency: 23 | group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }} 24 | cancel-in-progress: true 25 | 26 | jobs: 27 | 28 | # ------------------------------------------------------------------------------- 29 | # Do a clean build, test, and publish 30 | # ------------------------------------------------------------------------------- 31 | build: 32 | name: Build for Node-${{ matrix.node_ver }} ${{ matrix.config.target }}/${{ matrix.config.arch }} 33 | runs-on: ${{ matrix.config.os }} 34 | # container: ${{ matrix.config.container }} 35 | 36 | env: 37 | BUILD_NUMBER: ${{ github.run_number }} 38 | AWS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 39 | 40 | strategy: 41 | matrix: 42 | config: 43 | - { os: windows-latest, target: "windows", arch: "x64" } 44 | - { os: ubuntu-20.04, target: "linux", arch: "x64" } 45 | - { os: ubuntu-20.04, target: "linux", arch: "arm64" } 46 | - { os: macos-13, target: "macos", arch: "x64" } 47 | - { os: macos-14, target: "macos", arch: "arm64" } 48 | node_ver: [ 18, 20, 21, 22, 23 ] 49 | fail-fast: false 50 | 51 | steps: 52 | 53 | - name: Checkout 54 | uses: actions/checkout@v4 55 | with: 56 | fetch-depth: 0 57 | submodules: 'recursive' 58 | # token: ${{ secrets.ZITI_CI_GH_TOKEN }} 59 | 60 | - name: macOS tools 61 | if: runner.os == 'macOS' 62 | shell: bash 63 | run: brew install autoconf autoconf-archive automake pkg-config libtool 64 | 65 | - name: Setup PkgConfig (Windows) 66 | if: matrix.config.target == 'windows' 67 | run: | 68 | choco install pkgconfiglite 69 | 70 | - name: Get crossbuild tools 71 | if: matrix.config.target == 'linux' && matrix.config.arch != 'x64' 72 | run: | 73 | sudo apt update -y 74 | sudo apt install -y crossbuild-essential-${{ matrix.config.arch }} 75 | 76 | - name: Node Version 77 | uses: actions/setup-node@v3 78 | with: 79 | node-version: ${{ matrix.node_ver }} 80 | 81 | - name: Extract branch name 82 | shell: bash 83 | run: echo "branch=${GITHUB_REF#refs/heads/}" >> $GITHUB_OUTPUT 84 | id: extract_branch 85 | 86 | - name: Print branch name 87 | shell: bash 88 | run: | 89 | echo "## branch name is: ${{ steps.extract_branch.outputs.branch }}" 90 | 91 | - name: Get current date 92 | id: date 93 | run: echo "date=$(date)" >> $GITHUB_OUTPUT 94 | 95 | - name: Install CMake/Ninja 96 | uses: lukka/get-cmake@v3.30.1 97 | 98 | - name: Run VCPKG 99 | uses: lukka/run-vcpkg@v11 100 | # will use baseline from vcpkg.json 101 | with: 102 | # get baseline from vcpkg 103 | vcpkgJsonGlob: './vcpkg.json' 104 | 105 | - name: show versions 106 | run: | 107 | echo ===== gcc ===== 108 | gcc --version 109 | echo ===== cmake ===== 110 | cmake --version 111 | echo "ninja: $(ninja --version)" 112 | echo "node: $(node --version)" 113 | echo "npm: $(npm --version)" 114 | 115 | - name: restore dependencies cache 116 | uses: actions/cache/restore@v4 117 | id: cache-restore 118 | with: 119 | key: deps-${{ matrix.config.target }}-${{ matrix.config.arch }}-${{ hashFiles('./vcpkg.json') }} 120 | path: './vcpkg/packages' 121 | 122 | - name: Build NodeJS-SDK 123 | id: buildSDK 124 | run: | 125 | cd ${{ runner.workspace }}/${{ github.event.repository.name }} 126 | npm install 127 | npm run build:package -- --target_arch=${{ matrix.config.arch }} 128 | env: 129 | PRESET: ci-${{ matrix.config.target }}-${{ matrix.config.arch }} 130 | TARGET_ARCH: ${{ matrix.config.arch }} 131 | BUILD_DATE: ${{ steps.date.outputs.date }} 132 | 133 | - name: show build result 134 | if: always() 135 | run: echo build result ${{ steps.buildSDK.outcome }} 136 | 137 | - name: debug build failure 138 | if: always() && steps.buildSDK.outcome == 'failure' 139 | uses: actions/upload-artifact@v4 140 | with: 141 | name: build-logs-${{ matrix.config.target }}-${{ matrix.config.arch }} 142 | path: | 143 | ./vcpkg/buildtrees/**/*.log 144 | ./build/**/*log 145 | 146 | - name: Hello test 147 | if: matrix.config.arch == 'x64' 148 | run: | 149 | node tests/hello.js 150 | 151 | - name: save dependencies cache 152 | uses: actions/cache/save@v4 153 | id: cache-save 154 | # save deps even build has failed 155 | if: always() 156 | with: 157 | key: ${{ steps.cache-restore.outputs.cache-primary-key }} 158 | path: './vcpkg/packages' 159 | 160 | - name: upload artifacts 161 | uses: actions/upload-artifact@v4 162 | with: 163 | name: ziti_nodejs_${{ matrix.config.target }}_${{ matrix.config.arch }}_nodev${{ matrix.node_ver }} 164 | path: | 165 | ./build/Release/ziti_sdk_nodejs.node 166 | if-no-files-found: error 167 | 168 | - name: upload release bundle 169 | uses: softprops/action-gh-release@v0.1.13 170 | if: startsWith(github.ref, 'refs/tags/') 171 | with: 172 | files: | 173 | ./build/stage/**/ziti_sdk_nodejs*.tar.gz 174 | 175 | - name: Configure AWS Credentials 176 | uses: aws-actions/configure-aws-credentials@v4 177 | if: env.AWS_KEY_ID != '' && startsWith(github.ref, 'refs/tags/') 178 | with: 179 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 180 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 181 | aws-region: us-east-1 182 | 183 | - name: Publish Binary to S3 (for all OS's, Node versions, and architectures) 184 | run: | 185 | ./node_modules/.bin/node-pre-gyp --target_arch=${{ matrix.config.arch }} unpublish publish 186 | sleep 5 187 | if: startsWith(github.ref, 'refs/tags/') 188 | 189 | - name: Install Binary 190 | run: | 191 | npm install --fallback-to-build=false 192 | sleep 5 193 | if: startsWith(github.ref, 'refs/tags/') 194 | 195 | - name: Fetch Binary info 196 | if: env.AWS_KEY_ID != '' && startsWith(github.ref, 'refs/tags/') 197 | run: | 198 | ./node_modules/.bin/node-pre-gyp info 199 | 200 | - name: NPM Publish 201 | uses: JS-DevTools/npm-publish@v1 202 | with: 203 | token: ${{ secrets.NPM_TOKEN }} 204 | access: public 205 | if: | 206 | matrix.config.os == 'ubuntu-20.04' && matrix.node_ver == '20' && matrix.config.arch == 'x64' && startsWith(github.ref, 'refs/tags/') 207 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | name: "CodeQL" 7 | 8 | on: 9 | push: 10 | branches: [master] 11 | pull_request: 12 | # The branches below must be a subset of the branches above 13 | branches: [master] 14 | schedule: 15 | - cron: '0 12 * * 5' 16 | 17 | jobs: 18 | analyze: 19 | name: Analyze 20 | runs-on: ubuntu-latest 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | # Override automatic language detection by changing the below list 26 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] 27 | language: ['cpp', 'javascript', 'python'] 28 | # Learn more... 29 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection 30 | 31 | steps: 32 | - name: Checkout repository 33 | uses: actions/checkout@v4 34 | with: 35 | # We must fetch at least the immediate parents so that if this is 36 | # a pull request then we can checkout the head. 37 | fetch-depth: 2 38 | 39 | # If this run was triggered by a pull request event, then checkout 40 | # the head of the pull request instead of the merge commit. 41 | - run: git checkout HEAD^2 42 | if: ${{ github.event_name == 'pull_request' }} 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v2 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v2 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v2 72 | -------------------------------------------------------------------------------- /.github/workflows/mattermost-ziti-webhook.yml: -------------------------------------------------------------------------------- 1 | name: mattermost-ziti-webhook 2 | on: 3 | create: 4 | delete: 5 | issues: 6 | issue_comment: 7 | pull_request_review: 8 | pull_request_review_comment: 9 | pull_request: 10 | push: 11 | fork: 12 | release: 13 | 14 | jobs: 15 | mattermost-ziti-webhook: 16 | runs-on: ubuntu-latest 17 | name: POST Webhook 18 | env: 19 | ZITI_LOG: 99 20 | ZITI_NODEJS_LOG: 99 21 | steps: 22 | - uses: openziti/ziti-webhook-action@main 23 | with: 24 | ziti-id: ${{ secrets.ZITI_MATTERMOST_IDENTITY }} 25 | webhook-url: ${{ secrets.ZITI_MATTERMOST_WEBHOOK_URL }} 26 | webhook-secret: ${{ secrets.ZITI_MATTERMOSTI_WEBHOOK_SECRET }} 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/ 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | .vscode/ 107 | .DS_Store 108 | /CMakeUserPresets.json 109 | 110 | # Jetbrains tools 111 | .idea/ 112 | cmake-build-* 113 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openziti/ziti-sdk-nodejs/53f2a339ffb16e8a11939618877e4590b982a8f7/.gitmodules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/ 39 | deps/ 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # TypeScript v1 declaration files 46 | typings/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Microbundle cache 58 | .rpt2_cache/ 59 | .rts2_cache_cjs/ 60 | .rts2_cache_es/ 61 | .rts2_cache_umd/ 62 | 63 | # Optional REPL history 64 | .node_repl_history 65 | 66 | # Output of 'npm pack' 67 | *.tgz 68 | 69 | # Yarn Integrity file 70 | .yarn-integrity 71 | 72 | # dotenv environment variables file 73 | .env 74 | .env.test 75 | 76 | # parcel-bundler cache (https://parceljs.org/) 77 | .cache 78 | 79 | # Next.js build output 80 | .next 81 | 82 | # Nuxt.js build / generate output 83 | .nuxt 84 | dist 85 | 86 | # Gatsby files 87 | .cache/ 88 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 89 | # https://nextjs.org/blog/next-9-1#public-directory-support 90 | # public 91 | 92 | # vuepress build output 93 | .vuepress/dist 94 | 95 | # Serverless directories 96 | .serverless/ 97 | 98 | # FuseBox cache 99 | .fusebox/ 100 | 101 | # DynamoDB Local files 102 | .dynamodb/ 103 | 104 | # TernJS port file 105 | .tern-port 106 | 107 | .vscode/ 108 | .DS_Store 109 | -------------------------------------------------------------------------------- /API_REFERENCE.hbs: -------------------------------------------------------------------------------- 1 | 2 | # API Reference 3 | {{#module name="@openziti/ziti-sdk-nodejs"}} 4 | {{>body~}} 5 | {{>member-index~}} 6 | {{>separator~}} 7 | {{>members~}} 8 | {{/module}} 9 | 10 | * * * 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19) 2 | cmake_policy(SET CMP0091 NEW) 3 | cmake_policy(SET CMP0042 NEW) 4 | 5 | include(cmake/variables.cmake) 6 | 7 | message(NOTICE "branch: ${ZITI_BRANCH}/${ZITI_COMMIT}") 8 | 9 | project (ziti_sdk_nodejs) 10 | 11 | configure_file( 12 | ${CMAKE_CURRENT_SOURCE_DIR}/src/build_config.h.in 13 | ${CMAKE_CURRENT_BINARY_DIR}/include/build_config.h 14 | ) 15 | 16 | # add_definitions(-DNAPI_VERSION=4) 17 | 18 | if (WIN32) 19 | # make sure TLSUV picks it up 20 | include_directories(${CMAKE_JS_INC}) 21 | endif (WIN32) 22 | 23 | file(GLOB SOURCE_FILES ./src/*.c ./src/.cpp) 24 | 25 | add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC}) 26 | set_target_properties(${PROJECT_NAME} PROPERTIES 27 | PREFIX "" 28 | SUFFIX ".node" 29 | C_STANDARD 11 30 | POSITION_INDEPENDENT_CODE ON 31 | ) 32 | 33 | target_include_directories(${PROJECT_NAME} 34 | PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include 35 | PRIVATE ${CMAKE_JS_INC} 36 | ) 37 | 38 | if (WIN32) 39 | target_compile_definitions(${PROJECT_NAME} PRIVATE WIN32_LEAN_AND_MEAN) 40 | endif (WIN32) 41 | 42 | 43 | if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET) 44 | # Generate node.lib 45 | execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS}) 46 | endif() 47 | 48 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/deps) 49 | 50 | target_link_libraries(${PROJECT_NAME} PRIVATE ziti ${CMAKE_JS_LIB}) 51 | if (WIN32) 52 | target_link_libraries(${PROJECT_NAME} PRIVATE dbghelp version ntdll) 53 | endif (WIN32) -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 23 6 | }, 7 | "configurePresets": [ 8 | { 9 | "name": "base-win64", 10 | "hidden": false, 11 | "inherits": [ "base" ], 12 | "condition": { 13 | "type": "equals", 14 | "lhs": "${hostSystemName}", 15 | "rhs": "Windows" 16 | }, 17 | "cacheVariables": { 18 | "VCPKG_TARGET_TRIPLET": "x64-windows-static" 19 | } 20 | }, 21 | { 22 | "name": "base", 23 | "hidden": false, 24 | "generator": "Ninja", 25 | "cacheVariables": { 26 | "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", 27 | "CMAKE_C_EXTENSIONS": "ON", 28 | "CMAKE_C_STANDARD": "99", 29 | "CMAKE_C_STANDARD_REQUIRED": "ON", 30 | "CMAKE_CXX_EXTENSIONS": "OFF", 31 | "CMAKE_CXX_STANDARD": "11", 32 | "CMAKE_CXX_STANDARD_REQUIRED": "ON", 33 | "CMAKE_BUILD_TYPE": "Debug" 34 | } 35 | }, 36 | { 37 | "name": "ci", 38 | "hidden": true, 39 | "binaryDir": "${sourceDir}/build", 40 | "inherits": [ 41 | "base" 42 | ], 43 | "cacheVariables": { 44 | "CMAKE_BUILD_TYPE": "Release" 45 | } 46 | }, 47 | { 48 | "name": "ci-macos-x64", 49 | "inherits": [ "ci" ] 50 | }, 51 | { 52 | "name": "ci-macos-arm64", 53 | "inherits": [ "ci" ], 54 | "cacheVariables": { 55 | "VCPKG_TARGET_ARCHITECTURE": "arm64", 56 | "VCPKG_TARGET_TRIPLET": "arm64-osx", 57 | "VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "${sourceDir}/toolchains/macOS-arm64.cmake" 58 | } 59 | }, 60 | { 61 | "name": "ci-linux-x64", 62 | "inherits": [ "ci" ] 63 | }, 64 | { 65 | "name": "ci-linux-arm64", 66 | "inherits": "ci-linux-x64", 67 | "cacheVariables": { 68 | "VCPKG_TARGET_TRIPLET": "arm64-linux", 69 | "VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "${sourceDir}/toolchains/Linux-arm64.cmake" 70 | } 71 | }, 72 | { 73 | "name": "ci-linux-arm", 74 | "inherits": "ci-linux-x64", 75 | "cacheVariables": { 76 | "VCPKG_TARGET_TRIPLET": "arm-linux", 77 | "VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "${sourceDir}/toolchains/Linux-arm.cmake" 78 | } 79 | }, 80 | { 81 | "name": "ci-windows-x64", 82 | "inherits": [ "ci", "base-win64"] 83 | }, 84 | { 85 | "name": "ci-windows-arm64", 86 | "inherits": [ "ci-windows-x64" ], 87 | "cacheVariables": { 88 | "VCPKG_TARGET_TRIPLET": "arm64-windows-static-md" 89 | } 90 | } 91 | ] 92 | } 93 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | All open source projects managed by OpenZiti share a common [code of conduct](https://docs.openziti.io/policies/CODE_OF_CONDUCT.html) 4 | which all contributors are expected to follow. Please be sure you read, understand and adhere to the guidelines expressed therein. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | NetFoundry welcomes all and any contributions. All open source projects managed by NetFoundry share a common 4 | [guide for contributions](https://docs.openziti.io/policies/CONTRIBUTING.html). 5 | 6 | If you are eager to contribute to a NetFoundry-managed open source project please read and act accordingly. 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright NetFoundry Inc. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.hbs: -------------------------------------------------------------------------------- 1 |

2 | OpenZiti is a free and open source project focused on bringing zero trust to any application. 3 |
4 | The project provides all the pieces required to implement or integrate zero trust into your solutions. 5 |
6 |
7 | Please star us. 8 |
9 | 10 |
11 |
12 |

13 | 14 |

15 | 16 |

17 | 18 |

19 | 20 | @openziti/ziti-sdk-nodejs 21 |
22 |
23 | 24 | This repo hosts the OpenZiti SDK for NodeJS, and is designed to help you deliver secure applications over a OpenZiti Network 25 |
26 |
27 | Part of the OpenZiti ecosystem 28 |

29 | 30 |

31 |
32 | Are you interested in knowing how to easily embed programmable, high performance, zero trust networking into your NodeJS app, on any internet connection, without VPNs? 33 |
34 | Learn more about our OpenZiti project.
35 |
36 |

37 | 38 | --- 39 | [![Build Status](https://github.com/openziti/ziti-sdk-nodejs/workflows/Build/badge.svg?branch=main)]() 40 | [![Issues](https://img.shields.io/github/issues-raw/openziti/ziti-sdk-nodejs)]() 41 | [![npm version](https://badge.fury.io/js/@openziti%2Fziti-sdk-nodejs.svg)](https://badge.fury.io/js/@openziti%2Fziti-sdk-nodejs.svg) 42 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 43 | [![LOC](https://img.shields.io/tokei/lines/github/openziti/ziti-sdk-nodejs)]() 44 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=rounded)](CONTRIBUTING.md) 45 | [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md) 46 | 47 | --- 48 | 49 | # Associated Article(s) 50 | For more context on this SDK, you may be interested in this 51 | [article concerning how to secure NodeJS applications](https://openziti.io/securing-nodejs-applications) 52 | 53 | 54 | 55 | # Supported platforms 56 | 57 | The `@openziti/ziti-sdk-nodejs` module works with the following Node.js versions: 58 | - v12.x 59 | - v13.x 60 | - v14.x 61 | - v15.x 62 | - v16.x 63 | - v17.x 64 | - v18.x 65 | 66 | Binaries for most Node versions and platforms are provided by default via [@mapbox/node-pre-gyp](https://github.com/mapbox/node-pre-gyp). 67 | 68 | # Installing 69 | 70 | NPM 71 | ``` js 72 | npm i @openziti/ziti-sdk-nodejs 73 | ``` 74 | or Yarn 75 | ``` js 76 | yarn add @openziti/ziti-sdk-nodejs 77 | ``` 78 | 79 | Special note on previous package: 80 | 81 | On June 7, 2020 @openziti/ziti-sdk-nodejs@0.6.0 was released. Older, unscoped versions that are not part of the @openziti org are deprecated and only @openziti/ziti-sdk-nodejs will see updates going forward. To upgrade to the new package do: 82 | 83 | ``` js 84 | npm uninstall ziti-sdk-nodejs --save 85 | npm install @openziti/ziti-sdk-nodejs --save 86 | ``` 87 | 88 | # Usage 89 | 90 | **Note:** the module must be [installed](#installing) before use. 91 | 92 | ESM example (client-side) 93 | ``` js 94 | import ziti from '@openziti/ziti-sdk-nodejs'; 95 | 96 | // Somehow provide path to identity file, e.g. via env var 97 | const zitiIdentityFile = process.env.ZITI_IDENTITY_FILE; 98 | // Authenticate ourselves onto the Ziti network 99 | await ziti.init( zitiIdentityFile ).catch(( err ) => { /* probably exit */ }); 100 | 101 | const on_resp_data = ( obj ) => { 102 | console.log(`response is: ${obj.body.toString('utf8')}`); 103 | }; 104 | 105 | // Perform an HTTP GET request to a dark OpenZiti web service 106 | ziti.httpRequest( 107 | 'myDarkWebService', // OpenZiti Service name or HTTP origin part of the URL 108 | 'GET', 109 | '/', // path part of the URL including query params 110 | ['Accept: application/json' ], // headers 111 | undefined, // optional on_req cb 112 | undefined, // optional on_req_data cb 113 | on_resp_data // optional on_resp_data cb 114 | ); 115 | 116 | ``` 117 | 118 | ESM example (server-side ExpressJS) 119 | ``` js 120 | import ziti from '@openziti/ziti-sdk-nodejs'; 121 | import express from 'express'; 122 | let app = ziti.express( express, zitiServiceName ); 123 | app.listen(ignored, function() { ... } 124 | 125 | /** 126 | 127 | That's right. 128 | 129 | With only a single-line code change (the ziti.express call), your web server is now capable 130 | of being invisible to malicious attackers on the internet, and only accessible to your 131 | trusted remote users. 132 | 133 | Nothing else in your existing ExpressJS web server code needs to change! 134 | 135 | Existing routing, middleware, etc., all operates the same as it always did... 136 | but now you enjoy the comfort of knowing that if a connection comes in, it is from 137 | a trusted identity on the client side. 138 | 139 | No malicious actors can see your dark web server, and thus, no malicious actors can attack it. 140 | 141 | */ 142 | ``` 143 | 144 | CJS example (client-side) 145 | ``` js 146 | var ziti = require('@openziti/ziti-sdk-nodejs'); 147 | 148 | const ziti_init = async (identity) => { 149 | return new Promise((resolve) => { 150 | ziti.ziti_init(identity, () => { 151 | resolve(); 152 | }); 153 | }); 154 | }; 155 | 156 | const ziti_service_available = (service) => { 157 | return new Promise((resolve) => { 158 | ziti.ziti_service_available(service, (status) => { 159 | resolve(status); 160 | }); 161 | }); 162 | }; 163 | 164 | function ziti_dial(service) { 165 | return new Promise((resolve, reject) => { 166 | ziti.ziti_dial( 167 | service, 168 | (conn) => { 169 | resolve(conn); 170 | }, 171 | (data) => { 172 | // Do something with data... 173 | }, 174 | ); 175 | }); 176 | } 177 | 178 | const ziti_write = (conn, data) => { 179 | return new Promise((resolve) => { 180 | ziti.ziti_write(conn, data, () => { 181 | resolve(); 182 | }); 183 | }); 184 | }; 185 | 186 | (async () => { 187 | 188 | await ziti_init(LOCATION_OF_IDENTITY_FILE); 189 | 190 | let status = await ziti_service_available(YOUR_SERVICE_NAME); 191 | 192 | if (status === 0) { 193 | 194 | const conn = await ziti_dial(YOUR_SERVICE_NAME); 195 | 196 | let data = SOME_KIND_OF_DATA; 197 | 198 | let buffer = Buffer.from(data); 199 | 200 | await ziti_write(conn, buffer); 201 | 202 | ...etc 203 | } 204 | 205 | })(); 206 | ``` 207 | 208 | # API Reference 209 | {{#module name="@openziti/ziti-sdk-nodejs"}} 210 | {{>body~}} 211 | {{>member-index~}} 212 | {{>separator~}} 213 | {{>members~}} 214 | {{/module}} 215 | 216 | * * * 217 | 218 | Getting Help 219 | ------------ 220 | Please use these community resources for getting help. We use GitHub [issues](https://github.com/openziti/ziti-sdk-nodejs/issues) 221 | for tracking bugs and feature requests and have limited bandwidth to address them. 222 | 223 | - Read the [docs](https://openziti.github.io/ziti/overview.html) 224 | - Participate in discussion on [Discourse](https://openziti.discourse.group/) 225 | 226 | 227 | Copyright© NetFoundry, Inc. 228 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Ziggy using the ziti-sdk-nodejs](https://raw.githubusercontent.com/openziti/branding/main/images/banners/Node.jpg) 2 | 3 |

4 | OpenZiti is a free and open source project focused on bringing zero trust to any application. 5 |
6 | The project provides all the pieces required to implement or integrate zero trust into your solutions. 7 |
8 |
9 | Please star us. 10 |
11 | 12 |
13 |
14 |

15 | 16 |

17 | 18 |

19 | 20 |

21 | 22 | @openziti/ziti-sdk-nodejs 23 |
24 |
25 | 26 | This repo hosts the OpenZiti SDK for NodeJS, and is designed to help you deliver secure applications over a OpenZiti Network 27 |
28 |
29 | Part of the OpenZiti ecosystem 30 |

31 | 32 |

33 |
34 | Are you interested in knowing how to easily embed programmable, high performance, zero trust networking into your NodeJS app, on any internet connection, without VPNs? 35 |
36 | Learn more about our OpenZiti project.
37 |
38 |

39 | 40 | --- 41 | [![Build Status](https://github.com/openziti/ziti-sdk-nodejs/workflows/Build/badge.svg?branch=main)]() 42 | [![Issues](https://img.shields.io/github/issues-raw/openziti/ziti-sdk-nodejs)]() 43 | [![npm version](https://badge.fury.io/js/@openziti%2Fziti-sdk-nodejs.svg)](https://badge.fury.io/js/@openziti%2Fziti-sdk-nodejs.svg) 44 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 45 | [![LOC](https://img.shields.io/tokei/lines/github/openziti/ziti-sdk-nodejs)]() 46 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=rounded)](CONTRIBUTING.md) 47 | [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md) 48 | 49 | --- 50 | 51 | # Associated Article(s) 52 | For more context on this SDK, you may be interested in this 53 | [article concerning how to secure NodeJS applications](https://blog.openziti.io/securing-nodejs-applications) 54 | 55 | 56 | 57 | # Supported platforms 58 | 59 | The `@openziti/ziti-sdk-nodejs` module works with the following Node.js versions: 60 | - v16.x 61 | - v18.x 62 | - v19.x 63 | - v20.x 64 | - v21.x 65 | - v22.x 66 | - v23.x 67 | 68 | The `@openziti/ziti-sdk-nodejs` module works with the following architectures: 69 | - amd64 70 | - arm64 71 | 72 | The `@openziti/ziti-sdk-nodejs` module works with the following Operating Systems: 73 | - macos 74 | - linux 75 | - windows 76 | 77 | 78 | # Installing 79 | 80 | NPM 81 | ``` js 82 | npm i @openziti/ziti-sdk-nodejs 83 | ``` 84 | or Yarn 85 | ``` js 86 | yarn add @openziti/ziti-sdk-nodejs 87 | ``` 88 | 89 | Special note on previous package: 90 | 91 | On June 7, 2020 @openziti/ziti-sdk-nodejs@0.6.0 was released. Older, unscoped versions that are not part of the @openziti org are deprecated and only @openziti/ziti-sdk-nodejs will see updates going forward. To upgrade to the new package do: 92 | 93 | ``` js 94 | npm uninstall ziti-sdk-nodejs --save 95 | npm install @openziti/ziti-sdk-nodejs --save 96 | ``` 97 | 98 | # Usage 99 | 100 | **Note:** the module must be [installed](#installing) before use. 101 | 102 | ESM example (client-side) 103 | ``` js 104 | import ziti from '@openziti/ziti-sdk-nodejs'; 105 | 106 | // Somehow provide path to identity file, e.g. via env var 107 | const zitiIdentityFile = process.env.ZITI_IDENTITY_FILE; 108 | // Authenticate ourselves onto the Ziti network 109 | await ziti.init( zitiIdentityFile ).catch(( err ) => { /* probably exit */ }); 110 | 111 | const on_resp_data = ( obj ) => { 112 | console.log(`response is: ${obj.body.toString('utf8')}`); 113 | }; 114 | 115 | // Perform an HTTP GET request to a dark OpenZiti web service 116 | ziti.httpRequest( 117 | 'myDarkWebService', // OpenZiti Service name or HTTP origin part of the URL 118 | undefined, // schemeHostPort parm is mutually-exclusive with serviceName parm 119 | 'GET', 120 | '/', // path part of the URL including query params 121 | ['Accept: application/json' ], // headers 122 | undefined, // optional on_req cb 123 | undefined, // optional on_req_data cb 124 | on_resp_data // optional on_resp_data cb 125 | ); 126 | 127 | ``` 128 | 129 | ESM example (server-side ExpressJS) 130 | ``` js 131 | import ziti from '@openziti/ziti-sdk-nodejs'; 132 | import express from 'express'; 133 | let app = ziti.express( express, zitiServiceName ); 134 | app.listen(ignored, function() { ... } 135 | 136 | /** 137 | 138 | That's right. 139 | 140 | With only a single-line code change (the ziti.express call), your web server is now capable 141 | of being invisible to malicious attackers on the internet, and only accessible to your 142 | trusted remote users. 143 | 144 | Nothing else in your existing ExpressJS web server code needs to change! 145 | 146 | Existing routing, middleware, etc., all operates the same as it always did... 147 | but now you enjoy the comfort of knowing that if a connection comes in, it is from 148 | a trusted identity on the client side. 149 | 150 | No malicious actors can see your dark web server, and thus, no malicious actors can attack it. 151 | 152 | */ 153 | ``` 154 | 155 | CJS example (client-side) 156 | ``` js 157 | var ziti = require('@openziti/ziti-sdk-nodejs'); 158 | 159 | const ziti_init = async (identity) => { 160 | return new Promise((resolve) => { 161 | ziti.ziti_init(identity, () => { 162 | resolve(); 163 | }); 164 | }); 165 | }; 166 | 167 | const ziti_service_available = (service) => { 168 | return new Promise((resolve) => { 169 | ziti.ziti_service_available(service, (status) => { 170 | resolve(status); 171 | }); 172 | }); 173 | }; 174 | 175 | function ziti_dial(service) { 176 | return new Promise((resolve, reject) => { 177 | ziti.ziti_dial( 178 | service, 179 | (conn) => { 180 | resolve(conn); 181 | }, 182 | (data) => { 183 | // Do something with data... 184 | }, 185 | ); 186 | }); 187 | } 188 | 189 | const ziti_write = (conn, data) => { 190 | return new Promise((resolve) => { 191 | ziti.ziti_write(conn, data, () => { 192 | resolve(); 193 | }); 194 | }); 195 | }; 196 | 197 | (async () => { 198 | 199 | await ziti_init(LOCATION_OF_IDENTITY_FILE); 200 | 201 | let status = await ziti_service_available(YOUR_SERVICE_NAME); 202 | 203 | if (status === 0) { 204 | 205 | const conn = await ziti_dial(YOUR_SERVICE_NAME); 206 | 207 | let data = SOME_KIND_OF_DATA; 208 | 209 | let buffer = Buffer.from(data); 210 | 211 | await ziti_write(conn, buffer); 212 | 213 | ...etc 214 | } 215 | 216 | })(); 217 | ``` 218 | 219 | # API Reference 220 | For doc concerning API's contained in this SDK, you may be interested in this 221 | [SDK API Reference](API_REFERENCE.md) 222 | 223 | 224 | Getting Help 225 | ------------ 226 | Please use these community resources for getting help. We use GitHub [issues](https://github.com/openziti/ziti-sdk-nodejs/issues) 227 | for tracking bugs and feature requests and have limited bandwidth to address them. 228 | 229 | - Read the [docs](https://openziti.github.io/ziti/overview.html) 230 | - Participate in discussion on [Discourse](https://openziti.discourse.group/) 231 | 232 | 233 | # Building from source on MacOS 234 | 235 | ``` js 236 | git clone https://github.com/microsoft/vcpkg.git 237 | ./vcpkg/bootstrap-vcpkg.sh 238 | export VCPKG_ROOT=`pwd`/vcpkg 239 | brew install cmake 240 | brew install ninja 241 | brew install pkg-config 242 | git clone https://github.com/openziti/ziti-sdk-nodejs.git 243 | cd ziti-sdk-nodejs 244 | npm run build 245 | ``` 246 | 247 | Copyright© NetFoundry, Inc. 248 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | Please refer to the [openziti-security repository](https://github.com/openziti/openziti-security) for details of the security policies and processes for this repository. -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | # "includes": [ "deps/common-ziti.gypi" ], 3 | 4 | # The "cd" variable is passed in, and used, only during Windows builds 5 | 'variables': { 6 | 'cd%': '.', 7 | 8 | # node v0.6.x doesn't give us its build variables, 9 | # but on Unix it was only possible to use the system OpenSSL library, 10 | # so default the variable to "true", v0.8.x node and up will overwrite it. 11 | 'node_shared_openssl%': 'true', 12 | 13 | }, 14 | 15 | "targets": [ 16 | { 17 | 'defines': [ 18 | 'BUILD_DATE= { 24 | 25 | ziti.ziti_close( conn ); 26 | 27 | }; 28 | 29 | exports.close = close; 30 | -------------------------------------------------------------------------------- /lib/dial.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | 19 | /** 20 | * on_connect() 21 | * 22 | */ 23 | const on_connect = ( status ) => { 24 | 25 | }; 26 | 27 | /** 28 | * on_data() 29 | * 30 | */ 31 | const on_data = ( status ) => { 32 | 33 | }; 34 | 35 | 36 | /** 37 | * dial() 38 | * 39 | * @param {*} serviceName 40 | * @param {*} isWebSocket 41 | * @param {*} on_connect_cb callback 42 | * @param {*} on_data_cb callback 43 | */ 44 | const dial = ( serviceName, isWebSocket, on_connect_cb, on_data_cb ) => { 45 | 46 | let connect_cb; 47 | let data_cb; 48 | 49 | if (typeof on_connect_cb === 'undefined') { 50 | connect_cb = on_connect; 51 | } else { 52 | connect_cb = on_connect_cb; 53 | } 54 | 55 | if (typeof on_data_cb === 'undefined') { 56 | data_cb = on_data; 57 | } else { 58 | data_cb = on_data_cb; 59 | } 60 | 61 | ziti.ziti_dial(serviceName, isWebSocket, connect_cb, data_cb); 62 | 63 | }; 64 | 65 | 66 | exports.dial = dial; 67 | -------------------------------------------------------------------------------- /lib/enroll.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | 19 | /** 20 | * on_enroll() 21 | * 22 | */ 23 | const on_enroll = ( status ) => { 24 | 25 | }; 26 | 27 | 28 | /** 29 | * enroll() 30 | * 31 | * @param {*} jwt_path 32 | * @param {*} on_enroll_cb callback 33 | */ 34 | const enroll = ( jwt_path, on_enroll_cb ) => { 35 | 36 | let enroll_cb; 37 | 38 | if (typeof on_enroll_cb === 'undefined') { 39 | enroll_cb = on_enroll; 40 | } else { 41 | enroll_cb = on_enroll_cb; 42 | } 43 | 44 | ziti.ziti_enroll(jwt_path, enroll_cb); 45 | 46 | }; 47 | 48 | 49 | exports.enroll = enroll; 50 | -------------------------------------------------------------------------------- /lib/express-listener.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const zitiListen = require('./listen').listen; 18 | const EventEmitter = require('events'); 19 | const { ZitiSocket } = require('./ziti-socket'); 20 | 21 | 22 | const normalizedArgsSymbol = Symbol('normalizedArgs'); 23 | 24 | 25 | var _serversIndex = 1; 26 | var _servers = new Map(); 27 | 28 | /** 29 | * on_listen() 30 | * 31 | * @param {*} status 32 | */ 33 | Server.prototype.on_listen = ( status ) => { 34 | 35 | }; 36 | 37 | /** 38 | * on_listen_client() 39 | * 40 | * @param {*} obj 41 | */ 42 | Server.prototype.on_listen_client = ( obj ) => { 43 | 44 | }; 45 | 46 | /** 47 | * on_client_write() 48 | * 49 | * @param {*} obj 50 | */ 51 | Server.prototype.on_client_write = ( obj ) => { 52 | 53 | }; 54 | 55 | /** 56 | * on_listen_client_connect() 57 | * 58 | * @param {*} obj 59 | */ 60 | Server.prototype.on_listen_client_connect = ( obj ) => { 61 | 62 | let self = _servers.get(obj.js_arb_data); 63 | 64 | const socket = new ZitiSocket({ client: obj.client }); 65 | 66 | self._socket = socket; 67 | 68 | self.emit('connection', socket); 69 | }; 70 | 71 | /** 72 | * on_listen_client_data() 73 | * 74 | * @param {*} obj 75 | */ 76 | Server.prototype.on_listen_client_data = ( obj ) => { 77 | 78 | let self = _servers.get(obj.js_arb_data); 79 | let socket = self._socket; 80 | 81 | socket.captureData(obj.app_data); 82 | }; 83 | 84 | 85 | /** 86 | * 87 | * @param {*} args 88 | */ 89 | function normalizeArgs(args) { 90 | let arr; 91 | 92 | if (args.length === 0) { 93 | arr = [{}, null]; 94 | arr[normalizedArgsSymbol] = true; 95 | return arr; 96 | } 97 | 98 | const arg0 = args[0]; 99 | let options = {}; 100 | if (typeof arg0 === 'object' && arg0 !== null) { 101 | // (options[...][, cb]) 102 | options = arg0; 103 | } else { 104 | // ([port][, host][...][, cb]) 105 | options.port = arg0; 106 | if (args.length > 1 && typeof args[1] === 'string') { 107 | options.host = args[1]; 108 | } 109 | } 110 | 111 | const cb = args[args.length - 1]; 112 | if (typeof cb !== 'function') 113 | arr = [options, null]; 114 | else 115 | arr = [options, cb]; 116 | 117 | arr[normalizedArgsSymbol] = true; 118 | return arr; 119 | } 120 | 121 | function Server(serviceName, options, connectionListener) { 122 | 123 | if (!(this instanceof Server)) 124 | return new Server(options, connectionListener); 125 | 126 | EventEmitter.call(this); 127 | 128 | if (typeof options === 'function') { 129 | connectionListener = options; 130 | options = {}; 131 | this.on('connection', connectionListener); 132 | } else if (options == null || typeof options === 'object') { 133 | options = { ...options }; 134 | 135 | if (typeof connectionListener === 'function') { 136 | this.on('connection', connectionListener); 137 | } 138 | } else { 139 | throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); 140 | } 141 | if (typeof options.keepAliveInitialDelay !== 'undefined') { 142 | validateNumber( 143 | options.keepAliveInitialDelay, 'options.keepAliveInitialDelay' 144 | ); 145 | 146 | if (options.keepAliveInitialDelay < 0) { 147 | options.keepAliveInitialDelay = 0; 148 | } 149 | } 150 | 151 | this._serviceName = serviceName; 152 | 153 | this._connections = 0; 154 | 155 | // this[async_id_symbol] = -1; 156 | this._handle = null; 157 | this._usingWorkers = false; 158 | this._workers = []; 159 | this._unref = false; 160 | 161 | this.allowHalfOpen = options.allowHalfOpen || false; 162 | this.pauseOnConnect = !!options.pauseOnConnect; 163 | this.noDelay = Boolean(options.noDelay); 164 | this.keepAlive = Boolean(options.keepAlive); 165 | this.keepAliveInitialDelay = ~~(options.keepAliveInitialDelay / 1000); 166 | } 167 | Object.setPrototypeOf(Server.prototype, EventEmitter.prototype); 168 | Object.setPrototypeOf(Server, EventEmitter); 169 | 170 | 171 | Server.prototype.listen = function( serviceName, ...args ) { 172 | 173 | let normalized = normalizeArgs(args); 174 | normalized = normalizeArgs(normalized[0]); 175 | 176 | // let options = normalized[0]; // we currently ignore options (a.k.a. `port`) 177 | let cb = normalized[1]; 178 | if (cb === null) { cb = this.on_listen; } // Use our on_listen cb is necessary, else use cb from teh calling app 179 | 180 | let index = _serversIndex++; 181 | _servers.set(index, this); 182 | 183 | zitiListen( serviceName, index, cb, this.on_listen_client, this.on_listen_client_connect, this.on_listen_client_data ); 184 | }; 185 | 186 | Server.prototype.address = function() { 187 | if (this._handle && this._handle.getsockname) { 188 | const out = {}; 189 | const err = this._handle.getsockname(out); 190 | if (err) { 191 | throw errnoException(err, 'address'); 192 | } 193 | return out; 194 | } else if (this._pipeName) { 195 | return this._pipeName; 196 | } 197 | return null; 198 | }; 199 | 200 | Server.prototype.close = function(cb) { 201 | if (typeof cb === 'function') { 202 | if (!this._handle) { 203 | this.once('close', function close() { 204 | cb(new ERR_SERVER_NOT_RUNNING()); 205 | }); 206 | } else { 207 | this.once('close', cb); 208 | } 209 | } 210 | 211 | if (this._handle) { 212 | this._handle.close(); 213 | this._handle = null; 214 | } 215 | 216 | if (this._usingWorkers) { 217 | let left = this._workers.length; 218 | const onWorkerClose = () => { 219 | if (--left !== 0) return; 220 | 221 | this._connections = 0; 222 | this._emitCloseIfDrained(); 223 | }; 224 | 225 | // Increment connections to be sure that, even if all sockets will be closed 226 | // during polling of workers, `close` event will be emitted only once. 227 | this._connections++; 228 | 229 | // Poll workers 230 | for (let n = 0; n < this._workers.length; n++) 231 | this._workers[n].close(onWorkerClose); 232 | } else { 233 | this._emitCloseIfDrained(); 234 | } 235 | 236 | return this; 237 | }; 238 | 239 | Object.defineProperty(Server.prototype, 'listening', { 240 | __proto__: null, 241 | get: function() { 242 | return !!this._handle; 243 | }, 244 | configurable: true, 245 | enumerable: true 246 | }); 247 | 248 | 249 | module.exports = { 250 | Server, 251 | }; 252 | -------------------------------------------------------------------------------- /lib/express.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const expressListener = require('./express-listener'); 18 | const { Server } = require('_http_server'); // from NodeJS internals 19 | 20 | 21 | 22 | /** 23 | * express() 24 | * 25 | * @param {*} express 26 | * @param {*} serviceName 27 | */ 28 | const express = ( express, serviceName ) => { 29 | 30 | var wrappedExpressApp = express(); 31 | 32 | /** 33 | * Listen for connections. 34 | * 35 | * A node `http.Server` is returned, with this 36 | * application (which is a `Function`) as its 37 | * callback. 38 | * 39 | * @return {http.Server} 40 | * @public 41 | */ 42 | wrappedExpressApp.listen = function() { 43 | 44 | Object.setPrototypeOf(Server.prototype, expressListener.Server.prototype); 45 | Object.setPrototypeOf(Server, expressListener.Server); 46 | var server = new Server(this); 47 | 48 | expressListener.Server.call( server, serviceName, { } ); 49 | 50 | return server.listen(serviceName, arguments); 51 | 52 | }; 53 | 54 | return wrappedExpressApp; 55 | 56 | }; 57 | 58 | exports.express = express; 59 | -------------------------------------------------------------------------------- /lib/httpRequest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | /** 19 | * on_req() 20 | * 21 | */ 22 | const on_req = ( obj ) => { 23 | 24 | console.log('on_req entered: ', obj); 25 | 26 | }; 27 | 28 | /** 29 | * on_resp() 30 | * 31 | */ 32 | const on_resp = ( obj ) => { 33 | 34 | console.log('on_resp entered: ', obj); 35 | 36 | }; 37 | 38 | /** 39 | * on_resp_data() 40 | * 41 | */ 42 | const on_resp_data = ( obj ) => { 43 | 44 | console.log('on_resp_data entered: ', obj); 45 | console.log('as string: ', obj.body.toString('utf8')); 46 | 47 | }; 48 | 49 | const httpRequest = ( serviceName, schemeHostPort, method, path, headers, on_req_cb, on_resp_cb, on_resp_data_cb ) => { 50 | 51 | let _on_req_cb; 52 | let _on_resp_cb; 53 | let _on_resp_data_cb; 54 | 55 | if (typeof on_req_cb === 'undefined') { 56 | _on_req_cb = on_req; 57 | } else { 58 | _on_req_cb = on_req_cb; 59 | } 60 | 61 | if (typeof on_resp_cb === 'undefined') { 62 | _on_resp_cb = on_resp; 63 | } else { 64 | _on_resp_cb = on_req_cb; 65 | } 66 | 67 | if (typeof on_resp_data_cb === 'undefined') { 68 | _on_resp_data_cb = on_resp_data; 69 | } else { 70 | _on_resp_data_cb = on_resp_data_cb; 71 | } 72 | 73 | ziti.Ziti_http_request( serviceName, schemeHostPort, method, path, headers, _on_req_cb, _on_resp_cb, _on_resp_data_cb ); 74 | 75 | }; 76 | 77 | 78 | exports.httpRequest = httpRequest; 79 | 80 | -------------------------------------------------------------------------------- /lib/httpRequestData.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | /** 19 | * on_req_data() 20 | * 21 | */ 22 | const on_req_data = ( obj ) => { 23 | 24 | console.log('on_req_data entered: ', obj); 25 | console.log('as string: ', obj.body.toString('utf8')); 26 | 27 | }; 28 | 29 | const httpRequestData = ( req, buffer, on_req_data_cb ) => { 30 | 31 | console.log('httpRequestData entered: ', req, buffer); 32 | 33 | let _on_req_data_cb; 34 | 35 | if (typeof on_req_data_cb === 'undefined') { 36 | _on_req_data_cb = on_req_data; 37 | } else { 38 | _on_req_data_cb = on_req_data_cb; 39 | } 40 | 41 | ziti.Ziti_http_request_data( req, buffer, _on_req_data_cb ); 42 | }; 43 | 44 | 45 | exports.httpRequestData = httpRequestData; 46 | 47 | -------------------------------------------------------------------------------- /lib/httpRequestEnd.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | const httpRequestEnd = ( req ) => { 19 | ziti.Ziti_http_request_end( req ); 20 | }; 21 | 22 | exports.httpRequestEnd = httpRequestEnd; 23 | 24 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | module.exports = require('./ziti'); -------------------------------------------------------------------------------- /lib/init.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | const init = ( identityPath ) => { 19 | 20 | return new Promise((resolve, reject) => { 21 | 22 | let rc = ziti.ziti_init( identityPath, ( result ) => { 23 | 24 | return resolve( result ); 25 | 26 | }); 27 | 28 | if (rc < 0) { 29 | return reject(`ziti.init() failed with return code ${rc}`); 30 | } 31 | 32 | }); 33 | }; 34 | 35 | exports.init = init; 36 | -------------------------------------------------------------------------------- /lib/listen.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | /** 19 | * listen() 20 | * 21 | * @param {*} identityPath 22 | */ 23 | const listen = ( serviceName, js_arb_data, on_listen, on_listen_client, on_client_connect, on_client_data ) => { 24 | 25 | ziti.ziti_listen( serviceName, js_arb_data, on_listen, on_listen_client, on_client_connect, on_client_data ); 26 | 27 | }; 28 | 29 | exports.listen = listen; 30 | -------------------------------------------------------------------------------- /lib/serviceAvailable.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | /** 19 | * on_serviceAvailable() 20 | * 21 | */ 22 | const on_serviceAvailable = ( status ) => { 23 | 24 | }; 25 | 26 | 27 | /** 28 | * write() 29 | * 30 | * @param {*} service 31 | * @param {*} on_write callback 32 | */ 33 | const serviceAvailable = ( service, sa_cb ) => { 34 | 35 | let cb; 36 | 37 | if (typeof sa_cb === 'undefined') { 38 | cb = on_serviceAvailable; 39 | } else { 40 | cb = sa_cb; 41 | } 42 | 43 | ziti.ziti_service_available( service, cb ); 44 | 45 | }; 46 | 47 | exports.serviceAvailable = serviceAvailable; 48 | -------------------------------------------------------------------------------- /lib/servicesRefresh.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | /** 19 | * servicesRefresh() 20 | * 21 | */ 22 | const servicesRefresh = () => { 23 | 24 | ziti.ziti_services_refresh(); 25 | 26 | }; 27 | 28 | exports.servicesRefresh = servicesRefresh; 29 | -------------------------------------------------------------------------------- /lib/setLogLevel.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | /** 19 | * setLogLevel() 20 | * 21 | * @param {*} lvl 22 | */ 23 | const setLogLevel = ( lvl ) => { 24 | 25 | ziti.ziti_set_log_level( lvl ); 26 | 27 | }; 28 | 29 | exports.setLogLevel = setLogLevel; 30 | -------------------------------------------------------------------------------- /lib/write.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | /** 19 | * on_write() 20 | * 21 | */ 22 | const on_write = ( status ) => { 23 | 24 | }; 25 | 26 | 27 | /** 28 | * write() 29 | * 30 | * @param {*} conn 31 | * @param {*} buf 32 | * @param {*} on_write callback 33 | */ 34 | const write = ( conn, buf, on_write_cb ) => { 35 | 36 | let cb; 37 | 38 | if (typeof on_write_cb === 'undefined') { 39 | cb = on_write; 40 | } else { 41 | cb = on_write_cb; 42 | } 43 | 44 | ziti.ziti_write( conn, buf, cb ); 45 | 46 | }; 47 | 48 | exports.write = write; 49 | -------------------------------------------------------------------------------- /lib/ziti-socket.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const EventEmitter = require('events'); 18 | const stream = require('stream'); 19 | const zitiWrite = require('./write').write; 20 | 21 | 22 | 23 | class ZitiSocket extends EventEmitter { 24 | 25 | constructor(opts) { 26 | 27 | super(); 28 | 29 | if (typeof opts !== 'undefined') { 30 | if (typeof opts.client !== 'undefined') { 31 | this.client = opts.client; 32 | } 33 | } 34 | 35 | this._writableState = new stream.Writable.WritableState({}, this, true); 36 | 37 | /** 38 | * This stream is where we'll put any data returned from Ziti (see on_listen_client_data cb) 39 | */ 40 | let self = this; 41 | this.readableZitiStream = new ReadableStream({ 42 | start(controller) { 43 | self.readableZitiStreamController = controller; 44 | } 45 | }); 46 | } 47 | 48 | 49 | /** 50 | * 51 | */ 52 | captureData(data) { 53 | 54 | if ((typeof data !== 'undefined') && (data.byteLength > 0)) { 55 | 56 | this.readableZitiStreamController.enqueue(data); 57 | this.emit('data', data); 58 | 59 | } else { 60 | 61 | this.emit('close'); 62 | 63 | } 64 | } 65 | 66 | 67 | /** 68 | * Implements the writeable stream method `_write` by pushing the data onto the underlying Ziti connection. 69 | */ 70 | async write(chunk, encoding, cb) { 71 | 72 | let buffer; 73 | 74 | if (typeof chunk === 'string' || chunk instanceof String) { 75 | buffer = Buffer.from(chunk, 'utf8'); 76 | } else if (Buffer.isBuffer(chunk)) { 77 | buffer = chunk; 78 | } else if (chunk instanceof Uint8Array) { 79 | buffer = Buffer.from(chunk, 'utf8'); 80 | } else { 81 | throw new Error('chunk type of [' + typeof chunk + '] is not a supported type'); 82 | } 83 | 84 | if (buffer.length > 0) { 85 | zitiWrite(this.client, buffer); 86 | } 87 | if (cb) { 88 | cb(); 89 | } 90 | } 91 | 92 | /** 93 | * 94 | */ 95 | _read() { /* NOP */ } 96 | read() { /* NOP */ } 97 | destroy() { /* NOP */ } 98 | cork() { /* NOP */ } 99 | uncork() { /* NOP */ } 100 | pause() { /* NOP */ } 101 | resume() { /* NOP */ } 102 | destroy() { /* NOP */ } 103 | end(data, encoding, callback) { /* NOP */ } 104 | _final(cb) { cb(); } 105 | setTimeout() { /* NOP */ } 106 | setNoDelay() { /* NOP */ } 107 | unshift(head) { /* NOP */ } 108 | } 109 | 110 | Object.defineProperty(ZitiSocket.prototype, 'writable', { 111 | get() { 112 | return ( 113 | true 114 | ); 115 | } 116 | }); 117 | 118 | 119 | exports.ZitiSocket = ZitiSocket; 120 | -------------------------------------------------------------------------------- /lib/ziti.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /** 18 | * OpenZiti SDK for Node.js 19 | * 20 | * @module @openziti/ziti-sdk-nodejs 21 | * 22 | */ 23 | 24 | var binding; 25 | 26 | function importAll (r) { 27 | r.keys().forEach(key => { 28 | binding = r(key); // Load the addon 29 | }); 30 | } 31 | 32 | if (typeof require.context == 'function') { 33 | 34 | importAll( require.context("../build/", true, /\.node$/) ); 35 | 36 | } else { 37 | 38 | const binary = require('@mapbox/node-pre-gyp'); 39 | const path = require('path') 40 | const binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')), {debug: false}); 41 | 42 | binding = require(binding_path); 43 | 44 | } 45 | 46 | ziti = module.exports = exports = binding; 47 | 48 | 49 | 50 | /** 51 | * Attach the external, app-facing, API to the 'ziti' object 52 | */ 53 | 54 | /** 55 | * Close a Ziti connection. 56 | * @function close 57 | * @param {number} conn - A Ziti connection handle. 58 | * @returns {void} No return value. 59 | */ 60 | exports.close = require('./close').close; 61 | 62 | /** 63 | * Create a connection to Ziti Service. 64 | * @async 65 | * @function dial 66 | * @param {string} serviceName - The name of the Ziti Service to connect to 67 | * @param {boolean} isWebSocket - True or False indicator concerning whether this connection if bi-directional. 68 | * @param {onConnectCallback} onConnect - The callback that receives the connection handle. 69 | * @param {onDataCallback} onData - The callback that receives incoming data from the connection. 70 | * @returns {void} No return value. 71 | */ 72 | /** 73 | * This callback is part of the `dial` API. 74 | * @callback onDataCallback - The callback that receives the connection handle. 75 | * @param {number} conn - A Ziti connection handle. 76 | */ 77 | /** 78 | * This callback is part of the `dial` API. 79 | * @callback onConnectCallback - The callback that receives incoming data from the connection. 80 | * @param {Buffer} data - Incoming data from the Ziti connection. 81 | * @returns {void} No return value. 82 | */ 83 | exports.dial = require('./dial').dial; 84 | 85 | /** 86 | * Enroll a Ziti Identity. 87 | * @async 88 | * @function enroll 89 | * @param {string} jwt_path - The path to the JWT 90 | * @param {onEnrollCallback} onEnroll - The callback that receives the enrollment status. 91 | * @returns {void} No return value. 92 | */ 93 | /** 94 | * This callback is part of the `enroll` API. 95 | * @callback onEnrollCallback - The callback that receives the enrollment status. 96 | * @param {object} obj - enrollment status. 97 | */ 98 | exports.enroll = require('./enroll').enroll; 99 | 100 | /** 101 | * Wrap ExpressJS to facilitate hosting (listening) on a Ziti Service instead of a TCP port. 102 | * @function express 103 | * @param {*} express - The express() object. 104 | * @param {string} serviceName - The name of the Ziti Service being served (hosted). 105 | * @returns {*} The wrapped express() object. 106 | */ 107 | exports.express = require('./express').express; 108 | 109 | /** 110 | * Initiate an HTTP request to a Ziti Service. 111 | * @function httpRequest 112 | * @param {string} serviceName - The name of the Ziti Service to send the request. (mutually exclusive with url) 113 | * @param {string} schemeHostPort - The scheme/host/port (e.g. http://myserver.ziti:8080) of a Ziti service-config/intercept to send the request. (mutually exclusive with serviceName) 114 | * @param {string} method - The REST verb to use (e.g. `GET`, `POST`). 115 | * @param {string} path - The URL PATH to use on the request (can include HTTP query parms). 116 | * @param {string[]} headers - The HTTP Headers to use on the request. 117 | * @param {onRequestCallback} onRequest - The callback that receives the request handle. 118 | * @param {onResonseCallback} onResponse - The callback that receives the HTTP Response. 119 | * @param {onResonseDataCallback} onResponseData - The callback that receives the HTTP Response data. 120 | * @returns {void} No return value. 121 | */ 122 | /** 123 | * This callback is part of the `httpRequest` API. 124 | * @callback onRequestCallback - The callback that receives the request handle. 125 | * @param {number} req - A Ziti HttpRequest handle. 126 | * @returns {void} No return value. 127 | */ 128 | /** 129 | * This callback is part of the `httpRequest` API. 130 | * @callback onResonseCallback - The callback that receives response from the request. 131 | * @param resp - Incoming response from the HTTP request. 132 | * @param resp.req - The request handle. 133 | * @param resp.code - The HTTP status code. 134 | * @param resp.headers - The HTTP Headers on the response. 135 | * @returns {void} No return value. 136 | */ 137 | /** 138 | * This callback is part of the `httpRequest` API. 139 | * @callback onResonseDataCallback - The callback that receives incoming data from the request. 140 | * @param respData - Incoming response data from the HTTP request. 141 | * @param respData.req - The request handle. 142 | * @param respData.len - The length of the response body. 143 | * @param respData.body - The response body. 144 | * @returns {void} No return value. 145 | */ 146 | exports.httpRequest = require('./httpRequest').httpRequest; 147 | 148 | /** 149 | * Send payload data for HTTP POST request to a Ziti Service. 150 | * @function httpRequestData 151 | * @param {number} req - A Ziti HttpRequest handle. 152 | * @param {Buffer} data - The HTTP payload data to send. 153 | * @param {onRequestDataCallback} onRequestData - The callback that acknowleges the send. 154 | * @returns {void} No return value. 155 | */ 156 | /** 157 | * This callback is part of the `httpRequestData` API. 158 | * @callback onRequestDataCallback - The callback that acknowleges the send. 159 | * @param reqData - Incoming status data from the HTTP request. 160 | * @param respData.req - The request handle. 161 | * @param respData.status - positive value indicates successful transmit. 162 | * @returns {void} No return value. 163 | */ 164 | exports.httpRequestData = require('./httpRequestData').httpRequestData; 165 | 166 | /** 167 | * Terminate payload data transmission for HTTP POST request to a Ziti Service. 168 | * @function httpRequestEnd 169 | * @param {number} req - A Ziti HttpRequest handle. 170 | * @returns {void} No return value. 171 | */ 172 | exports.httpRequestEnd = require('./httpRequestEnd').httpRequestEnd; 173 | 174 | /** 175 | * Initialize the Ziti session and authenticate with control plane. 176 | * @function init 177 | * @param {string} identityPath - File system path to the identity file. 178 | * @returns {number} A status value ranging from 0 to 255. 179 | */ 180 | exports.init = require('./init').init; 181 | 182 | // Internal use only 183 | exports.listen = require('./listen').listen; 184 | 185 | /** 186 | * Set the logging level. 187 | * @function setLogLevel 188 | * @param {number} level - 0=NONE, 1=ERROR, 2=WARN, 3=INFO, 4=DEBUG, 5=TRACE 189 | * @returns {void} No return value. 190 | */ 191 | exports.setLogLevel = require('./setLogLevel').setLogLevel; 192 | 193 | /** 194 | * Set the logging level. 195 | * @function serviceAvailable 196 | * @param {string} serviceName - The name of the Ziti Service being queried. 197 | * @param {onServiceAvailableCallback} onServiceAvailable - The callback that returns results of the query. 198 | * @returns {void} No return value. 199 | */ 200 | /** 201 | * This callback is part of the `serviceAvailable` API. 202 | * @callback onServiceAvailableCallback - The callback that returns results of the query. 203 | * @param availability - results of the query. 204 | * @param availability.status - 0 means `available and OK`, <0 means `unavailable` 205 | * @param availability.permissions - 1 means the identity can dial, 2 means the identity can bind 206 | * @returns {void} No return value. 207 | */ 208 | exports.serviceAvailable = require('./serviceAvailable').serviceAvailable; 209 | 210 | /** 211 | * write data to a Ziti connection. 212 | * @function write 213 | * @param {number} conn - A Ziti connection handle. 214 | * @param {Buffer} data - The data to send. 215 | * @param {onWriteCallback} onWrite - The callback that returns status of the write. 216 | * @returns {void} No return value. 217 | */ 218 | /** 219 | * This callback is part of the `write` API. 220 | * @callback onWriteCallback - The callback that returns results of the v. 221 | * @param status - 0 means success, <0 means failure. 222 | * @returns {void} No return value. 223 | */ 224 | exports.write = require('./write').write; 225 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openziti/ziti-sdk-nodejs", 3 | "description": "A NodeJS-based SDK for delivering secure applications over a Ziti Network", 4 | "version": "0.20.0", 5 | "main": "./lib/ziti", 6 | "scripts": { 7 | "build": "npm run build:configure && npm run build:make", 8 | "build:configure": "run-script-os", 9 | "build:configure:windows": "configure", 10 | "build:configure:linux:darwin": "./configure", 11 | "build:make": "cmake-js build", 12 | "build:package": "node-pre-gyp package", 13 | "clean": "cmake-js clean", 14 | "test": "echo \"Error: no test specified\"", 15 | "install": "node-pre-gyp install || npm run build", 16 | "publish": "node-pre-gyp package publish", 17 | "docs": "jsdoc2md -t API_REFERENCE.hbs lib/*.js > API_REFERENCE.md" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/openziti/ziti-sdk-nodejs.git" 22 | }, 23 | "binary": { 24 | "module_name": "ziti_sdk_nodejs", 25 | "module_path": "./build/{configuration}/", 26 | "remote_path": "./{module_name}/v{version}/{configuration}/", 27 | "package_name": "{module_name}-v{version}-{node_abi}-{platform}-{arch}.tar.gz", 28 | "host": "https://ziti-npm.s3.amazonaws.com" 29 | }, 30 | "license": "Apache-2.0", 31 | "licenses": [ 32 | { 33 | "type": "Apache-2.0", 34 | "url": "http://www.apache.org/licenses/LICENSE-2.0" 35 | } 36 | ], 37 | "keywords": [ 38 | "ziti", 39 | "nodejs" 40 | ], 41 | "engines": { 42 | "node": ">=11.0.0" 43 | }, 44 | "author": { 45 | "name": "OpenZiti", 46 | "url": "http://openziti.io" 47 | }, 48 | "devDependencies": { 49 | "aws-sdk": "^2.1692.0", 50 | "docdash": "^1.2.0", 51 | "jsdoc": "^4.0.4", 52 | "jsdoc-to-markdown": "^7.1.1", 53 | "run-script-os": "^1.1.6" 54 | }, 55 | "dependencies": { 56 | "@mapbox/node-pre-gyp": "^1.0.11", 57 | "bindings": "^1.5.0", 58 | "cmake-js": "^7.3.0" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /scripts/build-appveyor.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | SETLOCAL 3 | SET EL=0 4 | 5 | ECHO ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ %~f0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | IF /I "%msvs_toolset%"=="" ECHO msvs_toolset unset, defaulting to 14 && SET msvs_toolset=14 8 | IF /I "%msvs_version%"=="" ECHO msvs_version unset, defaulting to 2019 && SET msvs_version=2019 9 | 10 | SET PATH=%CD%;%PATH% 11 | IF "%msvs_toolset%"=="12" SET msvs_version=2013 12 | IF NOT "%NODE_RUNTIME%"=="" SET "TOOLSET_ARGS=%TOOLSET_ARGS% --runtime=%NODE_RUNTIME%" 13 | IF NOT "%NODE_RUNTIME_VERSION%"=="" SET "TOOLSET_ARGS=%TOOLSET_ARGS% --target=%NODE_RUNTIME_VERSION%" 14 | 15 | ECHO APPVEYOR^: %APPVEYOR% 16 | ECHO nodejs_version^: %nodejs_version% 17 | ECHO platform^: %platform% 18 | ECHO msvs_toolset^: %msvs_toolset% 19 | ECHO msvs_version^: %msvs_version% 20 | ECHO TOOLSET_ARGS^: %TOOLSET_ARGS% 21 | 22 | ECHO activating VS command prompt 23 | :: NOTE this call makes the x64 -> X64 24 | IF /I "%platform%"=="x64" ECHO x64 && CALL "C:\Program Files (x86)\Microsoft Visual Studio\%msvs_version%\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64 25 | IF /I "%platform%"=="x86" ECHO x86 && CALL "C:\Program Files (x86)\Microsoft Visual Studio\%msvs_version%\Community\VC\Auxiliary\Build\vcvarsall.bat" x86 26 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 27 | 28 | ECHO using compiler^: && CALL cl 29 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 30 | 31 | ECHO using MSBuild^: && CALL msbuild /version && ECHO. 32 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 33 | 34 | ECHO downloading/installing node 35 | powershell Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) $env:PLATFORM 36 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 37 | 38 | powershell Set-ExecutionPolicy Unrestricted -Scope CurrentUser -Force 39 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 40 | 41 | ECHO available node.exe^: 42 | call where node 43 | ECHO available npm^: 44 | call where npm 45 | 46 | ECHO node^: && call node -v 47 | call node -e "console.log(' - arch:',process.arch,'\n - argv:',process.argv,'\n - execPath:',process.execPath)" 48 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 49 | 50 | ECHO npm^: && CALL npm -v 51 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 52 | 53 | ECHO ===== where npm puts stuff START ============ 54 | ECHO npm root && CALL npm root 55 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 56 | ECHO npm root -g && CALL npm root -g 57 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 58 | 59 | ECHO npm bin && CALL npm bin 60 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 61 | ECHO npm bin -g && CALL npm bin -g 62 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 63 | 64 | SET NPM_BIN_DIR= 65 | FOR /F "tokens=*" %%i in ('CALL npm bin -g') DO SET NPM_BIN_DIR=%%i 66 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 67 | IF /I "%NPM_BIN_DIR%"=="%CD%" ECHO ERROR npm bin -g equals local directory && SET ERRORLEVEL=1 && GOTO ERROR 68 | ECHO ===== where npm puts stuff END ============ 69 | 70 | IF "%nodejs_version:~0,1%"=="4" CALL npm install node-gyp@3.x 71 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 72 | IF "%nodejs_version:~0,1%"=="5" CALL npm install node-gyp@3.x 73 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 74 | 75 | ::Need to force update node-gyp to v6+ for electron v6 and v5 76 | ECHO ===== conditional node-gyp upgrade START ============ 77 | :: Find the folder to install the node-gyp in 78 | SET npm_in_nodejs_dir="%ProgramFiles%\nodejs\node_modules\npm" 79 | ECHO npm_in_nodejs_dir^: %npm_in_nodejs_dir% 80 | IF /I "%platform%"=="x86" SET npm_in_nodejs_dir="%ProgramFiles(x86)%\nodejs\node_modules\npm" 81 | ECHO npm_in_nodejs_dir^: %npm_in_nodejs_dir% 82 | :: Set boolean whether the update has to happen 83 | SET "needs_patch=" 84 | IF DEFINED NODE_RUNTIME_VERSION ( 85 | ECHO NODE_RUNTIME_VERSION_REDUCED^: %NODE_RUNTIME_VERSION:~0,1% 86 | IF "%NODE_RUNTIME_VERSION:~0,1%"=="5" SET "needs_patch=y" 87 | IF "%NODE_RUNTIME_VERSION:~0,1%"=="6" SET "needs_patch=y" 88 | ) 89 | :: Check if electron and install 90 | ECHO NODE_RUNTIME^: %NODE_RUNTIME% 91 | IF DEFINED needs_patch CALL npm install --prefix %npm_in_nodejs_dir% node-gyp@6.x 92 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 93 | ECHO ===== conditional node-gyp upgrade END ============ 94 | 95 | :: install node-gyp v6.1 96 | ECHO ===== install node-gyp v6.1 ============ 97 | CALL npm install -g node-gyp 98 | REM SET npm_config_node_gyp=C:\Users\appveyor\AppData\Roaming\npm\node-gyp 99 | SET npm_config_node_gyp=C:\Users\appveyor\AppData\Roaming\npm\node_modules\node-gyp\bin\node-gyp.js 100 | ECHO ===== install node-gyp v6.1 completed ============ 101 | 102 | :: build Ziti C-SDK 103 | CALL git submodule update --init --recursive 104 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 105 | CALL cd deps 106 | CALL cd ziti-sdk-c 107 | CALL git submodule update --init --recursive 108 | ECHO ===== starting make sequence... ============ 109 | CALL mkdir build 110 | CALL cd build 111 | 112 | ECHO ===== calling cmake -G Ninja ============ 113 | CALL cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=OFF -G "NMake Makefiles" .. 114 | 115 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 116 | ECHO ===== calling cmake --build ============ 117 | CALL nmake ziti 118 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 119 | CALL cd .. 120 | CALL cd .. 121 | CALL cd .. 122 | 123 | :: build Ziti NodeJS-SDK 124 | ECHO ===== calling npm install --build-from-source ============ 125 | CALL npm install --build-from-source --cd=%CD% --msvs_version=%msvs_version% %TOOLSET_ARGS% 126 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 127 | 128 | :: test our module 129 | CALL node tests/hello.js 130 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 131 | 132 | FOR /F "tokens=*" %%i in ('"CALL node_modules\.bin\node-pre-gyp reveal module %TOOLSET_ARGS% --silent"') DO SET MODULE=%%i 133 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 134 | FOR /F "tokens=*" %%i in ('node -e "console.log(process.execPath)"') DO SET NODE_EXE=%%i 135 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 136 | 137 | dumpbin /DEPENDENTS "%NODE_EXE%" 138 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 139 | dumpbin /DEPENDENTS "%MODULE%" 140 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 141 | 142 | 143 | IF "%NODE_RUNTIME%"=="electron" GOTO CHECK_ELECTRON_TEST_ERRORLEVEL 144 | 145 | ::skipping check for errorlevel npm test result when using io.js 146 | ::@springmeyer: how to proceed? 147 | IF NOT "%nodejs_version%"=="1.8.1" IF NOT "%nodejs_version%"=="2.0.0" GOTO CHECK_NPM_TEST_ERRORLEVEL 148 | 149 | ECHO test our module 150 | CALL node tests/hello.js 151 | ECHO ========================================== 152 | ECHO ========================================== 153 | ECHO ========================================== 154 | 155 | GOTO NPM_TEST_FINISHED 156 | 157 | 158 | :CHECK_ELECTRON_TEST_ERRORLEVEL 159 | ECHO installing electron 160 | CALL npm install -g "electron@%NODE_RUNTIME_VERSION%" 161 | ECHO installing electron-mocha 162 | IF "%nodejs_version%" LEQ 6 CALL npm install -g "electron-mocha@7" 163 | IF "%nodejs_version%" GTR 6 CALL npm install -g "electron-mocha" 164 | ECHO preparing tests 165 | CALL electron "test/support/createdb-electron.js" 166 | DEL "test\support\createdb-electron.js" 167 | ECHO calling electron-mocha 168 | CALL electron-mocha -R spec --timeout 480000 169 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 170 | GOTO NPM_TEST_FINISHED 171 | 172 | 173 | :CHECK_NPM_TEST_ERRORLEVEL 174 | ECHO calling npm test 175 | CALL npm test 176 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 177 | 178 | :NPM_TEST_FINISHED 179 | ECHO packaging for node-gyp 180 | CALL node_modules\.bin\node-pre-gyp package %TOOLSET_ARGS% 181 | ::make commit message env var shorter 182 | SET CM=%APPVEYOR_REPO_COMMIT_MESSAGE% 183 | IF NOT "%CM%" == "%CM:[publish binary]=%" (ECHO publishing && CALL node_modules\.bin\node-pre-gyp --msvs_version=%msvs_version% publish %TOOLSET_ARGS%) ELSE (ECHO not publishing) 184 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 185 | 186 | GOTO DONE 187 | 188 | 189 | 190 | :ERROR 191 | ECHO ~~~~~~~~~~~~~~~~~~~~~~ ERROR %~f0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 192 | ECHO ERRORLEVEL^: %ERRORLEVEL% 193 | SET EL=%ERRORLEVEL% 194 | 195 | :DONE 196 | ECHO ~~~~~~~~~~~~~~~~~~~~~~ DONE %~f0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 197 | 198 | EXIT /b %EL% 199 | -------------------------------------------------------------------------------- /scripts/install_node.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | do_node_install() { 4 | # if an existing nvm is already installed we need to unload it 5 | nvm unload || true 6 | 7 | # here we set up the node version on the fly based on the matrix value. 8 | # This is done manually so that the build works the same on OS X 9 | rm -rf ./__nvm/ && git clone --depth 1 https://github.com/creationix/nvm.git ./__nvm 10 | source ./__nvm/nvm.sh 11 | nvm install ${NODE_VERSION} 12 | nvm use --delete-prefix ${NODE_VERSION} 13 | which node 14 | } 15 | 16 | do_win_node_install() { 17 | # # install NVS 18 | # choco install nvs 19 | 20 | # Install NVS. 21 | export NVS_HOME="$HOME/.nvs" 22 | git clone https://github.com/jasongin/nvs "$NVS_HOME" 23 | export NVS_EXECUTE=1 24 | . "$NVS_HOME/nvs.sh" install 25 | 26 | echo "DEBUG 1: check nvs version" 27 | nvs --version 28 | echo "DEBUG 1: done" 29 | 30 | echo "DEBUG 2: do nvs add" 31 | nvs add node/${NODE_VERSION} 32 | echo "DEBUG 2: done" 33 | 34 | echo "DEBUG 3: do nvs use" 35 | "$NVS_HOME/nvs.sh" use node/${NODE_VERSION} 36 | $PATH += ~/.nvs/node/${NODE_VERSION}/x64 37 | echo "DEBUG 3: done" 38 | 39 | echo "DEBUG 4: do nvs link" 40 | "$NVS_HOME/nvs.sh" link node/${NODE_VERSION} 41 | echo "DEBUG 4: done" 42 | 43 | echo "DEBUG 5: do node --version" 44 | node --version 45 | echo "DEBUG 5: done" 46 | 47 | echo "DEBUG 6: do npm --version" 48 | npm --version 49 | echo "DEBUG 6: done" 50 | 51 | 52 | # ls -l ${LOCALAPPDATA}/nvs 53 | 54 | # # Install the selected version of Node.js using NVS. 55 | # ${LOCALAPPDATA}/nvs/nvs.cmd add ${NODE_VERSION} 56 | # ${LOCALAPPDATA}/nvs/nvs.cmd use ${NODE_VERSION} 57 | # ${LOCALAPPDATA}/nvs/nvs.cmd link ${NODE_VERSION} 58 | 59 | # export PATH=${LOCALAPPDATA}/nvs/default/:$PATH 60 | 61 | # echo "after nvs, PATH is now: $PATH" 62 | 63 | # ls -l ${LOCALAPPDATA}/nvs/default/node_modules/npm/bin 64 | 65 | } 66 | 67 | if [[ ${1:-false} == 'false' ]]; then 68 | echo "Error: pass node version as first argument" 69 | exit 1 70 | fi 71 | 72 | NODE_VERSION=$1 73 | 74 | echo "OSTYPE is: $OSTYPE" 75 | 76 | if [[ "$OSTYPE" == "linux-gnu" ]]; then 77 | echo "OSTYPE is supported"; 78 | do_node_install; 79 | elif [[ "$OSTYPE" == "darwin"* ]]; then 80 | echo "OSTYPE is supported"; 81 | do_node_install; 82 | elif [[ "$OSTYPE" == "cygwin" ]]; then 83 | # POSIX compatibility layer and Linux environment emulation for Windows 84 | echo "OSTYPE is supported"; 85 | do_win_node_install; 86 | elif [[ "$OSTYPE" == "msys" ]]; then 87 | # Lightweight shell and GNU utilities compiled for Windows (part of MinGW) 88 | echo "OSTYPE is supported"; 89 | # do_win_node_install; 90 | do_node_install; 91 | elif [[ "$OSTYPE" == "win32" ]]; then 92 | # I'm not sure this can happen. 93 | echo "OSTYPE is unsupported"; 94 | elif [[ "$OSTYPE" == "freebsd"* ]]; then 95 | echo "OSTYPE is supported"; 96 | do_node_install; 97 | else 98 | echo "OSTYPE is unsupported"; 99 | fi 100 | -------------------------------------------------------------------------------- /scripts/validate_tag.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -u 4 | 5 | # let's catch the case where we tag but 6 | # forget to increment the package.json version 7 | 8 | # check if we are on a tag 9 | if [ `git describe --tags --always HEAD` ]; then 10 | echo 'looks like we are on a tag' 11 | if [[ $TRAVIS_BRANCH == `git describe --tags --always HEAD` ]]; then 12 | echo 'git reports the same tag as travis' 13 | # now check to make sure package.json `version` matches 14 | MODULE_VERSION=$(node -e "console.log(require('./package.json').version)") 15 | if [[ $MODULE_VERSION != $TRAVIS_BRANCH ]] && [[ v$MODULE_VERSION != $TRAVIS_BRANCH ]]; then 16 | echo "package.json version ($MODULE_VERSION) does not match tag ($TRAVIS_BRANCH)" 17 | exit 1 18 | else 19 | echo "Validation success: package.json ($MODULE_VERSION) matches tag ($TRAVIS_BRANCH)" 20 | fi 21 | else 22 | echo "warning: travis thinks the tag ($TRAVIS_BRANCH) differs from git (`git describe --tags --always HEAD`)" 23 | fi 24 | fi 25 | -------------------------------------------------------------------------------- /src/Ziti_https_request_data.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "ziti-nodejs.h" 18 | #include 19 | 20 | 21 | /** 22 | * This function is responsible for calling the JavaScript on_resp_data callback function 23 | * that was specified when the Ziti_https_request_data(...) was called from JavaScript. 24 | */ 25 | static void CallJs_on_req_body(napi_env env, napi_value js_cb, void* context, void* data) { 26 | 27 | ZITI_NODEJS_LOG(DEBUG, "entered"); 28 | 29 | // This parameter is not used. 30 | (void) context; 31 | 32 | // Retrieve the HttpsRespBodyItem created by the worker thread. 33 | HttpsReqBodyItem* item = (HttpsReqBodyItem*)data; 34 | 35 | // env and js_cb may both be NULL if Node.js is in its cleanup phase, and 36 | // items are left over from earlier thread-safe calls from the worker thread. 37 | // When env is NULL, we simply skip over the call into Javascript 38 | if (env != NULL) { 39 | 40 | napi_value undefined; 41 | 42 | // Retrieve the JavaScript `undefined` value so we can use it as the `this` 43 | // value of the JavaScript function call. 44 | napi_get_undefined(env, &undefined); 45 | 46 | // const obj = {} 47 | napi_value js_http_item, js_req, js_status, js_body; 48 | int rc = napi_create_object(env, &js_http_item); 49 | if (rc != napi_ok) { 50 | napi_throw_error(env, "EINVAL", "failure to create object"); 51 | } 52 | 53 | // obj.req = req 54 | napi_create_int64(env, (int64_t)item->req, &js_req); 55 | if (rc != napi_ok) { 56 | napi_throw_error(env, "EINVAL", "failure to create resp.req"); 57 | } 58 | rc = napi_set_named_property(env, js_http_item, "req", js_req); 59 | if (rc != napi_ok) { 60 | napi_throw_error(env, "EINVAL", "failure to set named property req"); 61 | } 62 | ZITI_NODEJS_LOG(DEBUG, "js_req: %p", item->req); 63 | 64 | // obj.code = status 65 | rc = napi_create_int32(env, item->status, &js_status); 66 | if (rc != napi_ok) { 67 | napi_throw_error(env, "EINVAL", "failure to create resp.status"); 68 | } 69 | rc = napi_set_named_property(env, js_http_item, "status", js_status); 70 | if (rc != napi_ok) { 71 | napi_throw_error(env, "EINVAL", "failure to set named property status"); 72 | } 73 | ZITI_NODEJS_LOG(DEBUG, "status: %zd", item->status); 74 | 75 | // obj.body = body 76 | rc = napi_create_int32(env, (int64_t)item->body, &js_body); 77 | if (rc != napi_ok) { 78 | napi_throw_error(env, "EINVAL", "failure to create resp.body"); 79 | } 80 | rc = napi_set_named_property(env, js_http_item, "body", js_body); 81 | if (rc != napi_ok) { 82 | napi_throw_error(env, "EINVAL", "failure to set named property body"); 83 | } 84 | 85 | // Call the JavaScript function and pass it the HttpsRespItem 86 | rc = napi_call_function( 87 | env, 88 | undefined, 89 | js_cb, 90 | 1, 91 | &js_http_item, 92 | NULL 93 | ); 94 | if (rc != napi_ok) { 95 | napi_throw_error(env, "EINVAL", "failure to invoke JS callback"); 96 | } 97 | 98 | } 99 | } 100 | 101 | 102 | 103 | /** 104 | * 105 | */ 106 | void on_req_body(tlsuv_http_req_t *req, char *body, ssize_t status) { 107 | 108 | ZITI_NODEJS_LOG(DEBUG, "status: %zd, body: %p", status, body); 109 | 110 | HttpsAddonData* addon_data = (HttpsAddonData*) req->data; 111 | ZITI_NODEJS_LOG(DEBUG, "addon_data is: %p", addon_data); 112 | 113 | HttpsReqBodyItem* item = calloc(1, sizeof(*item)); 114 | ZITI_NODEJS_LOG(DEBUG, "new HttpsReqBodyItem is: %p", item); 115 | 116 | // Grab everything off the tlsuv_http_resp_t that we need to eventually pass on to the JS on_resp_body callback. 117 | // If we wait until CallJs_on_resp_body is invoked to do that work, the tlsuv_http_resp_t may have already been free'd by the C-SDK 118 | 119 | item->req = req; 120 | item->body = (void*)body; 121 | item->status = status; 122 | 123 | ZITI_NODEJS_LOG(DEBUG, "calling tsfn_on_req_body: %p", addon_data->tsfn_on_req_body); 124 | 125 | // Initiate the call into the JavaScript callback. 126 | int rc = napi_call_threadsafe_function( 127 | addon_data->tsfn_on_req_body, 128 | item, 129 | napi_tsfn_blocking); 130 | if (rc != napi_ok) { 131 | napi_throw_error(addon_data->env, "EINVAL", "failure to invoke JS callback"); 132 | } 133 | } 134 | 135 | 136 | 137 | /** 138 | * Send Body data over active HTTPS request 139 | * 140 | * @param {string} [0] req 141 | * @param {string} [1] data (we expect a Buffer) 142 | * @param {func} [2] JS on_write callback; This is invoked from 'on_req_body' function above 143 | * 144 | * @returns NULL 145 | */ 146 | napi_value _Ziti_http_request_data(napi_env env, const napi_callback_info info) { 147 | napi_status status; 148 | int rc; 149 | size_t argc = 3; 150 | napi_value args[3]; 151 | status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); 152 | if (status != napi_ok) { 153 | napi_throw_error(env, NULL, "Failed to parse arguments"); 154 | } 155 | 156 | if (argc < 3) { 157 | napi_throw_error(env, "EINVAL", "Too few arguments"); 158 | return NULL; 159 | } 160 | 161 | // Obtain tlsuv_http_req_t 162 | int64_t js_req; 163 | status = napi_get_value_int64(env, args[0], &js_req); 164 | if (status != napi_ok) { 165 | napi_throw_error(env, NULL, "Failed to get Req"); 166 | } 167 | // tlsuv_http_req_t *r = (tlsuv_http_req_t*)js_req; 168 | HttpsReq* httpsReq = (HttpsReq*)js_req; 169 | tlsuv_http_req_t *r = httpsReq->req; 170 | 171 | ZITI_NODEJS_LOG(DEBUG, "req: %p", r); 172 | 173 | HttpsAddonData* addon_data = httpsReq->addon_data; 174 | ZITI_NODEJS_LOG(DEBUG, "addon_data is: %p", addon_data); 175 | 176 | // If some kind of Ziti error previously occured on this request, then short-circuit now 177 | if (httpsReq->on_resp_has_fired && (httpsReq->respCode < 0)) { 178 | ZITI_NODEJS_LOG(DEBUG, "aborting due to previous error: %d", httpsReq->respCode); 179 | return NULL; 180 | } 181 | 182 | // Obtain data to write (we expect a Buffer) 183 | void* buffer; 184 | size_t bufferLength; 185 | status = napi_get_buffer_info(env, args[1], &buffer, &bufferLength); 186 | if (status != napi_ok) { 187 | napi_throw_error(env, NULL, "Failed to get Buffer info"); 188 | } 189 | ZITI_NODEJS_LOG(DEBUG, "bufferLength: %zd", bufferLength); 190 | 191 | // Since the underlying Buffer's lifetime is not guaranteed if it's managed by the VM, we will copy the chunk into our heap 192 | void* chunk = calloc(1, bufferLength + 1); 193 | memcpy(chunk, buffer, bufferLength); 194 | 195 | // Obtain ptr to JS 'on_write' callback function 196 | napi_value js_write_cb = args[2]; 197 | napi_value work_name; 198 | 199 | // Create a string to describe this asynchronous operation. 200 | status = napi_create_string_utf8( 201 | env, 202 | "N-API on_write", 203 | NAPI_AUTO_LENGTH, 204 | &work_name); 205 | if (status != napi_ok) { 206 | napi_throw_error(env, NULL, "Failed to napi_create_string_utf8"); 207 | } 208 | 209 | // Convert the callback retrieved from JavaScript into a thread-safe function (tsfn) 210 | // which we can call from a worker thread. 211 | rc = napi_create_threadsafe_function( 212 | env, 213 | js_write_cb, 214 | NULL, 215 | work_name, 216 | 0, 217 | 1, 218 | NULL, 219 | NULL, 220 | NULL, 221 | CallJs_on_req_body, 222 | &(addon_data->tsfn_on_req_body) 223 | ); 224 | if (rc != napi_ok) { 225 | napi_throw_error(env, "EINVAL", "Failed to create threadsafe_function"); 226 | } 227 | ZITI_NODEJS_LOG(DEBUG, "napi_create_threadsafe_function addon_data->tsfn_on_req_body() : %p", addon_data->tsfn_on_req_body); 228 | 229 | // Now, call the C-SDK to actually write the data over to the service 230 | tlsuv_http_req_data(r, chunk, bufferLength, on_req_body ); 231 | 232 | return NULL; 233 | } 234 | 235 | 236 | void expose_ziti_https_request_data(napi_env env, napi_value exports) { 237 | napi_status status; 238 | napi_value fn; 239 | 240 | status = napi_create_function(env, NULL, 0, _Ziti_http_request_data, NULL, &fn); 241 | if (status != napi_ok) { 242 | napi_throw_error(env, NULL, "Unable to wrap native function '_Ziti_http_request_data"); 243 | } 244 | 245 | status = napi_set_named_property(env, exports, "Ziti_http_request_data", fn); 246 | if (status != napi_ok) { 247 | napi_throw_error(env, NULL, "Unable to populate exports for 'Ziti_http_request_data"); 248 | } 249 | 250 | } 251 | -------------------------------------------------------------------------------- /src/Ziti_https_request_end.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "ziti-nodejs.h" 18 | #include 19 | 20 | 21 | /** 22 | * Indicate that an active HTTPS request is now complete 23 | * 24 | * @param {string} [0] req 25 | * 26 | * @returns NULL 27 | */ 28 | napi_value _Ziti_http_request_end(napi_env env, const napi_callback_info info) { 29 | napi_status status; 30 | size_t argc = 1; 31 | napi_value args[1]; 32 | status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); 33 | if (status != napi_ok) { 34 | napi_throw_error(env, NULL, "Failed to parse arguments"); 35 | } 36 | 37 | if (argc != 1) { 38 | napi_throw_error(env, "EINVAL", "Invalid argument count"); 39 | return NULL; 40 | } 41 | 42 | // Obtain tlsuv_http_req_t 43 | int64_t js_req; 44 | status = napi_get_value_int64(env, args[0], &js_req); 45 | if (status != napi_ok) { 46 | napi_throw_error(env, NULL, "Failed to get Req"); 47 | } 48 | HttpsReq* httpsReq = (HttpsReq*)js_req; 49 | tlsuv_http_req_t *r = httpsReq->req; 50 | 51 | ZITI_NODEJS_LOG(DEBUG, "req: %p", r); 52 | 53 | // TEMP hack to work around an issue still being debugged 54 | ZITI_NODEJS_LOG(DEBUG, "httpsReq->on_resp_has_fired: %o", httpsReq->on_resp_has_fired); 55 | if (httpsReq->on_resp_has_fired) { 56 | ZITI_NODEJS_LOG(DEBUG, "seems as though on_resp has previously fired... skipping call to tlsuv_http_req_end"); 57 | } else { 58 | tlsuv_http_req_end(r); 59 | } 60 | 61 | return NULL; 62 | } 63 | 64 | 65 | void expose_ziti_https_request_end(napi_env env, napi_value exports) { 66 | napi_status status; 67 | napi_value fn; 68 | 69 | status = napi_create_function(env, NULL, 0, _Ziti_http_request_end, NULL, &fn); 70 | if (status != napi_ok) { 71 | napi_throw_error(env, NULL, "Unable to wrap native function '_Ziti_http_request_end"); 72 | } 73 | 74 | status = napi_set_named_property(env, exports, "Ziti_http_request_end", fn); 75 | if (status != napi_ok) { 76 | napi_throw_error(env, NULL, "Unable to populate exports for 'Ziti_http_request_end"); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/build_config.h.in: -------------------------------------------------------------------------------- 1 | #define ZITI_NODEJS_VERSION @ZITI_VERSION@ 2 | #define ZITI_NODEJS_COMMIT @ZITI_COMMIT@ 3 | #define ZITI_NODEJS_BRANCH @ZITI_BRANCH@ 4 | #define ZITI_OS @CMAKE_SYSTEM_NAME@ 5 | #define ZITI_ARCH @CMAKE_SYSTEM_PROCESSOR@ 6 | #define BUILD_DATE @BUILD_DATE@ -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "utils.h" 19 | #include 20 | #include 21 | 22 | 23 | #if !defined(BUILD_DATE) 24 | #define BUILD_DATE unknown 25 | #endif 26 | 27 | #if !defined(ZITI_OS) 28 | #define ZITI_OS unknown 29 | #endif 30 | 31 | #if !defined(ZITI_ARCH) 32 | #define ZITI_ARCH unknown 33 | #endif 34 | 35 | #if !defined(ZITI_VERSION) 36 | #define ZITI_VERSION unknown 37 | #endif 38 | 39 | #if !defined(ZITI_BRANCH) 40 | #define ZITI_BRANCH no-branch 41 | #define ZITI_COMMIT sha 42 | #endif 43 | 44 | // #define to_str(x) str(x) 45 | #define str(x) #x 46 | 47 | 48 | const char* ziti_nodejs_get_version(int verbose) { 49 | if (verbose) { 50 | return "\n\tVersion:\t" to_str(ZITI_NODEJS_VERSION) 51 | "\n\tBuild Date:\t" to_str(BUILD_DATE) 52 | "\n\tGit Branch:\t" to_str(ZITI_NODEJS_BRANCH) 53 | "\n\tGit SHA:\t" to_str(ZITI_NODEJS_COMMIT) 54 | "\n\tOS: \t" to_str(ZITI_OS) 55 | "\n\tArch: \t" to_str(ZITI_ARCH) 56 | "\n\t"; 57 | 58 | } 59 | return to_str(ZITI_VERSION); 60 | } 61 | 62 | const char* ziti_nodejs_git_branch() { 63 | return to_str(ZITI_BRANCH); 64 | } 65 | 66 | const char* ziti_nodejs_git_commit() { 67 | return to_str(ZITI_COMMIT); 68 | } 69 | 70 | int ziti_nodejs_debug_level = ZITI_LOG_DEFAULT_LEVEL; 71 | FILE *ziti_nodejs_debug_out; 72 | 73 | #if _WIN32 74 | LARGE_INTEGER frequency; 75 | LARGE_INTEGER start; 76 | LARGE_INTEGER end; 77 | #else 78 | struct timespec starttime; 79 | #endif 80 | 81 | void init_nodejs_debug() { 82 | char *level = getenv("ZITI_NODEJS_LOG"); 83 | if (level != NULL) { 84 | ziti_nodejs_debug_level = (int) strtol(level, NULL, 10); 85 | } 86 | ziti_nodejs_debug_out = stderr; 87 | 88 | #if _WIN32 89 | QueryPerformanceFrequency(&frequency); 90 | QueryPerformanceCounter(&start); 91 | #else 92 | clock_gettime(CLOCK_MONOTONIC, &starttime); 93 | #endif 94 | } 95 | 96 | long get_nodejs_elapsed() { 97 | #if _WIN32 98 | QueryPerformanceCounter(&end); 99 | return end.QuadPart - start.QuadPart; 100 | #else 101 | struct timespec cur; 102 | clock_gettime(CLOCK_MONOTONIC, &cur); 103 | return (cur.tv_sec - starttime.tv_sec) * 1000 + ((cur.tv_nsec - starttime.tv_nsec) / ((long)1e6)); 104 | #endif 105 | } 106 | 107 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef ZITI_TLS_UTILS_H 18 | #define ZITI_TLS_UTILS_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | extern const char *ziti_nodejs_get_version(int verbose); 29 | extern const char *ziti_nodejs_git_branch(); 30 | extern const char *ziti_nodejs_git_commit(); 31 | extern void nodejs_hexDump(char *desc, void *addr, int len); 32 | 33 | 34 | typedef const char *(*fmt_error_t)(int); 35 | typedef int *(*cond_error_t)(int); 36 | 37 | #define __FILENAME_NODEJS__ (__FILENAME__) 38 | 39 | 40 | extern void init_nodejs_debug(); 41 | 42 | extern int ziti_nodejs_debug_level; 43 | extern FILE *ziti_nodejs_debug_out; 44 | 45 | 46 | /// for windows compilation NOGDI needs to be set: 47 | // #define DEBUG_LEVELS(XX) \ 48 | // XX(NONE) \ 49 | // XX(ERROR) /*WINDOWS - see comment above wrt NOGDI*/ \ 50 | // XX(WARN) \ 51 | // XX(INFO) \ 52 | // XX(DEBUG) \ 53 | // XX(VERBOSE) \ 54 | // XX(TRACE) 55 | 56 | 57 | // enum DebugLevel { 58 | // #define _level(n) n, 59 | // DEBUG_LEVELS(_level) 60 | // #undef _level 61 | // }; 62 | 63 | // #define container_of(ptr, type, member) ((type *) ((ptr) - offsetof(type, member))) 64 | 65 | // TEMP: skip logging on windows 66 | #ifdef WIN32 67 | #define ZITI_NODEJS_LOG(...) 68 | #else 69 | #define ZITI_NODEJS_LOG(level, fmt, ...) do { \ 70 | if (level <= ziti_nodejs_debug_level) {\ 71 | long elapsed = get_nodejs_elapsed();\ 72 | fprintf(ziti_nodejs_debug_out, "[%9ld.%03ld] " #level "\tziti-sdk-nodejs/%s:%d %s(): " fmt "\n",\ 73 | (long int)(elapsed/1000), (long int)(elapsed%1000), __FILENAME_NODEJS__, __LINE__, __func__, ##__VA_ARGS__);\ 74 | }\ 75 | } while(0) 76 | #endif 77 | 78 | long get_nodejs_elapsed(); 79 | 80 | 81 | #ifdef __cplusplus 82 | } 83 | #endif 84 | 85 | #endif //ZITI_TLS_UTILS_H 86 | -------------------------------------------------------------------------------- /src/ziti-add-on.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "ziti-nodejs.h" 18 | 19 | extern void set_signal_handler(); 20 | 21 | uv_loop_t *thread_loop; 22 | 23 | napi_value Init(napi_env env, napi_value exports) { 24 | 25 | if (uv_mutex_init(&client_pool_lock)) 26 | abort(); 27 | 28 | // Install call-stack tracer 29 | // set_signal_handler(); 30 | 31 | // TEMP: skip logging on windows 32 | #ifndef WIN32 33 | 34 | init_nodejs_debug(); 35 | 36 | # ifdef NODE_MAJOR_VERSION 37 | # if NODE_MAJOR_VERSION == 11 38 | uv_timeval_t start_time; 39 | # else 40 | uv_timeval64_t start_time; 41 | # endif 42 | # endif 43 | 44 | uv_gettimeofday(&start_time); 45 | 46 | struct tm *start_tm = gmtime((const time_t*)&start_time.tv_sec); 47 | char time_str[32]; 48 | strftime(time_str, sizeof(time_str), "%FT%T", start_tm); 49 | 50 | # ifdef NODE_MAJOR_VERSION 51 | # if NODE_MAJOR_VERSION == 11 52 | ZITI_NODEJS_LOG(INFO, "Ziti NodeJS SDK version %s@%s(%s) starting at (%s.%03ld)", 53 | ziti_nodejs_get_version(true), ziti_nodejs_git_commit(), ziti_nodejs_git_branch(), 54 | time_str, 55 | start_time.tv_usec/1000); 56 | # else 57 | ZITI_NODEJS_LOG(INFO, "Ziti NodeJS SDK version %s@%s(%s) starting at (%s.%03d)", 58 | ziti_nodejs_get_version(true), ziti_nodejs_git_commit(), ziti_nodejs_git_branch(), 59 | time_str, 60 | start_time.tv_usec/1000); 61 | # endif 62 | # endif 63 | 64 | #endif 65 | 66 | napi_status status = napi_get_uv_event_loop(env, &thread_loop); 67 | if (status != napi_ok) { 68 | ZITI_NODEJS_LOG(ERROR, "napi_get_uv_event_loop failed, status: %d", status); 69 | abort(); 70 | } 71 | 72 | // Expose some Ziti SDK functions to JavaScript 73 | expose_ziti_close(env, exports); 74 | expose_ziti_dial(env, exports); 75 | expose_ziti_enroll(env, exports); 76 | expose_ziti_sdk_version(env, exports); 77 | expose_ziti_init(env, exports); 78 | expose_ziti_listen(env, exports); 79 | expose_ziti_service_available(env, exports); 80 | expose_ziti_services_refresh(env, exports); 81 | expose_ziti_shutdown(env, exports); 82 | expose_ziti_write(env, exports); 83 | 84 | expose_ziti_https_request(env, exports); 85 | expose_ziti_https_request_data(env, exports); 86 | expose_ziti_https_request_end(env, exports); 87 | 88 | expose_ziti_websocket_connect(env, exports); 89 | expose_ziti_websocket_write(env, exports); 90 | 91 | expose_ziti_set_log_level(env, exports); 92 | 93 | return exports; 94 | } 95 | 96 | NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) -------------------------------------------------------------------------------- /src/ziti-nodejs.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef ZITI_ADD_ON_H 18 | #define ZITI_ADD_ON_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #define NAPI_EXPERIMENTAL 30 | #include 31 | 32 | #include 33 | #include 34 | #include "utils.h" 35 | 36 | 37 | #define NEWP(var, type) type *var = calloc(1, sizeof(type)) 38 | 39 | 40 | #ifdef _WIN32 41 | #define _NO_CRT_STDIO_INLINE 1 42 | /* Windows - set up dll import/export decorators. */ 43 | # if defined(BUILDING_UV_SHARED) 44 | /* Building shared library. */ 45 | # define UV_EXTERN __declspec(dllexport) 46 | # elif defined(USING_UV_SHARED) 47 | /* Using shared library. */ 48 | # define UV_EXTERN __declspec(dllimport) 49 | # else 50 | /* Building static library. */ 51 | # define UV_EXTERN /* nothing */ 52 | # endif 53 | 54 | 55 | #define strcasecmp _stricmp 56 | #define strncasecmp _strnicmp 57 | 58 | #if !defined (strndup_DEFINED) 59 | #define strndup_DEFINED 60 | static char* strndup(char* p, size_t len) { 61 | char *s = malloc(len + 1); 62 | strncpy(s, p, len); 63 | s[len] = '\0'; 64 | return s; 65 | } 66 | #endif // strndup_DEFINED 67 | 68 | 69 | #elif __GNUC__ >= 4 70 | # define UV_EXTERN __attribute__((visibility("default"))) 71 | #else 72 | # define UV_EXTERN /* nothing */ 73 | #endif 74 | 75 | # ifdef NODE_MAJOR_VERSION 76 | # if NODE_MAJOR_VERSION == 11 77 | UV_EXTERN int uv_gettimeofday(uv_timeval_t* tv); 78 | # else 79 | UV_EXTERN int uv_gettimeofday(uv_timeval64_t* tv); 80 | # endif 81 | # endif 82 | 83 | 84 | /** 85 | * 86 | */ 87 | typedef struct { 88 | napi_async_work work; 89 | napi_threadsafe_function tsfn_on_enroll; 90 | } EnrollAddonData; 91 | 92 | 93 | /** 94 | * 95 | */ 96 | typedef struct { 97 | bool isWebsocket; 98 | napi_async_work work; 99 | napi_threadsafe_function tsfn_on_connect; 100 | napi_threadsafe_function tsfn_on_data; 101 | napi_threadsafe_function tsfn_on_write; 102 | napi_threadsafe_function tsfn_on_service_available; 103 | } ConnAddonData; 104 | 105 | /** 106 | * 107 | */ 108 | typedef struct { 109 | char *service_name; 110 | int64_t js_arb_data; 111 | ziti_connection server; 112 | napi_async_work work; 113 | napi_threadsafe_function tsfn_on_listen; 114 | napi_threadsafe_function tsfn_on_listen_client; 115 | napi_threadsafe_function tsfn_on_listen_client_connect; 116 | napi_threadsafe_function tsfn_on_listen_client_data; 117 | } ListenAddonData; 118 | 119 | /** 120 | * 121 | */ 122 | typedef struct { 123 | napi_async_work work; 124 | napi_threadsafe_function tsfn_on_connect; 125 | napi_threadsafe_function tsfn_on_data; 126 | napi_threadsafe_function tsfn_on_write; 127 | tlsuv_src_t ziti_src; 128 | tlsuv_websocket_t ws; 129 | uv_connect_t req; 130 | uint32_t headers_array_length; 131 | char* header_name[100]; 132 | char* header_value[100]; 133 | char* service; 134 | } WSAddonData; 135 | 136 | 137 | // An item that will be passed into the JavaScript on_resp callback 138 | typedef struct HttpsRespItem { 139 | tlsuv_http_req_t *req; 140 | int code; 141 | char* status; 142 | tlsuv_http_hdr *headers; 143 | } HttpsRespItem; 144 | 145 | // An item that will be passed into the JavaScript on_resp_body callback 146 | typedef struct HttpsRespBodyItem { 147 | tlsuv_http_req_t *req; 148 | const void *body; 149 | ssize_t len; 150 | } HttpsRespBodyItem; 151 | 152 | // An item that will be passed into the JavaScript on_req_body callback 153 | typedef struct HttpsReqBodyItem { 154 | tlsuv_http_req_t *req; 155 | const void *body; 156 | ssize_t status; 157 | } HttpsReqBodyItem; 158 | 159 | 160 | typedef struct HttpsAddonData HttpsAddonData; 161 | 162 | typedef struct HttpsReq { 163 | tlsuv_http_req_t *req; 164 | bool on_resp_has_fired; 165 | int respCode; 166 | HttpsAddonData *addon_data; 167 | } HttpsReq; 168 | 169 | typedef struct { 170 | char* scheme_host_port; 171 | tlsuv_http_t client; 172 | tlsuv_src_t ziti_src; 173 | bool active; 174 | bool purge; 175 | } HttpsClient; 176 | 177 | struct HttpsAddonData { 178 | napi_env env; 179 | tlsuv_http_t client; 180 | tlsuv_http_req_t ziti_src; 181 | napi_threadsafe_function tsfn_on_req; 182 | napi_threadsafe_function tsfn_on_resp; 183 | napi_threadsafe_function tsfn_on_resp_body; 184 | napi_threadsafe_function tsfn_on_req_body; 185 | HttpsRespItem* item; 186 | HttpsReq* httpsReq; 187 | uv_work_t uv_req; 188 | bool haveURL; 189 | char* service; 190 | char* scheme_host_port; 191 | char* method; 192 | char* path; 193 | uint32_t headers_array_length; 194 | char* header_name[100]; 195 | char* header_value[100]; 196 | HttpsClient* httpsClient; 197 | } ; 198 | 199 | 200 | #ifdef __cplusplus 201 | extern "C" { 202 | #endif 203 | 204 | extern ziti_context ztx; 205 | extern uv_loop_t *thread_loop; 206 | 207 | extern uv_mutex_t client_pool_lock; 208 | 209 | // extern void set_signal_handler(); 210 | 211 | extern void expose_ziti_close(napi_env env, napi_value exports); 212 | extern void expose_ziti_dial(napi_env env, napi_value exports); 213 | extern void expose_ziti_enroll(napi_env env, napi_value exports); 214 | extern void expose_ziti_sdk_version(napi_env env, napi_value exports); 215 | extern void expose_ziti_init(napi_env env, napi_value exports); 216 | extern void expose_ziti_listen(napi_env env, napi_value exports); 217 | extern void expose_ziti_service_available(napi_env env, napi_value exports); 218 | extern void expose_ziti_services_refresh(napi_env env, napi_value exports); 219 | extern void expose_ziti_set_log_level(napi_env env, napi_value exports); 220 | extern void expose_ziti_shutdown(napi_env env, napi_value exports); 221 | extern void expose_ziti_write(napi_env env, napi_value exports); 222 | extern void expose_ziti_https_request(napi_env env, napi_value exports); 223 | extern void expose_ziti_https_request_data(napi_env env, napi_value exports); 224 | extern void expose_ziti_https_request_end(napi_env env, napi_value exports); 225 | extern void expose_ziti_websocket_connect(napi_env env, napi_value exports); 226 | extern void expose_ziti_websocket_write(napi_env env, napi_value exports); 227 | 228 | // 229 | extern int tlsuv_websocket_init_with_src (uv_loop_t *loop, tlsuv_websocket_t *ws, tlsuv_src_t *src); 230 | 231 | extern void track_service_to_hostname(const char* service_name, char* hostname, int port); 232 | 233 | #ifdef __cplusplus 234 | } 235 | #endif 236 | 237 | #endif /* ZITI_ADD_ON_H */ -------------------------------------------------------------------------------- /src/ziti_close.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "ziti-nodejs.h" 18 | #include 19 | 20 | 21 | /** 22 | * 23 | */ 24 | napi_value _ziti_close(napi_env env, const napi_callback_info info) { 25 | napi_status status; 26 | size_t argc = 1; 27 | napi_value args[1]; 28 | napi_value jsRetval; 29 | 30 | status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); 31 | if (status != napi_ok) { 32 | napi_throw_error(env, NULL, "Failed to parse arguments"); 33 | } 34 | 35 | if (argc < 1) { 36 | napi_throw_error(env, "EINVAL", "Too few arguments"); 37 | return NULL; 38 | } 39 | 40 | // Obtain ziti_connection 41 | int64_t js_conn; 42 | status = napi_get_value_int64(env, args[0], &js_conn); 43 | if (status != napi_ok) { 44 | napi_throw_error(env, NULL, "Failed to get Conn"); 45 | } 46 | ziti_connection conn = (ziti_connection)js_conn; 47 | 48 | // Now, call the C-SDK to close the connection 49 | ZITI_NODEJS_LOG(DEBUG, "calling ziti_close for conn=%p", conn); 50 | ziti_close(conn, NULL); 51 | 52 | status = napi_create_int32(env, 0, &jsRetval); 53 | if (status != napi_ok) { 54 | napi_throw_error(env, NULL, "Unable to create return value"); 55 | } 56 | 57 | return jsRetval; 58 | } 59 | 60 | 61 | /** 62 | * 63 | */ 64 | void expose_ziti_close(napi_env env, napi_value exports) { 65 | napi_status status; 66 | napi_value fn; 67 | 68 | status = napi_create_function(env, NULL, 0, _ziti_close, NULL, &fn); 69 | if (status != napi_ok) { 70 | napi_throw_error(env, NULL, "Unable to wrap native function '_ziti_close"); 71 | } 72 | 73 | status = napi_set_named_property(env, exports, "ziti_close", fn); 74 | if (status != napi_ok) { 75 | napi_throw_error(env, NULL, "Unable to populate exports for 'ziti_close"); 76 | } 77 | 78 | } 79 | 80 | -------------------------------------------------------------------------------- /src/ziti_dial.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "ziti-nodejs.h" 18 | #include 19 | 20 | // An item that will be generated here and passed into the JavaScript on_data callback 21 | typedef struct OnDataItem { 22 | 23 | const unsigned char *buf; 24 | int len; 25 | 26 | } OnDataItem; 27 | 28 | /** 29 | * This function is responsible for calling the JavaScript 'connect' callback function 30 | * that was specified when the ziti_dial(...) was called from JavaScript. 31 | */ 32 | static void CallJs_on_connect(napi_env env, napi_value js_cb, void* context, void* data) { 33 | napi_status status; 34 | 35 | // This parameter is not used. 36 | (void) context; 37 | 38 | // env and js_cb may both be NULL if Node.js is in its cleanup phase, and 39 | // items are left over from earlier thread-safe calls from the worker thread. 40 | // When env is NULL, we simply skip over the call into Javascript 41 | if (env != NULL) { 42 | napi_value undefined, js_conn; 43 | 44 | ZITI_NODEJS_LOG(INFO, "data: %p", data); 45 | 46 | // Convert the ziti_connection to a napi_value. 47 | if (NULL != data) { 48 | // Retrieve the ziti_connection from the item created by the worker thread. 49 | ziti_connection conn = *(ziti_connection*)data; 50 | status = napi_create_int64(env, (int64_t)conn, &js_conn); 51 | if (status != napi_ok) { 52 | napi_throw_error(env, NULL, "Unable to napi_create_int64"); 53 | } 54 | } else { 55 | status = napi_get_undefined(env, &js_conn) == napi_ok; 56 | if (status != napi_ok) { 57 | napi_throw_error(env, NULL, "Unable to napi_get_undefined (1)"); 58 | } 59 | } 60 | 61 | // Retrieve the JavaScript `undefined` value so we can use it as the `this` 62 | // value of the JavaScript function call. 63 | status = napi_get_undefined(env, &undefined); 64 | if (status != napi_ok) { 65 | napi_throw_error(env, NULL, "Unable to napi_get_undefined (2)"); 66 | } 67 | 68 | ZITI_NODEJS_LOG(INFO, "calling JS callback..."); 69 | 70 | // Call the JavaScript function and pass it the ziti_connection 71 | status = napi_call_function( 72 | env, 73 | undefined, 74 | js_cb, 75 | 1, 76 | &js_conn, 77 | NULL 78 | ); 79 | ZITI_NODEJS_LOG(INFO, "returned from JS callback..."); 80 | if (status != napi_ok) { 81 | napi_throw_error(env, NULL, "Unable to napi_call_function"); 82 | } 83 | } 84 | } 85 | 86 | 87 | /** 88 | * This function is responsible for calling the JavaScript 'data' callback function 89 | * that was specified when the ziti_dial(...) was called from JavaScript. 90 | */ 91 | static void CallJs_on_data(napi_env env, napi_value js_cb, void* context, void* data) { 92 | napi_status status; 93 | 94 | // This parameter is not used. 95 | (void) context; 96 | 97 | // Retrieve the OnDataItem created by the worker thread. 98 | OnDataItem* item = (OnDataItem*)data; 99 | 100 | // env and js_cb may both be NULL if Node.js is in its cleanup phase, and 101 | // items are left over from earlier thread-safe calls from the worker thread. 102 | // When env is NULL, we simply skip over the call into Javascript and free the 103 | // items. 104 | if (env != NULL) { 105 | napi_value undefined, js_buffer; 106 | void* result_data; 107 | 108 | // Convert the buffer to a napi_value. 109 | status = napi_create_buffer_copy(env, 110 | item->len, 111 | (const void*)item->buf, 112 | (void**)&result_data, 113 | &js_buffer); 114 | if (status != napi_ok) { 115 | napi_throw_error(env, NULL, "Unable to napi_create_buffer_copy"); 116 | } 117 | 118 | // Retrieve the JavaScript `undefined` value so we can use it as the `this` 119 | // value of the JavaScript function call. 120 | status = napi_get_undefined(env, &undefined); 121 | if (status != napi_ok) { 122 | napi_throw_error(env, NULL, "Unable to napi_get_undefined (3)"); 123 | } 124 | 125 | // Call the JavaScript function and pass it the data 126 | status = napi_call_function( 127 | env, 128 | undefined, 129 | js_cb, 130 | 1, 131 | &js_buffer, 132 | NULL 133 | ); 134 | if (status != napi_ok) { 135 | napi_throw_error(env, NULL, "Unable to napi_call_function"); 136 | } 137 | } 138 | } 139 | 140 | 141 | 142 | /** 143 | * This function is the callback invoked by the C-SDK when data arrives on the connection. 144 | */ 145 | long on_data(struct ziti_conn *conn, const unsigned char *buf, long len) { 146 | napi_status status; 147 | 148 | ConnAddonData* addon_data = (ConnAddonData*) ziti_conn_data(conn); 149 | 150 | ZITI_NODEJS_LOG(INFO, "len: %zd, conn: %p", len, conn); 151 | 152 | if (len == ZITI_EOF) { 153 | if (addon_data->isWebsocket) { 154 | ZITI_NODEJS_LOG(DEBUG, "skipping ziti_close on ZITI_EOF due to isWebsocket=true"); 155 | return 0; 156 | } else { 157 | ziti_close(conn, NULL); 158 | return 0; 159 | } 160 | } 161 | else if (len < 0) { 162 | ziti_close(conn, NULL); 163 | return 0; 164 | } 165 | else { 166 | 167 | OnDataItem* item = memset(malloc(sizeof(*item)), 0, sizeof(*item)); 168 | item->buf = buf; 169 | item->buf = calloc(1, len); 170 | memcpy((void*)item->buf, buf, len); 171 | item->len = len; 172 | 173 | // if (addon_data->isWebsocket) { 174 | // hexDump("on_data", item->buf, item->len); 175 | // } 176 | 177 | // Initiate the call into the JavaScript callback. 178 | // The call into JavaScript will not have happened 179 | // when this function returns, but it will be queued. 180 | status = napi_call_threadsafe_function( 181 | addon_data->tsfn_on_data, 182 | item, // Send the data we received from the service on over to the JS callback 183 | napi_tsfn_blocking); 184 | if (status != napi_ok) { 185 | ZITI_NODEJS_LOG(ERROR, "Unable to napi_call_threadsafe_function"); 186 | } 187 | 188 | return len; 189 | } 190 | } 191 | 192 | 193 | /** 194 | * 195 | */ 196 | void on_connect(ziti_connection conn, int status) { 197 | napi_status nstatus; 198 | 199 | 200 | ConnAddonData* addon_data = (ConnAddonData*) ziti_conn_data(conn); 201 | ziti_connection* the_conn = NULL; 202 | 203 | ZITI_NODEJS_LOG(DEBUG, "conn: %p, status: %o, isWebsocket: %o", conn, status, addon_data->isWebsocket); 204 | 205 | if (status == ZITI_OK) { 206 | 207 | // Save the 'ziti_connection' to the heap. The JavaScript marshaller (CallJs) 208 | // will free this item after having sent it to JavaScript. 209 | the_conn = malloc(sizeof(ziti_connection)); 210 | *the_conn = conn; 211 | } 212 | 213 | ZITI_NODEJS_LOG(DEBUG, "the_conn: %p", the_conn); 214 | 215 | // Initiate the call into the JavaScript callback. 216 | // The call into JavaScript will not have happened 217 | // when this function returns, but it will be queued. 218 | nstatus = napi_call_threadsafe_function( 219 | addon_data->tsfn_on_connect, 220 | the_conn, // Send the ziti_connection over to the JS callback 221 | napi_tsfn_blocking); 222 | if (nstatus != napi_ok) { 223 | ZITI_NODEJS_LOG(ERROR, "Unable to napi_call_threadsafe_function"); 224 | } 225 | } 226 | 227 | 228 | /** 229 | * 230 | */ 231 | napi_value _ziti_dial(napi_env env, const napi_callback_info info) { 232 | 233 | napi_status status; 234 | size_t argc = 4; 235 | napi_value args[4]; 236 | status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); 237 | if (status != napi_ok) { 238 | napi_throw_error(env, NULL, "Failed to parse arguments"); 239 | } 240 | 241 | if (argc < 4) { 242 | napi_throw_error(env, "EINVAL", "Too few arguments"); 243 | return NULL; 244 | } 245 | 246 | // Obtain service name 247 | size_t result; 248 | char ServiceName[256]; //TODO: make this smarter 249 | status = napi_get_value_string_utf8(env, args[0], ServiceName, 256, &result); 250 | if (status != napi_ok) { 251 | napi_throw_error(env, NULL, "Failed to get Service Name"); 252 | } 253 | ZITI_NODEJS_LOG(DEBUG, "ServiceName: %s", ServiceName); 254 | 255 | // Obtain isWebsocket flag 256 | bool isWebsocket = false; 257 | status = napi_get_value_bool(env, args[1], &isWebsocket); 258 | if (status != napi_ok) { 259 | napi_throw_error(env, NULL, "Failed to get isWebsocket flag"); 260 | } 261 | ZITI_NODEJS_LOG(DEBUG, "isWebsocket is: %o", isWebsocket); 262 | 263 | // Obtain ptr to JS 'connect' callback function 264 | napi_value js_connect_cb = args[2]; 265 | napi_value work_name_connect; 266 | 267 | ConnAddonData* addon_data = memset(malloc(sizeof(*addon_data)), 0, sizeof(*addon_data)); 268 | 269 | addon_data->isWebsocket = isWebsocket; 270 | 271 | // Create a string to describe this asynchronous operation. 272 | status = napi_create_string_utf8( 273 | env, 274 | "N-API on_connect", 275 | NAPI_AUTO_LENGTH, 276 | &work_name_connect); 277 | if (status != napi_ok) { 278 | napi_throw_error(env, NULL, "Unable to napi_create_string_utf8"); 279 | } 280 | 281 | // Convert the callback retrieved from JavaScript into a thread-safe function (tsfn) 282 | // which we can call from a worker thread. 283 | status = napi_create_threadsafe_function( 284 | env, 285 | js_connect_cb, 286 | NULL, 287 | work_name_connect, 288 | 0, 289 | 1, 290 | NULL, 291 | NULL, 292 | NULL, 293 | CallJs_on_connect, 294 | &(addon_data->tsfn_on_connect)); 295 | if (status != napi_ok) { 296 | napi_throw_error(env, NULL, "Unable to napi_create_threadsafe_function"); 297 | } 298 | 299 | // Obtain ptr to JS 'data' callback function 300 | napi_value js_data_cb = args[3]; 301 | napi_value work_name_data; 302 | 303 | // Create a string to describe this asynchronous operation. 304 | status = napi_create_string_utf8( 305 | env, 306 | "N-API on_data", 307 | NAPI_AUTO_LENGTH, 308 | &work_name_data); 309 | if (status != napi_ok) { 310 | napi_throw_error(env, NULL, "Unable to napi_create_string_utf8"); 311 | } 312 | 313 | // Convert the callback retrieved from JavaScript into a thread-safe function (tsfn) 314 | // which we can call from a worker thread. 315 | status = napi_create_threadsafe_function( 316 | env, 317 | js_data_cb, 318 | NULL, 319 | work_name_data, 320 | 0, 321 | 1, 322 | NULL, 323 | NULL, 324 | NULL, 325 | CallJs_on_data, 326 | &(addon_data->tsfn_on_data)); 327 | if (status != napi_ok) { 328 | napi_throw_error(env, NULL, "Unable to napi_create_threadsafe_function"); 329 | } 330 | 331 | // Init a Ziti connection object, and attach our add-on data to it so we can 332 | // pass context around between our callbacks, as propagate it all the way out 333 | // to the JavaScript callbacks 334 | ziti_connection conn; 335 | int rc = ziti_conn_init(ztx, &conn, addon_data); 336 | if (rc != ZITI_OK) { 337 | napi_throw_error(env, NULL, "failure in 'ziti_conn_init"); 338 | } 339 | 340 | 341 | // Connect to the service 342 | ZITI_NODEJS_LOG(DEBUG, "calling ziti_dial: %p", ztx); 343 | rc = ziti_dial(conn, ServiceName, on_connect, on_data); 344 | if (rc != ZITI_OK) { 345 | napi_throw_error(env, NULL, "failure in 'ziti_dial"); 346 | } 347 | ZITI_NODEJS_LOG(DEBUG, "returned from ziti_dial: %p", ztx); 348 | 349 | return NULL; 350 | } 351 | 352 | 353 | 354 | /** 355 | * 356 | */ 357 | void expose_ziti_dial(napi_env env, napi_value exports) { 358 | napi_status status; 359 | napi_value fn; 360 | 361 | status = napi_create_function(env, NULL, 0, _ziti_dial, NULL, &fn); 362 | if (status != napi_ok) { 363 | napi_throw_error(env, NULL, "Unable to wrap native function '_ziti_dial"); 364 | } 365 | 366 | status = napi_set_named_property(env, exports, "ziti_dial", fn); 367 | if (status != napi_ok) { 368 | napi_throw_error(env, NULL, "Unable to populate exports for 'ziti_dial"); 369 | } 370 | 371 | } 372 | 373 | -------------------------------------------------------------------------------- /src/ziti_enroll.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "ziti-nodejs.h" 18 | 19 | 20 | // An item that will be generated here and passed into the JavaScript enroll callback 21 | typedef struct EnrollItem { 22 | 23 | unsigned char *json_salvo; 24 | int status; 25 | char *err; 26 | 27 | } EnrollItem; 28 | 29 | 30 | /** 31 | * This function is responsible for calling the JavaScript callback function 32 | * that was specified when the ziti_enroll(...) was called from JavaScript. 33 | */ 34 | static void CallJs_on_enroll(napi_env env, napi_value js_cb, void* context, void* data) { 35 | 36 | napi_status status; 37 | 38 | ZITI_NODEJS_LOG(DEBUG, "entered"); 39 | 40 | // This parameter is not used. 41 | (void) context; 42 | 43 | // Retrieve the EnrollItem created by the worker thread. 44 | EnrollItem* item = (EnrollItem*)data; 45 | 46 | ZITI_NODEJS_LOG(DEBUG, "item->json_salvo: %s", item->json_salvo); 47 | ZITI_NODEJS_LOG(DEBUG, "item->status: %d", item->status); 48 | ZITI_NODEJS_LOG(DEBUG, "item->err: %s", item->err); 49 | 50 | // env and js_cb may both be NULL if Node.js is in its cleanup phase, and 51 | // items are left over from earlier thread-safe calls from the worker thread. 52 | // When env is NULL, we simply skip over the call into Javascript 53 | if (env != NULL) { 54 | 55 | napi_value undefined; 56 | 57 | // Retrieve the JavaScript `undefined` value so we can use it as the `this` 58 | // value of the JavaScript function call. 59 | assert(napi_get_undefined(env, &undefined) == napi_ok); 60 | 61 | // const obj = {} 62 | napi_value js_enroll_item, js_json_salvo, js_status, js_err; 63 | status = napi_create_object(env, &js_enroll_item); 64 | if (status != napi_ok) { 65 | napi_throw_error(env, NULL, "Unable to napi_create_object"); 66 | } 67 | 68 | // obj.identity = identity 69 | if (NULL != item->json_salvo) { 70 | napi_create_string_utf8(env, (const char*)item->json_salvo, NAPI_AUTO_LENGTH, &js_json_salvo); 71 | napi_set_named_property(env, js_enroll_item, "identity", js_json_salvo); 72 | } 73 | 74 | // obj.status = status 75 | napi_create_int64(env, (int64_t)item->status, &js_status); 76 | napi_set_named_property(env, js_enroll_item, "status", js_status); 77 | 78 | // obj.err = err 79 | if (NULL != item->err) { 80 | napi_create_string_utf8(env, (const char*)item->err, NAPI_AUTO_LENGTH, &js_err); 81 | napi_set_named_property(env, js_enroll_item, "err", js_err); 82 | } 83 | 84 | 85 | // Call the JavaScript function and pass it the EnrollItem 86 | napi_value global; 87 | status = napi_get_global(env, &global); 88 | 89 | status = napi_call_function( 90 | env, 91 | global, 92 | js_cb, 93 | 1, 94 | &js_enroll_item, 95 | NULL 96 | ); 97 | 98 | if (status != napi_ok) { 99 | napi_throw_error(env, "EINVAL", "failure to invoke JS callback"); 100 | } 101 | 102 | } 103 | } 104 | 105 | 106 | /** 107 | * 108 | */ 109 | void on_ziti_enroll(const ziti_config *cfg, int status, const char *err, void *ctx) { 110 | napi_status nstatus; 111 | 112 | ZITI_NODEJS_LOG(DEBUG, "\nstatus: %d, \nerr: %s,\nctx: %p", status, err, ctx); 113 | 114 | EnrollAddonData* addon_data = (EnrollAddonData*)ctx; 115 | 116 | EnrollItem* item = memset(malloc(sizeof(*item)), 0, sizeof(*item)); 117 | 118 | item->status = status; 119 | 120 | if (NULL != err) { 121 | item->err = calloc(1, strlen(err) + 1); 122 | strcpy(item->err, err); 123 | } else { 124 | item->err = NULL; 125 | } 126 | 127 | if (status == ZITI_OK) { 128 | size_t len; 129 | char *output_buf = ziti_config_to_json(cfg, 0, &len); 130 | item->json_salvo = calloc(1, len + 1); 131 | strcpy(item->json_salvo, output_buf); 132 | } 133 | 134 | // Initiate the call into the JavaScript callback. 135 | // The call into JavaScript will not have happened 136 | // when this function returns, but it will be queued. 137 | nstatus = napi_call_threadsafe_function( 138 | addon_data->tsfn_on_enroll, 139 | item, 140 | napi_tsfn_blocking); 141 | if (nstatus != napi_ok) { 142 | ZITI_NODEJS_LOG(ERROR, "Unable to napi_call_threadsafe_function"); 143 | } 144 | 145 | } 146 | 147 | 148 | 149 | 150 | /** 151 | * 152 | */ 153 | napi_value _ziti_enroll(napi_env env, const napi_callback_info info) { 154 | napi_status status; 155 | napi_value jsRetval; 156 | napi_valuetype js_cb_type; 157 | 158 | ziti_log_init(thread_loop, ZITI_LOG_DEFAULT_LEVEL, NULL); 159 | 160 | ZITI_NODEJS_LOG(DEBUG, "entered"); 161 | 162 | size_t argc = 2; 163 | napi_value args[2]; 164 | status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); 165 | if (status != napi_ok) { 166 | napi_throw_error(env, NULL, "Failed to parse arguments"); 167 | } 168 | 169 | if (argc < 2) { 170 | ZITI_NODEJS_LOG(DEBUG, "Too few arguments"); 171 | napi_throw_error(env, "EINVAL", "Too few arguments"); 172 | return NULL; 173 | } 174 | 175 | // Obtain location of JWT file 176 | size_t result; 177 | char JWTFileName[256]; 178 | status = napi_get_value_string_utf8(env, args[0], JWTFileName, 256, &result); 179 | 180 | // Obtain ptr to JS callback function 181 | // napi_value js_cb = args[1]; 182 | napi_typeof(env, args[1], &js_cb_type); 183 | if (js_cb_type != napi_function) { 184 | ZITI_NODEJS_LOG(DEBUG, "args[1] is NOT a napi_function"); 185 | } else { 186 | ZITI_NODEJS_LOG(DEBUG, "args[1] IS a napi_function"); 187 | } 188 | napi_value work_name; 189 | 190 | EnrollAddonData* addon_data = memset(malloc(sizeof(*addon_data)), 0, sizeof(*addon_data)); 191 | 192 | // Create a string to describe this asynchronous operation. 193 | assert(napi_create_string_utf8( 194 | env, 195 | "N-API on_ziti_enroll", 196 | NAPI_AUTO_LENGTH, 197 | &work_name) == napi_ok); 198 | 199 | // Convert the callback retrieved from JavaScript into a thread-safe function (tsfn) 200 | // which we can call from a worker thread. 201 | status = napi_create_threadsafe_function( 202 | env, 203 | args[1], 204 | NULL, 205 | work_name, 206 | 0, 207 | 1, 208 | NULL, 209 | NULL, 210 | NULL, 211 | CallJs_on_enroll, 212 | &(addon_data->tsfn_on_enroll)); 213 | if (status != napi_ok) { 214 | napi_throw_error(env, NULL, "Unable to napi_create_threadsafe_function"); 215 | } 216 | 217 | 218 | // Initiate the enrollment 219 | ziti_enroll_opts opts = {0}; 220 | opts.token = JWTFileName; 221 | int rc = ziti_enroll(&opts, thread_loop, on_ziti_enroll, addon_data); 222 | 223 | status = napi_create_int32(env, rc, &jsRetval); 224 | if (status != napi_ok) { 225 | napi_throw_error(env, NULL, "Unable to create return value"); 226 | } 227 | 228 | return jsRetval; 229 | } 230 | 231 | 232 | void expose_ziti_enroll(napi_env env, napi_value exports) { 233 | napi_status status; 234 | napi_value fn; 235 | 236 | status = napi_create_function(env, NULL, 0, _ziti_enroll, NULL, &fn); 237 | if (status != napi_ok) { 238 | napi_throw_error(env, NULL, "Unable to wrap native function '_ziti_enroll"); 239 | } 240 | 241 | status = napi_set_named_property(env, exports, "ziti_enroll", fn); 242 | if (status != napi_ok) { 243 | napi_throw_error(env, NULL, "Unable to populate exports for 'ziti_enroll"); 244 | } 245 | 246 | } 247 | -------------------------------------------------------------------------------- /src/ziti_sdk_version.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "ziti-nodejs.h" 18 | 19 | #include 20 | 21 | 22 | /** 23 | * 24 | */ 25 | napi_value _ziti_sdk_version(napi_env env, const napi_callback_info info) { 26 | napi_value jsRetval = NULL; 27 | napi_status status = napi_generic_failure; 28 | 29 | const ziti_version *ver = ziti_get_version(); 30 | 31 | status = napi_create_string_utf8(env, ver->version, NAPI_AUTO_LENGTH, &jsRetval); 32 | if (status != napi_ok) return NULL; 33 | 34 | return jsRetval; 35 | } 36 | 37 | 38 | /** 39 | * 40 | */ 41 | void expose_ziti_sdk_version(napi_env env, napi_value exports) { 42 | napi_status status; 43 | napi_value fn; 44 | 45 | status = napi_create_function(env, NULL, 0, _ziti_sdk_version, NULL, &fn); 46 | if (status != napi_ok) { 47 | napi_throw_error(env, NULL, "Unable to wrap native function '_ziti_sdk_version"); 48 | } 49 | 50 | status = napi_set_named_property(env, exports, "ziti_sdk_version", fn); 51 | if (status != napi_ok) { 52 | napi_throw_error(env, NULL, "Unable to populate exports for 'ziti_sdk_version"); 53 | } 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /src/ziti_service_available.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "ziti-nodejs.h" 18 | #include 19 | 20 | 21 | typedef struct { 22 | napi_async_work work; 23 | napi_threadsafe_function tsfn; 24 | } AddonData; 25 | 26 | // An item that will be generated here and passed into the JavaScript service_available callback 27 | typedef struct ServiceAvailableItem { 28 | ssize_t status; 29 | int permissions; 30 | } ServiceAvailableItem; 31 | 32 | 33 | /** 34 | * This function is responsible for calling the JavaScript 'service_available' callback function 35 | * that was specified when the ziti_service_available(...) was called from JavaScript. 36 | */ 37 | static void CallJs_on_service_available(napi_env env, napi_value js_cb, void* context, void* data) { 38 | napi_status status; 39 | 40 | // This parameter is not used. 41 | (void) context; 42 | 43 | // Retrieve the ServiceAvailableItem created by the worker thread. 44 | ServiceAvailableItem* item = (ServiceAvailableItem*)data; 45 | 46 | // env and js_cb may both be NULL if Node.js is in its cleanup phase, and 47 | // items are left over from earlier thread-safe calls from the worker thread. 48 | // When env is NULL, we simply skip over the call into Javascript and free the 49 | // items. 50 | if (env != NULL) { 51 | 52 | napi_value undefined; 53 | 54 | // const obj = {} 55 | napi_value js_service_available_item, js_status, js_permissions; 56 | status = napi_create_object(env, &js_service_available_item); 57 | if (status != napi_ok) { 58 | napi_throw_error(env, NULL, "Unable to napi_create_object"); 59 | } 60 | 61 | // obj.status = status 62 | status = napi_create_int64(env, (int64_t)item->status, &js_status); 63 | if (status != napi_ok) { 64 | napi_throw_error(env, NULL, "Unable to napi_create_int64"); 65 | } 66 | status = napi_set_named_property(env, js_service_available_item, "status", js_status); 67 | if (status != napi_ok) { 68 | napi_throw_error(env, NULL, "Unable to napi_set_named_property"); 69 | } 70 | 71 | // obj.permissions = permissions 72 | status = napi_create_int64(env, (int64_t)item->permissions, &js_permissions); 73 | if (status != napi_ok) { 74 | napi_throw_error(env, NULL, "Unable to napi_create_int64"); 75 | } 76 | status = napi_set_named_property(env, js_service_available_item, "permissions", js_permissions); 77 | if (status != napi_ok) { 78 | napi_throw_error(env, NULL, "Unable to napi_set_named_property"); 79 | } 80 | 81 | // Retrieve the JavaScript `undefined` value so we can use it as the `this` 82 | // value of the JavaScript function call. 83 | status = napi_get_undefined(env, &undefined); 84 | if (status != napi_ok) { 85 | napi_throw_error(env, NULL, "Unable to napi_get_undefined (4)"); 86 | } 87 | 88 | // Call the JavaScript function and pass it the ServiceAvailableItem 89 | status = napi_call_function( 90 | env, 91 | undefined, 92 | js_cb, 93 | 1, 94 | &js_service_available_item, 95 | NULL); 96 | if (status != napi_ok) { 97 | napi_throw_error(env, NULL, "Unable to napi_call_function"); 98 | } 99 | } 100 | } 101 | 102 | 103 | /** 104 | * 105 | */ 106 | static void on_service_available(ziti_context nf_ctx, const ziti_service* service, int status, void *ctx) { 107 | napi_status nstatus; 108 | 109 | AddonData* addon_data = (AddonData*)ctx; 110 | 111 | ServiceAvailableItem* item = memset(malloc(sizeof(*item)), 0, sizeof(*item)); 112 | item->status = status; 113 | 114 | if (ZITI_OK == status) { 115 | item->permissions = service->perm_flags; 116 | } 117 | 118 | // Initiate the call into the JavaScript callback. 119 | // The call into JavaScript will not have happened 120 | // when this function returns, but it will be queued. 121 | nstatus = napi_call_threadsafe_function( 122 | addon_data->tsfn, 123 | item, 124 | napi_tsfn_blocking); 125 | if (nstatus != napi_ok) { 126 | ZITI_NODEJS_LOG(ERROR, "Unable to napi_call_threadsafe_function"); 127 | } 128 | } 129 | 130 | /** 131 | * 132 | */ 133 | napi_value _ziti_service_available(napi_env env, const napi_callback_info info) { 134 | napi_status status; 135 | size_t argc = 2; 136 | napi_value args[2]; 137 | napi_value jsRetval; 138 | 139 | status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); 140 | if (status != napi_ok) { 141 | napi_throw_error(env, NULL, "Failed to parse arguments"); 142 | } 143 | 144 | if (argc < 2) { 145 | napi_throw_error(env, "EINVAL", "Too few arguments"); 146 | return NULL; 147 | } 148 | 149 | // Obtain service name 150 | size_t result; 151 | char* ServiceName = malloc(256); // TODO: make this smarter 152 | status = napi_get_value_string_utf8(env, args[0], ServiceName, 100, &result); 153 | if (status != napi_ok) { 154 | napi_throw_error(env, NULL, "Service Name not provided"); 155 | } 156 | 157 | // Obtain ptr to JS 'service_available' callback function 158 | napi_value js_write_cb = args[1]; 159 | napi_value work_name; 160 | AddonData* addon_data = malloc(sizeof(AddonData)); 161 | 162 | // Create a string to describe this asynchronous operation. 163 | status = napi_create_string_utf8( 164 | env, 165 | "N-API on_service_available", 166 | NAPI_AUTO_LENGTH, 167 | &work_name); 168 | if (status != napi_ok) { 169 | napi_throw_error(env, NULL, "Unable to napi_create_string_utf8"); 170 | } 171 | 172 | // Convert the callback retrieved from JavaScript into a thread-safe function (tsfn) 173 | // which we can call from a worker thread. 174 | status = napi_create_threadsafe_function( 175 | env, 176 | js_write_cb, 177 | NULL, 178 | work_name, 179 | 0, 180 | 1, 181 | NULL, 182 | NULL, 183 | NULL, 184 | CallJs_on_service_available, 185 | &(addon_data->tsfn)); 186 | if (status != napi_ok) { 187 | napi_throw_error(env, NULL, "Unable to napi_call_threadsafe_function"); 188 | } 189 | 190 | // Now, call the C-SDK to see if the service name is present 191 | ziti_service_available(ztx, ServiceName, on_service_available, addon_data); 192 | 193 | status = napi_create_int32(env, 0 /* always succeed here, it is the cb that tells the real tale */, &jsRetval); 194 | if (status != napi_ok) { 195 | napi_throw_error(env, NULL, "Unable to create return value"); 196 | } 197 | 198 | free(ServiceName); 199 | 200 | return jsRetval; 201 | } 202 | 203 | 204 | /** 205 | * 206 | */ 207 | void expose_ziti_service_available(napi_env env, napi_value exports) { 208 | napi_status status; 209 | napi_value fn; 210 | 211 | status = napi_create_function(env, NULL, 0, _ziti_service_available, NULL, &fn); 212 | if (status != napi_ok) { 213 | napi_throw_error(env, NULL, "Unable to wrap native function '_ziti_service_available"); 214 | } 215 | 216 | status = napi_set_named_property(env, exports, "ziti_service_available", fn); 217 | if (status != napi_ok) { 218 | napi_throw_error(env, NULL, "Unable to populate exports for 'ziti_service_available"); 219 | } 220 | 221 | } 222 | 223 | -------------------------------------------------------------------------------- /src/ziti_services_refresh.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "ziti-nodejs.h" 18 | #include 19 | 20 | void ziti_services_refresh(ziti_context ztx, bool now); 21 | 22 | 23 | /** 24 | * 25 | */ 26 | napi_value _ziti_services_refresh(napi_env env, const napi_callback_info info) { 27 | napi_status status; 28 | napi_value jsRetval; 29 | 30 | ZITI_NODEJS_LOG(INFO, "ziti_services_refresh initiated"); 31 | 32 | // Now, call the C-SDK to refresh the services list 33 | ziti_services_refresh(ztx, true); 34 | 35 | status = napi_create_int32(env, 0 /* always succeed here */, &jsRetval); 36 | if (status != napi_ok) { 37 | napi_throw_error(env, NULL, "Unable to create return value"); 38 | } 39 | 40 | return jsRetval; 41 | } 42 | 43 | 44 | /** 45 | * 46 | */ 47 | void expose_ziti_services_refresh(napi_env env, napi_value exports) { 48 | napi_status status; 49 | napi_value fn; 50 | 51 | status = napi_create_function(env, NULL, 0, _ziti_services_refresh, NULL, &fn); 52 | if (status != napi_ok) { 53 | napi_throw_error(env, NULL, "Unable to wrap native function '_ziti_services_refresh"); 54 | } 55 | 56 | status = napi_set_named_property(env, exports, "ziti_services_refresh", fn); 57 | if (status != napi_ok) { 58 | napi_throw_error(env, NULL, "Unable to populate exports for 'ziti_services_refresh"); 59 | } 60 | 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/ziti_set_log_level.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "ziti-nodejs.h" 18 | 19 | 20 | /** 21 | * 22 | */ 23 | napi_value _ziti_set_log_level(napi_env env, const napi_callback_info info) { 24 | napi_status status; 25 | napi_value jsRetval; 26 | 27 | size_t argc = 1; 28 | napi_value args[1]; 29 | status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); 30 | if (status != napi_ok) { 31 | napi_throw_error(env, NULL, "Failed to parse arguments"); 32 | } 33 | 34 | if (argc < 1) { 35 | napi_throw_error(env, "EINVAL", "Too few arguments"); 36 | return NULL; 37 | } 38 | 39 | int64_t js_log_level; 40 | status = napi_get_value_int64(env, args[0], &js_log_level); 41 | if (status != napi_ok) { 42 | napi_throw_error(env, NULL, "Failed to get logLevel"); 43 | } 44 | 45 | ZITI_NODEJS_LOG(DEBUG, "js_log_level: %lld", (long long)js_log_level); 46 | 47 | ziti_nodejs_debug_level = js_log_level; 48 | ziti_log_set_level(js_log_level, NULL); 49 | 50 | status = napi_create_int32(env, 0, &jsRetval); 51 | if (status != napi_ok) { 52 | napi_throw_error(env, NULL, "Unable to create return value"); 53 | } 54 | 55 | return jsRetval; 56 | } 57 | 58 | 59 | void expose_ziti_set_log_level(napi_env env, napi_value exports) { 60 | napi_status status; 61 | napi_value fn; 62 | 63 | status = napi_create_function(env, NULL, 0, _ziti_set_log_level, NULL, &fn); 64 | if (status != napi_ok) { 65 | napi_throw_error(env, NULL, "Unable to wrap native function '_ziti_set_log_level"); 66 | } 67 | 68 | status = napi_set_named_property(env, exports, "ziti_set_log_level", fn); 69 | if (status != napi_ok) { 70 | napi_throw_error(env, NULL, "Unable to populate exports for 'ziti_set_log_level"); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/ziti_shutdown.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "ziti-nodejs.h" 18 | 19 | 20 | /** 21 | * 22 | */ 23 | napi_value _ziti_shutdown(napi_env env, const napi_callback_info info) { 24 | 25 | ZITI_NODEJS_LOG(DEBUG, "ztx: %p", ztx); 26 | 27 | ziti_shutdown(ztx); 28 | 29 | return NULL; 30 | } 31 | 32 | 33 | void expose_ziti_shutdown(napi_env env, napi_value exports) { 34 | napi_status status; 35 | napi_value fn; 36 | 37 | status = napi_create_function(env, NULL, 0, _ziti_shutdown, NULL, &fn); 38 | if (status != napi_ok) { 39 | napi_throw_error(env, NULL, "Unable to wrap native function '_ziti_shutdown"); 40 | } 41 | 42 | status = napi_set_named_property(env, exports, "ziti_shutdown", fn); 43 | if (status != napi_ok) { 44 | napi_throw_error(env, NULL, "Unable to populate exports for 'ziti_shutdown"); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/ziti_websocket_write.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "ziti-nodejs.h" 18 | #include 19 | 20 | // An item that will be generated here and passed into the JavaScript write callback 21 | typedef struct WSWriteItem { 22 | tlsuv_websocket_t *ws; 23 | ssize_t status; 24 | } WSWriteItem; 25 | 26 | 27 | /** 28 | * This function is responsible for calling the JavaScript 'write' callback function 29 | * that was specified when the ziti_write(...) was called from JavaScript. 30 | */ 31 | static void CallJs_on_write(napi_env env, napi_value js_cb, void* context, void* data) { 32 | napi_status status; 33 | 34 | // This parameter is not used. 35 | (void) context; 36 | 37 | // Retrieve the WriteItem created by the worker thread. 38 | WSWriteItem* item = (WSWriteItem*)data; 39 | 40 | // env and js_cb may both be NULL if Node.js is in its cleanup phase, and 41 | // items are left over from earlier thread-safe calls from the worker thread. 42 | // When env is NULL, we simply skip over the call into Javascript and free the 43 | // items. 44 | if (env != NULL) { 45 | 46 | napi_value undefined; 47 | 48 | // const obj = {} 49 | napi_value js_write_item, js_ws, js_status; 50 | status = napi_create_object(env, &js_write_item); 51 | if (status != napi_ok) { 52 | napi_throw_error(env, NULL, "Unable to napi_create_object"); 53 | } 54 | 55 | // obj.ws = ws 56 | ZITI_NODEJS_LOG(DEBUG, "ws=%p", item->ws); 57 | status = napi_create_int64(env, (int64_t)item->ws, &js_ws); 58 | if (status != napi_ok) { 59 | napi_throw_error(env, NULL, "Unable to napi_create_int64"); 60 | } 61 | status = napi_set_named_property(env, js_write_item, "ws", js_ws); 62 | if (status != napi_ok) { 63 | napi_throw_error(env, NULL, "Unable to napi_set_named_property"); 64 | } 65 | 66 | // obj.status = status 67 | ZITI_NODEJS_LOG(DEBUG, "status=%zd", item->status); 68 | status = napi_create_int64(env, (int64_t)item->status, &js_status); 69 | if (status != napi_ok) { 70 | napi_throw_error(env, NULL, "Unable to napi_create_int64"); 71 | } 72 | status = napi_set_named_property(env, js_write_item, "status", js_status); 73 | if (status != napi_ok) { 74 | napi_throw_error(env, NULL, "Unable to napi_set_named_property"); 75 | } 76 | 77 | // Retrieve the JavaScript `undefined` value so we can use it as the `this` 78 | // value of the JavaScript function call. 79 | status = napi_get_undefined(env, &undefined); 80 | if (status != napi_ok) { 81 | napi_throw_error(env, NULL, "Unable to napi_get_undefined (5)"); 82 | } 83 | 84 | // Call the JavaScript function and pass it the WriteItem 85 | status = napi_call_function( 86 | env, 87 | undefined, 88 | js_cb, 89 | 1, 90 | &js_write_item, 91 | NULL); 92 | if (status != napi_ok) { 93 | napi_throw_error(env, NULL, "Unable to napi_call_function"); 94 | } 95 | 96 | free(item); 97 | } 98 | } 99 | 100 | 101 | /** 102 | * 103 | */ 104 | static void on_write(uv_write_t *req, int status) { 105 | ZITI_NODEJS_LOG(DEBUG, "=========ws_write_cb: req: %p, status: %d", req, status); 106 | 107 | WSAddonData* addon_data = (WSAddonData*) req->data; 108 | 109 | free(req); 110 | 111 | WSWriteItem* item = memset(malloc(sizeof(*item)), 0, sizeof(*item)); 112 | item->ws = &(addon_data->ws); 113 | item->status = status; 114 | 115 | // Initiate the call into the JavaScript callback. 116 | // The call into JavaScript will not have happened 117 | // when this function returns, but it will be queued. 118 | napi_status nstatus = napi_call_threadsafe_function( 119 | addon_data->tsfn_on_write, 120 | item, 121 | napi_tsfn_blocking); 122 | if (nstatus != napi_ok) { 123 | ZITI_NODEJS_LOG(ERROR, "Unable to napi_call_threadsafe_function"); 124 | } 125 | 126 | } 127 | 128 | 129 | /** 130 | * 131 | */ 132 | napi_value _ziti_websocket_write(napi_env env, const napi_callback_info info) { 133 | napi_status status; 134 | size_t argc = 3; 135 | napi_value args[3]; 136 | status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); 137 | if (status != napi_ok) { 138 | napi_throw_error(env, NULL, "Failed to parse arguments"); 139 | } 140 | 141 | if (argc < 3) { 142 | napi_throw_error(env, "EINVAL", "Too few arguments"); 143 | return NULL; 144 | } 145 | 146 | // Obtain websocket 147 | int64_t js_ws; 148 | status = napi_get_value_int64(env, args[0], &js_ws); 149 | if (status != napi_ok) { 150 | napi_throw_error(env, NULL, "Failed to get Conn"); 151 | } 152 | tlsuv_websocket_t *ws = (tlsuv_websocket_t*)js_ws; 153 | ZITI_NODEJS_LOG(DEBUG, "========= ws: %p", ws); 154 | 155 | WSAddonData* addon_data = (WSAddonData*) ws->data; 156 | ZITI_NODEJS_LOG(DEBUG, "========= &(addon_data->ws): %p", &(addon_data->ws)); 157 | 158 | // Obtain data to write (we expect a Buffer) 159 | void* buffer; 160 | size_t bufferLength; 161 | status = napi_get_buffer_info(env, args[1], &buffer, &bufferLength); 162 | if (status != napi_ok) { 163 | napi_throw_error(env, NULL, "Failed to get Buffer info"); 164 | } 165 | 166 | // Since the underlying Buffer's lifetime is not guaranteed if it's managed by the VM, we will copy the chunk into our heap 167 | void* chunk = memset(malloc(bufferLength + 1), 0, bufferLength + 1); 168 | memcpy(chunk, buffer, bufferLength); 169 | 170 | // Obtain ptr to JS 'write' callback function 171 | napi_value js_write_cb = args[2]; 172 | napi_value work_name; 173 | 174 | // Create a string to describe this asynchronous operation. 175 | status = napi_create_string_utf8( 176 | env, 177 | "N-API on_write", 178 | NAPI_AUTO_LENGTH, 179 | &work_name); 180 | if (status != napi_ok) { 181 | napi_throw_error(env, NULL, "Failed to napi_create_string_utf8"); 182 | } 183 | 184 | // Convert the callback retrieved from JavaScript into a thread-safe function (tsfn) 185 | // which we can call from a worker thread. 186 | status = napi_create_threadsafe_function( 187 | env, 188 | js_write_cb, 189 | NULL, 190 | work_name, 191 | 0, 192 | 1, 193 | NULL, 194 | NULL, 195 | NULL, 196 | CallJs_on_write, 197 | &(addon_data->tsfn_on_write)); 198 | if (status != napi_ok) { 199 | napi_throw_error(env, NULL, "Failed to napi_create_threadsafe_function"); 200 | } 201 | 202 | // Now, call the C-SDK to actually write the data over the websocket 203 | 204 | uv_write_t *wr = malloc(sizeof(uv_write_t)); 205 | wr->data = addon_data; 206 | uv_buf_t b; 207 | b.base = chunk; 208 | b.len = bufferLength; 209 | 210 | tlsuv_websocket_write(wr, &(addon_data->ws), &b, on_write); 211 | 212 | return NULL; 213 | } 214 | 215 | 216 | 217 | /** 218 | * 219 | */ 220 | void expose_ziti_websocket_write(napi_env env, napi_value exports) { 221 | napi_status status; 222 | napi_value fn; 223 | 224 | status = napi_create_function(env, NULL, 0, _ziti_websocket_write, NULL, &fn); 225 | if (status != napi_ok) { 226 | napi_throw_error(env, NULL, "Unable to wrap native function '_ziti_websocket_write"); 227 | } 228 | 229 | status = napi_set_named_property(env, exports, "ziti_websocket_write", fn); 230 | if (status != napi_ok) { 231 | napi_throw_error(env, NULL, "Unable to populate exports for 'ziti_websocket_write"); 232 | } 233 | 234 | } 235 | 236 | -------------------------------------------------------------------------------- /src/ziti_write.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright NetFoundry Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "ziti-nodejs.h" 18 | #include 19 | 20 | 21 | // An item that will be generated here and passed into the JavaScript write callback 22 | typedef struct WriteItem { 23 | ziti_connection conn; 24 | ssize_t status; 25 | } WriteItem; 26 | 27 | 28 | /** 29 | * This function is responsible for calling the JavaScript 'write' callback function 30 | * that was specified when the ziti_write(...) was called from JavaScript. 31 | */ 32 | static void CallJs_on_write(napi_env env, napi_value js_cb, void* context, void* data) { 33 | napi_status status; 34 | 35 | ZITI_NODEJS_LOG(DEBUG, "CallJs_on_write entered"); 36 | 37 | // This parameter is not used. 38 | (void) context; 39 | 40 | // Retrieve the WriteItem created by the worker thread. 41 | WriteItem* item = (WriteItem*)data; 42 | 43 | // env and js_cb may both be NULL if Node.js is in its cleanup phase, and 44 | // items are left over from earlier thread-safe calls from the worker thread. 45 | // When env is NULL, we simply skip over the call into Javascript and free the 46 | // items. 47 | if (env != NULL) { 48 | 49 | napi_value undefined; 50 | 51 | // const obj = {} 52 | napi_value js_write_item, js_conn, js_status; 53 | status = napi_create_object(env, &js_write_item); 54 | if (status != napi_ok) { 55 | napi_throw_error(env, NULL, "Unable to napi_create_object"); 56 | } 57 | 58 | // obj.conn = conn 59 | ZITI_NODEJS_LOG(DEBUG, "conn=%p", item->conn); 60 | status = napi_create_int64(env, (int64_t)item->conn, &js_conn); 61 | if (status != napi_ok) { 62 | napi_throw_error(env, NULL, "Unable to napi_create_int64"); 63 | } 64 | status = napi_set_named_property(env, js_write_item, "conn", js_conn); 65 | if (status != napi_ok) { 66 | napi_throw_error(env, NULL, "Unable to napi_set_named_property"); 67 | } 68 | 69 | // obj.status = status 70 | ZITI_NODEJS_LOG(DEBUG, "status=%zo", item->status); 71 | status = napi_create_int64(env, (int64_t)item->status, &js_status); 72 | if (status != napi_ok) { 73 | napi_throw_error(env, NULL, "Unable to napi_create_int64"); 74 | } 75 | status = napi_set_named_property(env, js_write_item, "status", js_status); 76 | if (status != napi_ok) { 77 | napi_throw_error(env, NULL, "Unable to napi_set_named_property"); 78 | } 79 | 80 | // Retrieve the JavaScript `undefined` value so we can use it as the `this` 81 | // value of the JavaScript function call. 82 | status = napi_get_undefined(env, &undefined); 83 | if (status != napi_ok) { 84 | napi_throw_error(env, NULL, "Unable to napi_get_undefined (5)"); 85 | } 86 | 87 | // Call the JavaScript function and pass it the WriteItem 88 | status = napi_call_function( 89 | env, 90 | undefined, 91 | js_cb, 92 | 1, 93 | &js_write_item, 94 | NULL); 95 | if (status != napi_ok) { 96 | napi_throw_error(env, NULL, "Unable to napi_call_function"); 97 | } 98 | 99 | free(item); 100 | } 101 | } 102 | 103 | 104 | /** 105 | * 106 | */ 107 | static void on_write(ziti_connection conn, ssize_t status, void *ctx) { 108 | 109 | ConnAddonData* addon_data = (ConnAddonData*) ziti_conn_data(conn); 110 | 111 | ZITI_NODEJS_LOG(DEBUG, "on_write cb entered: addon_data: %p", addon_data); 112 | 113 | WriteItem* item = memset(malloc(sizeof(*item)), 0, sizeof(*item)); 114 | item->conn = conn; 115 | item->status = status; 116 | 117 | // Initiate the call into the JavaScript callback. 118 | // The call into JavaScript will not have happened 119 | // when this function returns, but it will be queued. 120 | napi_status nstatus = napi_call_threadsafe_function( 121 | addon_data->tsfn_on_write, 122 | item, 123 | napi_tsfn_blocking); 124 | if (nstatus != napi_ok) { 125 | ZITI_NODEJS_LOG(ERROR, "Unable to napi_call_threadsafe_function"); 126 | } 127 | } 128 | 129 | 130 | /** 131 | * 132 | */ 133 | napi_value _ziti_write(napi_env env, const napi_callback_info info) { 134 | napi_status status; 135 | size_t argc = 3; 136 | napi_value args[3]; 137 | status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); 138 | if (status != napi_ok) { 139 | napi_throw_error(env, NULL, "Failed to parse arguments"); 140 | } 141 | 142 | if (argc < 3) { 143 | napi_throw_error(env, "EINVAL", "Too few arguments"); 144 | return NULL; 145 | } 146 | 147 | // Obtain ziti_connection 148 | int64_t js_conn; 149 | status = napi_get_value_int64(env, args[0], &js_conn); 150 | if (status != napi_ok) { 151 | napi_throw_error(env, NULL, "Failed to get Conn"); 152 | } 153 | ziti_connection conn = (ziti_connection)js_conn; 154 | 155 | ConnAddonData* addon_data = (ConnAddonData*) ziti_conn_data(conn); 156 | 157 | // Obtain data to write (we expect a Buffer) 158 | void* buffer; 159 | size_t bufferLength; 160 | status = napi_get_buffer_info(env, args[1], &buffer, &bufferLength); 161 | if (status != napi_ok) { 162 | napi_throw_error(env, NULL, "Failed to get Buffer info"); 163 | } 164 | 165 | // Since the underlying Buffer's lifetime is not guaranteed if it's managed by the VM, we will copy the chunk into our heap 166 | void* chunk = memset(malloc(bufferLength), 0, bufferLength); 167 | memcpy(chunk, buffer, bufferLength); 168 | 169 | // Obtain ptr to JS 'write' callback function 170 | napi_value js_write_cb = args[2]; 171 | ZITI_NODEJS_LOG(DEBUG, "js_write_cb: %p", js_write_cb); 172 | 173 | napi_value work_name; 174 | 175 | // Create a string to describe this asynchronous operation. 176 | status = napi_create_string_utf8( 177 | env, 178 | "N-API on_write", 179 | NAPI_AUTO_LENGTH, 180 | &work_name); 181 | if (status != napi_ok) { 182 | napi_throw_error(env, NULL, "Failed to napi_create_string_utf8"); 183 | } 184 | 185 | // Convert the callback retrieved from JavaScript into a thread-safe function (tsfn) 186 | // which we can call from a worker thread. 187 | status = napi_create_threadsafe_function( 188 | env, 189 | js_write_cb, 190 | NULL, 191 | work_name, 192 | 0, 193 | 1, 194 | NULL, 195 | NULL, 196 | NULL, 197 | CallJs_on_write, 198 | &(addon_data->tsfn_on_write)); 199 | if (status != napi_ok) { 200 | napi_throw_error(env, NULL, "Failed to napi_create_threadsafe_function"); 201 | } 202 | 203 | // Now, call the C-SDK to actually write the data over to the service 204 | ZITI_NODEJS_LOG(DEBUG, "call ziti_write"); 205 | ziti_write(conn, chunk, bufferLength, on_write, NULL); 206 | ZITI_NODEJS_LOG(DEBUG, "back from ziti_write"); 207 | 208 | return NULL; 209 | } 210 | 211 | 212 | 213 | /** 214 | * 215 | */ 216 | void expose_ziti_write(napi_env env, napi_value exports) { 217 | napi_status status; 218 | napi_value fn; 219 | 220 | status = napi_create_function(env, NULL, 0, _ziti_write, NULL, &fn); 221 | if (status != napi_ok) { 222 | napi_throw_error(env, NULL, "Unable to wrap native function '_ziti_write"); 223 | } 224 | 225 | status = napi_set_named_property(env, exports, "ziti_write", fn); 226 | if (status != napi_ok) { 227 | napi_throw_error(env, NULL, "Unable to populate exports for 'ziti_write"); 228 | } 229 | 230 | } 231 | 232 | -------------------------------------------------------------------------------- /tests/enroll-test.js: -------------------------------------------------------------------------------- 1 | const bindings = require('bindings')('ziti_sdk_nodejs') 2 | 3 | const result = bindings.ziti_sdk_version(); 4 | const binary = require('@mapbox/node-pre-gyp'); 5 | const path = require('path') 6 | const binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json'))); 7 | const ziti = require(binding_path); 8 | require('assert').notEqual(result,""); 9 | 10 | console.log("using ziti version: " + ziti.ziti_sdk_version()) 11 | 12 | 13 | 14 | const ziti_Enroll = async (jwt_path) => { 15 | console.log("JS ziti_Enroll() entered ") 16 | return new Promise((resolve, reject) => { 17 | let rc = ziti.ziti_enroll( 18 | jwt_path, 19 | (data) => { 20 | return resolve(data); 21 | } 22 | ); 23 | }); 24 | }; 25 | 26 | 27 | (async () => { 28 | 29 | let jwt_path = process.argv[2]; 30 | 31 | let data = await ziti_Enroll(jwt_path).catch((data) => { 32 | console.log('JS ziti_enroll failed with error code (%o/%s)', data.status, data.err); 33 | }); 34 | 35 | if (data && data.identity) { 36 | console.log("data.identity is:\n\n%s\n", data.identity); 37 | } 38 | 39 | process.exit(0); 40 | 41 | })(); 42 | -------------------------------------------------------------------------------- /tests/hello.js: -------------------------------------------------------------------------------- 1 | 2 | const bindings = require('bindings')('ziti_sdk_nodejs') 3 | 4 | console.log(bindings) 5 | const result = bindings.ziti_sdk_version(); 6 | console.log("ziti_sdk_version() result is: ", result); 7 | 8 | 9 | 10 | const binary = require('@mapbox/node-pre-gyp'); 11 | const path = require('path') 12 | // const binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')), {debug: true}); 13 | const binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json'))); 14 | console.log("binding_path is: ", binding_path); 15 | const ziti = require(binding_path); 16 | console.log("ziti native addon is: \n", ziti); 17 | require('assert').notEqual(result,""); 18 | console.log("SUCCESS"); 19 | -------------------------------------------------------------------------------- /tests/https-test.js: -------------------------------------------------------------------------------- 1 | 2 | var binary = require('@mapbox/node-pre-gyp'); 3 | var path = require('path') 4 | // var binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')), {debug: true}); 5 | var binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json'))); 6 | var ziti = require(binding_path); 7 | require('assert').equal(ziti.ziti_hello(),"ziti"); 8 | 9 | 10 | 11 | const Ziti_http_request = async (url, method, headers) => { 12 | return new Promise((resolve, reject) => { 13 | try { 14 | 15 | console.log('headers (%o)', headers); 16 | 17 | let req = ziti.Ziti_http_request( 18 | url, 19 | method, 20 | headers, 21 | // on_req callback 22 | (obj) => { 23 | console.log('----------- Now inside Ziti_http_request on_req callback ----------, obj is: \n%o', obj); 24 | }, 25 | // on_resp callback 26 | (obj) => { 27 | console.log('----------- Now inside Ziti_http_request on_resp callback ----------, obj is: \n%o', obj); 28 | // resolve(obj); 29 | }, 30 | // on_resp_data callback 31 | (obj) => { 32 | console.log('----------- Now inside Ziti_http_request on_resp_data callback ----------, obj is: \n%o', obj); 33 | if (obj.body) { 34 | console.log('----------- obj.body is: \n%o', obj.body.toString()); 35 | } 36 | }, 37 | ); 38 | 39 | console.log('inside JS Ziti_http_request(), req is (%o)', req); 40 | resolve(req); 41 | 42 | } 43 | catch (e) { 44 | reject(e); 45 | } 46 | }); 47 | } 48 | 49 | const Ziti_http_request_data = async (req, buffer) => { 50 | return new Promise((resolve, reject) => { 51 | ziti.Ziti_http_request_data( 52 | req, 53 | buffer, 54 | // on_req_data callback 55 | (obj) => { 56 | console.log('----------- Now inside Ziti_http_request_data on_req_data callback ----------, obj is: \n%o', obj); 57 | resolve(obj); 58 | } 59 | ); 60 | }); 61 | } 62 | 63 | const NF_init = async () => { 64 | return new Promise((resolve) => { 65 | ziti.ziti_init(process.argv[2], () => { 66 | resolve(); 67 | }); 68 | }); 69 | }; 70 | 71 | let ctr = 0; 72 | const spin = () => { 73 | setTimeout( () => { 74 | ctr++; 75 | console.log("1-sec wait, ctr=%d", ctr); 76 | if (ctr < 60) { 77 | spin(); 78 | } else { 79 | process.exit(0); 80 | } 81 | }, 1000); 82 | } 83 | 84 | // let chunkctr = 0; 85 | let chunkBody = process.argv[5]; 86 | 87 | // const sendChunk = (req) => { 88 | // setTimeout( (req) => { 89 | // chunkctr++; 90 | // console.log("chunkctr=%d", chunkctr); 91 | // if (chunkctr < 10) { 92 | 93 | // buffer = Buffer.from(chunkBody + "-" + chunkctr); 94 | 95 | // console.log("sending chunk %d", chunkctr); 96 | 97 | // Ziti_http_request_data(req, buffer); 98 | 99 | // sendChunk(req); 100 | 101 | // } else { 102 | // console.log("======== calling Ziti_http_request_end"); 103 | // ziti.Ziti_http_request_end( req ); 104 | // } 105 | // }, 500, req); 106 | // } 107 | 108 | const sendChunk = (req) => { 109 | setTimeout( (req) => { 110 | buffer = Buffer.from(chunkBody); 111 | console.log("sending chunk"); 112 | Ziti_http_request_data(req, buffer); 113 | }, 500, req); 114 | } 115 | 116 | 117 | 118 | (async () => { 119 | 120 | let url = process.argv[3]; 121 | let method = process.argv[4]; 122 | let body = process.argv[5]; 123 | let buffer; 124 | let results; 125 | let headersArray = [ 126 | "Content-Length:1", 127 | "Transfer-Encoding:chunked", 128 | ]; 129 | 130 | 131 | await NF_init(); 132 | 133 | if (typeof body !== 'undefined') { 134 | 135 | buffer = Buffer.from("1"); 136 | 137 | console.log("Sending 'body' of: %o", buffer); 138 | 139 | let req = await Ziti_http_request(url, method, headersArray).catch((err) => { 140 | console.log('Ziti_http_request failed with error (%o)', err); 141 | process.exit(-1); 142 | }); 143 | 144 | console.log("Ziti_http_request results is:\n\n%o", req); 145 | 146 | 147 | for (let i = 0; i < 10; i++) { 148 | console.log("queueing chunk %d", i); 149 | 150 | results = await Ziti_http_request_data(req, buffer).catch((err) => { 151 | console.log('Ziti_http_request_data failed with error (%o)', err); 152 | process.exit(-1); 153 | }); 154 | 155 | } 156 | 157 | console.log('========================================================================================================'); 158 | console.log('======================================== Ziti_http_request_end called =================================='); 159 | ziti.Ziti_http_request_end( req ); 160 | console.log('========================================================================================================'); 161 | 162 | } else { 163 | 164 | console.log("No 'body' will be sent"); 165 | 166 | 167 | let req = await Ziti_http_request(url, method, []).catch((err) => { 168 | console.log('Ziti_http_request failed with error (%o)', err); 169 | process.exit(-1); 170 | }); 171 | 172 | // console.log('inside JS main(), req is (%o)', req); 173 | 174 | 175 | // setTimeout( async () => { 176 | 177 | // for (let i=0; i<3; i++ ) { 178 | 179 | // let req2 = await Ziti_http_request(url, method, []).catch((err) => { 180 | // console.log('Ziti_http_request failed with error (%o)', err); 181 | // process.exit(-1); 182 | // }); 183 | 184 | // console.log('inside JS main() setTimeout(), req is (%o)', req2); 185 | 186 | // } 187 | 188 | // }, 100); 189 | 190 | // let req2 = await Ziti_http_request(url, method, []).catch((err) => { 191 | // console.log('Ziti_http_request failed with error (%o)', err); 192 | // process.exit(-1); 193 | // }); 194 | 195 | // console.log("Ziti_http_request results is:\n\n%o", req2); 196 | 197 | // console.log('========================================================================================================'); 198 | // console.log('======================================== Ziti_http_request_end called =================================='); 199 | // ziti.Ziti_http_request_end( req ); 200 | // console.log('========================================================================================================'); 201 | 202 | 203 | } 204 | 205 | 206 | spin(); 207 | 208 | })(); 209 | -------------------------------------------------------------------------------- /tests/mattermost-test.js: -------------------------------------------------------------------------------- 1 | 2 | var binary = require('@mapbox/node-pre-gyp'); 3 | var path = require('path') 4 | var binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json'))); 5 | var ziti = require(binding_path); 6 | require('assert').equal(ziti.ziti_hello(),"ziti"); 7 | 8 | 9 | 10 | function NF_dial(service) { 11 | console.log('----------- inside NF_dial() ---------- service is: ', service); 12 | return new Promise((resolve, reject) => { 13 | ziti.ziti_dial( 14 | service, 15 | false, // NOT a wabsocket 16 | (conn) => { 17 | console.log('----------- Now inside NF_dial connect callback ----------, conn is: ' + conn); 18 | resolve(conn); 19 | }, 20 | (data) => { 21 | console.log('----------- Now inside NF_dial data callback ----------, data is: [===\n%s\n===]', data); 22 | }, 23 | ); 24 | }); 25 | } 26 | 27 | const rand = max => Math.floor(Math.random() * max); 28 | const delay = (ms, value) => new Promise(resolve => setTimeout(resolve, ms, value)); 29 | 30 | const NF_init = async () => { 31 | return new Promise((resolve) => { 32 | ziti.ziti_init(process.argv[2], () => { 33 | resolve(); 34 | }); 35 | }); 36 | }; 37 | 38 | const NF_service_available = (service) => { 39 | return new Promise((resolve) => { 40 | ziti.ziti_service_available(service, (status) => { 41 | resolve(status); 42 | }); 43 | }); 44 | }; 45 | 46 | const NF_write = (conn, data) => { 47 | return new Promise((resolve) => { 48 | ziti.ziti_write(conn, data, () => { 49 | resolve(); 50 | }); 51 | }); 52 | }; 53 | 54 | let ctr = 0; 55 | const spin = () => { 56 | setTimeout( () => { 57 | ctr++; 58 | console.log("1-sec wait, ctr=%d", ctr); 59 | if (ctr < 5) { 60 | spin(); 61 | } else { 62 | process.exit(0); 63 | } 64 | }, 1000); 65 | } 66 | 67 | 68 | function NF_write_callback(item) { 69 | if (item.status < 0) { 70 | // console.log("NF_write_callback(): request performed on conn '%o' failed to submit status '%o'", item.conn, item.status); 71 | } 72 | else { 73 | // console.log("NF_write_callback(): request performed on conn '%o' successful: '%o' bytes sent", item.conn, item.status); 74 | } 75 | } 76 | 77 | function NF_dial_connect_callback(conn) { 78 | 79 | console.log("NF_dial_connect_callback(): received connection '%o'; now initiating Write to service", conn); 80 | 81 | let data = 82 | "GET / " + 83 | "HTTP/1.1\r\n" + 84 | "Accept: */*\r\n" + 85 | "Connection: keep-alive\r\n" + 86 | "Host: mattermost.ziti.netfoundry.io\r\n" + 87 | "User-Agent: curl/7.54.0\r\n" + 88 | "\r\n"; 89 | 90 | ziti.ziti_write( 91 | conn, 92 | data, 93 | NF_write_callback 94 | ); 95 | } 96 | 97 | function NF_dial_data_callback(data) { 98 | console.log("NF_dial_data_callback(): received the following data: \n\n", data); 99 | } 100 | 101 | (async () => { 102 | 103 | await NF_init(); 104 | 105 | let status = await NF_service_available('mattermost.ziti.netfoundry.io'); 106 | 107 | let conn = await NF_dial('mattermost.ziti.netfoundry.io'); 108 | 109 | let data = 110 | "GET / " + 111 | "HTTP/1.1\r\n" + 112 | "Accept: */*\r\n" + 113 | "Connection: keep-alive\r\n" + 114 | "Host: mattermost.ziti.netfoundry.io\r\n" + 115 | "User-Agent: curl/7.54.0\r\n" + 116 | "\r\n"; 117 | 118 | let buffer = Buffer.from(data); 119 | 120 | await NF_write(conn, buffer); 121 | 122 | spin(); 123 | 124 | })(); 125 | -------------------------------------------------------------------------------- /tests/websocket-test.js: -------------------------------------------------------------------------------- 1 | 2 | var binary = require('@mapbox/node-pre-gyp'); 3 | var path = require('path') 4 | var binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')), {debug: true}); 5 | // var binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json'))); 6 | var ziti = require(binding_path); 7 | require('assert').equal(ziti.ziti_hello(),"ziti"); 8 | 9 | 10 | 11 | const do_ziti_websocket_connect = async (url) => { 12 | return new Promise((resolve, reject) => { 13 | try { 14 | 15 | console.log('----------- url (%o)', url); 16 | 17 | let rc = ziti.ziti_websocket_connect( 18 | url, 19 | 20 | // on_connect callback 21 | (ws) => { 22 | console.log('----------- Now inside ziti_websocket_connect on_connect callback ----------, ws is: %o', ws); 23 | resolve(ws); 24 | }, 25 | 26 | // on_data callback 27 | (obj) => { 28 | console.log('----------- Now inside ziti_websocket_connect on_data callback ----------, obj is: \n%o', obj); 29 | console.log('----------- obj.body is: \n%o', obj.body.toString()); 30 | }, 31 | ); 32 | 33 | // console.log('inside JS Ziti_http_request(), req is (%o)', rc); 34 | // resolve(req); 35 | 36 | } 37 | catch (e) { 38 | reject(e); 39 | } 40 | }); 41 | } 42 | 43 | 44 | const do_ziti_websocket_write = async (ws, buffer) => { 45 | return new Promise((resolve, reject) => { 46 | try { 47 | 48 | console.log('----------- ws (%o)', ws); 49 | 50 | let rc = ziti.ziti_websocket_write( 51 | ws, 52 | buffer, 53 | 54 | // on_write callback 55 | (obj) => { 56 | console.log('----------- Now inside ziti_websocket_write on_write callback ----------, obj is: %o', obj); 57 | resolve(obj); 58 | }, 59 | 60 | ); 61 | 62 | } 63 | catch (e) { 64 | reject(e); 65 | } 66 | }); 67 | } 68 | 69 | 70 | const NF_init = async () => { 71 | return new Promise((resolve) => { 72 | ziti.ziti_init(process.argv[2], () => { 73 | resolve(); 74 | }); 75 | }); 76 | }; 77 | 78 | let ctr = 0; 79 | const spin = () => { 80 | setTimeout( () => { 81 | ctr++; 82 | console.log("----------- 1-sec wait, ctr=%d", ctr); 83 | if (ctr < 10) { 84 | spin(); 85 | } else { 86 | process.exit(0); 87 | } 88 | }, 1000); 89 | } 90 | 91 | 92 | 93 | 94 | (async () => { 95 | 96 | let url = process.argv[3]; 97 | 98 | await NF_init(); 99 | 100 | 101 | let ws = await do_ziti_websocket_connect(url).catch((err) => { 102 | console.log('----------- do_ziti_websocket_connect failed with error (%o)', err); 103 | process.exit(-1); 104 | }); 105 | 106 | console.log('----------- do_ziti_websocket_connect() returned, ws is (%o)', ws); 107 | 108 | let buffer = Buffer.from("this is some data"); 109 | 110 | let obj = await do_ziti_websocket_write(ws, buffer).catch((err) => { 111 | console.log('----------- do_ziti_websocket_write failed with error (%o)', err); 112 | process.exit(-1); 113 | }); 114 | 115 | console.log('----------- do_ziti_websocket_write() returned, obj is (%o)', obj); 116 | 117 | spin(); 118 | 119 | })(); 120 | -------------------------------------------------------------------------------- /toolchains/Linux-arm.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Linux) 2 | set(CMAKE_SYSTEM_PROCESSOR arm) 3 | 4 | set(triple arm-linux-gnueabihf) 5 | 6 | set(CMAKE_C_COMPILER ${triple}-gcc) 7 | set(CMAKE_CXX_COMPILER ${triple}-g++) 8 | 9 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 10 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 11 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 12 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 13 | -------------------------------------------------------------------------------- /toolchains/Linux-arm64.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Linux) 2 | set(CMAKE_SYSTEM_PROCESSOR arm64) 3 | 4 | set(triple aarch64-linux-gnu) 5 | 6 | set(CMAKE_C_COMPILER ${triple}-gcc) 7 | set(CMAKE_CXX_COMPILER ${triple}-g++) 8 | 9 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 10 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 11 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 12 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 13 | -------------------------------------------------------------------------------- /toolchains/Windows-arm64-msvc.cmake: -------------------------------------------------------------------------------- 1 | # cross-compile for windows/arm64 on windows/x64 host with visual studio 2 | set(CMAKE_SYSTEM_NAME Windows) 3 | set(CMAKE_SYSTEM_PROCESSOR ARM64) 4 | # setting CMAKE_GENERATOR_PLATFORM should be sufficient if you believe the doc, it results in build files 5 | # that cause msbuild to that the ZERO_CHECK project doesn't contain the "Debug|x64" platform/config 6 | # combination. running cmake with '-A ARCH64' avoids the msbuild failure. 7 | set(CMAKE_GENERATOR_PLATFORM ARM64) 8 | set(CMAKE_C_COMPILER cl.exe) -------------------------------------------------------------------------------- /toolchains/Windows-x86.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Windows) 2 | set(CMAKE_SYSTEM_PROCESSOR x86) 3 | 4 | set(triple i686-w64-mingw32) 5 | 6 | set(CMAKE_C_COMPILER ${triple}-gcc) 7 | set(CMAKE_CXX_COMPILER ${triple}-g++) 8 | 9 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 10 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 11 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 12 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 13 | -------------------------------------------------------------------------------- /toolchains/Windows-x86_64.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Windows) 2 | set(CMAKE_SYSTEM_PROCESSOR x86_64) 3 | 4 | set(triple x86_64-w64-mingw32) 5 | 6 | set(CMAKE_C_COMPILER ${triple}-gcc) 7 | set(CMAKE_CXX_COMPILER ${triple}-g++) 8 | 9 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 10 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 11 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 12 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 13 | -------------------------------------------------------------------------------- /toolchains/linux-embedded.cmake: -------------------------------------------------------------------------------- 1 | if (NOT DEFINED ENV{TOOLCHAIN}) 2 | message(FATAL_ERROR "TOOLCHAIN environment variable is not set") 3 | endif () 4 | 5 | set(triple mips-openwrt-linux) 6 | 7 | set(CMAKE_SYSTEM_NAME Linux) 8 | set(CMAKE_SYSTEM_PROCESSOR mips) 9 | 10 | set(CMAKE_SYSROOT $ENV{TOOLCHAIN}/) 11 | set(CMAKE_C_COMPILER $ENV{TOOLCHAIN}/bin/${triple}-gcc) 12 | set(CMAKE_CXX_COMPILER $ENV{TOOLCHAIN}/bin/${triple}-g++) 13 | 14 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 15 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 16 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 17 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 18 | -------------------------------------------------------------------------------- /toolchains/macOS-arm64.cmake: -------------------------------------------------------------------------------- 1 | # build-macOS-arm64 2 | 3 | if (NOT (CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") AND 4 | NOT(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64") ) 5 | set(CMAKE_SYSTEM_NAME Darwin) 6 | set(CMAKE_SYSTEM_PROCESSOR arm64) 7 | endif () 8 | 9 | set(ZITI_BUILD_TESTS OFF CACHE BOOL "" FORCE) 10 | 11 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch arm64") 12 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch arm64") 13 | 14 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 15 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 16 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 17 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 18 | -------------------------------------------------------------------------------- /toolchains/macOS-x86_64.cmake: -------------------------------------------------------------------------------- 1 | # build-macOS-x86_64 2 | 3 | set(CMAKE_SYSTEM_NAME Darwin) 4 | set(CMAKE_SYSTEM_PROCESSOR x86_64) 5 | 6 | set(ZITI_BUILD_TESTS OFF CACHE BOOL "" FORCE) 7 | 8 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch x86_64") 9 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch x86_64") 10 | 11 | # for libsodium 12 | set(triple x86_64-apple-macos11) 13 | execute_process(COMMAND /usr/bin/xcrun -sdk macosx --show-sdk-path 14 | OUTPUT_VARIABLE CMAKE_OSX_SYSROOT 15 | OUTPUT_STRIP_TRAILING_WHITESPACE) 16 | 17 | set(ENV{CFLAGS} "-arch x86_64 -isysroot ${CMAKE_OSX_SYSROOT}") 18 | set(ENV{LDFLAGS} "-arch x86_64 -isysroot ${CMAKE_OSX_SYSROOT}") 19 | 20 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 21 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 22 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 23 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 24 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ziti", 3 | "version-semver": "1.0.0", 4 | "dependencies": [ 5 | { 6 | "name": "libuv", 7 | "platform": "!windows", 8 | "$comment": "on windows we link against node binary and Cmake.js provides libuv headers" 9 | }, 10 | "openssl", 11 | "zlib", 12 | "llhttp", 13 | "libsodium", 14 | "json-c", 15 | "protobuf-c" 16 | ], 17 | "features": { 18 | }, 19 | "builtin-baseline": "b322364f06308bdd24823f9d8f03fe0cc86fd46f" 20 | } -------------------------------------------------------------------------------- /ziti.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/ziti'); -------------------------------------------------------------------------------- /ziti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openziti/ziti-sdk-nodejs/53f2a339ffb16e8a11939618877e4590b982a8f7/ziti.png --------------------------------------------------------------------------------