From 482569ceb85d752cb0058cf98568a803902e629f Mon Sep 17 00:00:00 2001 From: Dan Steren Date: Tue, 19 Dec 2023 16:32:14 -0700 Subject: [PATCH 01/22] Add boilerplate for http prop test --- .github/workflows/test.yml | 1 + .../canister_methods/http_request/dfx.json | 16 + .../http_request/package-lock.json | 996 ++++++++++++++++++ .../http_request/package.json | 12 + .../http_request/tsconfig.json | 10 + 5 files changed, 1035 insertions(+) create mode 100644 property_tests/tests/canister_methods/http_request/dfx.json create mode 100644 property_tests/tests/canister_methods/http_request/package-lock.json create mode 100644 property_tests/tests/canister_methods/http_request/package.json create mode 100644 property_tests/tests/canister_methods/http_request/tsconfig.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 68ee5b671a..7aa856c38a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -133,6 +133,7 @@ jobs: "examples/vanilla_js", "property_tests/tests/blob", "property_tests/tests/bool", + "property_tests/tests/canister_methods/http_request", "property_tests/tests/canister_methods/init", "property_tests/tests/canister_methods/post_upgrade", "property_tests/tests/canister_methods/pre_upgrade", diff --git a/property_tests/tests/canister_methods/http_request/dfx.json b/property_tests/tests/canister_methods/http_request/dfx.json new file mode 100644 index 0000000000..6ab9fbd7e4 --- /dev/null +++ b/property_tests/tests/canister_methods/http_request/dfx.json @@ -0,0 +1,16 @@ +{ + "canisters": { + "canister": { + "type": "custom", + "main": "src/index.ts", + "candid": "src/index.did", + "build": "npx azle canister", + "wasm": ".azle/canister/canister.wasm", + "gzip": true, + "declarations": { + "output": "test/dfx_generated/canister", + "node_compatibility": true + } + } + } +} diff --git a/property_tests/tests/canister_methods/http_request/package-lock.json b/property_tests/tests/canister_methods/http_request/package-lock.json new file mode 100644 index 0000000000..90fec02b45 --- /dev/null +++ b/property_tests/tests/canister_methods/http_request/package-lock.json @@ -0,0 +1,996 @@ +{ + "name": "http_request", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "azle": "^0.18.5" + }, + "devDependencies": { + "ts-node": "^10.9.1", + "typescript": "^5.2.2" + } + }, + "node_modules/@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@dfinity/principal": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@dfinity/principal/-/principal-0.19.3.tgz", + "integrity": "sha512-+nixVvdGt7ECxRvLXDXsvU9q9sSPssBtDQ4bXa149SK6gcYcmZ6lfWIi3DJNqj3tGROxILVBsguel9tECappsA==", + "dependencies": { + "@noble/hashes": "^1.3.1" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.5.tgz", + "integrity": "sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.5.tgz", + "integrity": "sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.5.tgz", + "integrity": "sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz", + "integrity": "sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.5.tgz", + "integrity": "sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.5.tgz", + "integrity": "sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.5.tgz", + "integrity": "sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.5.tgz", + "integrity": "sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz", + "integrity": "sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.5.tgz", + "integrity": "sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.5.tgz", + "integrity": "sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.5.tgz", + "integrity": "sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.5.tgz", + "integrity": "sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.5.tgz", + "integrity": "sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.5.tgz", + "integrity": "sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz", + "integrity": "sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.5.tgz", + "integrity": "sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.5.tgz", + "integrity": "sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.5.tgz", + "integrity": "sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.5.tgz", + "integrity": "sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.5.tgz", + "integrity": "sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.5.tgz", + "integrity": "sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@swc/core": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.93.tgz", + "integrity": "sha512-690GRr1wUGmGYZHk7fUduX/JUwViMF2o74mnZYIWEcJaCcd9MQfkhsxPBtjeg6tF+h266/Cf3RPYhsFBzzxXcA==", + "hasInstallScript": true, + "dependencies": { + "@swc/counter": "^0.1.1", + "@swc/types": "^0.1.5" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.3.93", + "@swc/core-darwin-x64": "1.3.93", + "@swc/core-linux-arm-gnueabihf": "1.3.93", + "@swc/core-linux-arm64-gnu": "1.3.93", + "@swc/core-linux-arm64-musl": "1.3.93", + "@swc/core-linux-x64-gnu": "1.3.93", + "@swc/core-linux-x64-musl": "1.3.93", + "@swc/core-win32-arm64-msvc": "1.3.93", + "@swc/core-win32-ia32-msvc": "1.3.93", + "@swc/core-win32-x64-msvc": "1.3.93" + }, + "peerDependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.93.tgz", + "integrity": "sha512-gEKgk7FVIgltnIfDO6GntyuQBBlAYg5imHpRgLxB1zSI27ijVVkksc6QwISzFZAhKYaBWIsFSVeL9AYSziAF7A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.93.tgz", + "integrity": "sha512-ZQPxm/fXdDQtn3yrYSL/gFfA8OfZ5jTi33yFQq6vcg/Y8talpZ+MgdSlYM0FkLrZdMTYYTNFiuBQuuvkA+av+Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.93.tgz", + "integrity": "sha512-OYFMMI2yV+aNe3wMgYhODxHdqUB/jrK0SEMHHS44GZpk8MuBXEF+Mcz4qjkY5Q1EH7KVQqXb/gVWwdgTHpjM2A==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.93.tgz", + "integrity": "sha512-BT4dT78odKnJMNiq5HdjBsv29CiIdcCcImAPxeFqAeFw1LL6gh9nzI8E96oWc+0lVT5lfhoesCk4Qm7J6bty8w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.93.tgz", + "integrity": "sha512-yH5fWEl1bktouC0mhh0Chuxp7HEO4uCtS/ly1Vmf18gs6wZ8DOOkgAEVv2dNKIryy+Na++ljx4Ym7C8tSJTrLw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.93.tgz", + "integrity": "sha512-OFUdx64qvrGJhXKEyxosHxgoUVgba2ztYh7BnMiU5hP8lbI8G13W40J0SN3CmFQwPP30+3oEbW7LWzhKEaYjlg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.93.tgz", + "integrity": "sha512-4B8lSRwEq1XYm6xhxHhvHmKAS7pUp1Q7E33NQ2TlmFhfKvCOh86qvThcjAOo57x8DRwmpvEVrqvpXtYagMN6Ig==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.93.tgz", + "integrity": "sha512-BHShlxtkven8ZjjvZ5QR6sC5fZCJ9bMujEkiha6W4cBUTY7ce7qGFyHmQd+iPC85d9kD/0cCiX/Xez8u0BhO7w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.93.tgz", + "integrity": "sha512-nEwNWnz4JzYAK6asVvb92yeylfxMYih7eMQOnT7ZVlZN5ba9WF29xJ6kcQKs9HRH6MvWhz9+wRgv3FcjlU6HYA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.93.tgz", + "integrity": "sha512-jibQ0zUr4kwJaQVwgmH+svS04bYTPnPw/ZkNInzxS+wFAtzINBYcU8s2PMWbDb2NGYiRSEeoSGyAvS9H+24JFA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz", + "integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==" + }, + "node_modules/@swc/types": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", + "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" + }, + "node_modules/@types/node": { + "version": "20.8.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.7.tgz", + "integrity": "sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==", + "peer": true, + "dependencies": { + "undici-types": "~5.25.1" + } + }, + "node_modules/@types/uuid": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.6.tgz", + "integrity": "sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew==" + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "node_modules/azle": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/azle/-/azle-0.18.5.tgz", + "integrity": "sha512-9lLhVKwexVCd8OOYMXgjk06L+Mu0q29Rpchmz07HYV6idwF5kg+8QCxzytpU9GpnUeoADTQNkEkyLABtkd2hCA==", + "dependencies": { + "@dfinity/candid": "github:demergent-labs/candid#minimum_viable", + "@dfinity/principal": "^0.19.0", + "@swc/core": "^1.3.86", + "@types/uuid": "^9.0.4", + "buffer": "^6.0.3", + "esbuild": "^0.19.3", + "fs-extra": "10.0.1", + "js-sha256": "0.9.0", + "text-encoding": "0.7.0", + "ts-node": "10.3.1", + "typescript": "^5.2.2", + "uuid": "^9.0.1" + }, + "bin": { + "azle": "bin.js" + } + }, + "node_modules/azle/node_modules/@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "dependencies": { + "@cspotcode/source-map-consumer": "0.8.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/azle/node_modules/@dfinity/candid": { + "version": "0.19.2", + "resolved": "git+ssh://git@github.com/demergent-labs/candid.git#5797fa906b1a7cc30c161dbb0eb919283ce2f80d", + "license": "Apache-2.0", + "peerDependencies": { + "@dfinity/principal": "^0.19.2" + } + }, + "node_modules/azle/node_modules/ts-node": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.3.1.tgz", + "integrity": "sha512-Yw3W2mYzhHfCHOICGNJqa0i+rbL0rAyg7ZIHxU+K4pgY8gd2Lh1j+XbHCusJMykbj6RZMJVOY0MlHVd+GOivcw==", + "dependencies": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/esbuild": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz", + "integrity": "sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.19.5", + "@esbuild/android-arm64": "0.19.5", + "@esbuild/android-x64": "0.19.5", + "@esbuild/darwin-arm64": "0.19.5", + "@esbuild/darwin-x64": "0.19.5", + "@esbuild/freebsd-arm64": "0.19.5", + "@esbuild/freebsd-x64": "0.19.5", + "@esbuild/linux-arm": "0.19.5", + "@esbuild/linux-arm64": "0.19.5", + "@esbuild/linux-ia32": "0.19.5", + "@esbuild/linux-loong64": "0.19.5", + "@esbuild/linux-mips64el": "0.19.5", + "@esbuild/linux-ppc64": "0.19.5", + "@esbuild/linux-riscv64": "0.19.5", + "@esbuild/linux-s390x": "0.19.5", + "@esbuild/linux-x64": "0.19.5", + "@esbuild/netbsd-x64": "0.19.5", + "@esbuild/openbsd-x64": "0.19.5", + "@esbuild/sunos-x64": "0.19.5", + "@esbuild/win32-arm64": "0.19.5", + "@esbuild/win32-ia32": "0.19.5", + "@esbuild/win32-x64": "0.19.5" + } + }, + "node_modules/fs-extra": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "node_modules/text-encoding": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", + "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", + "deprecated": "no longer maintained" + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.25.3", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", + "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", + "peer": true + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/property_tests/tests/canister_methods/http_request/package.json b/property_tests/tests/canister_methods/http_request/package.json new file mode 100644 index 0000000000..e5b0aa2544 --- /dev/null +++ b/property_tests/tests/canister_methods/http_request/package.json @@ -0,0 +1,12 @@ +{ + "scripts": { + "test": "ts-node --transpile-only --ignore=false test/test.ts" + }, + "dependencies": { + "azle": "^0.18.5" + }, + "devDependencies": { + "ts-node": "^10.9.1", + "typescript": "^5.2.2" + } +} diff --git a/property_tests/tests/canister_methods/http_request/tsconfig.json b/property_tests/tests/canister_methods/http_request/tsconfig.json new file mode 100644 index 0000000000..d20bea88d9 --- /dev/null +++ b/property_tests/tests/canister_methods/http_request/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "strict": true, + "target": "ES2020", + "moduleResolution": "node", + "allowJs": true, + "outDir": "HACK_BECAUSE_OF_ALLOW_JS", + "esModuleInterop": true + } +} From 8d33bd06244d4572b2a3eeaa5c15a71700683ec0 Mon Sep 17 00:00:00 2001 From: Dan Steren Date: Wed, 20 Dec 2023 15:51:51 -0700 Subject: [PATCH 02/22] Pull http gateway types into Azle --- .../motoko_examples/http_counter/src/index.ts | 114 ++++++----------- src/lib/candid/types/constructed/variant.ts | 12 +- src/lib/candid/types/reference/func.ts | 3 +- src/lib/canister_methods/http_gateway.ts | 115 ++++++++++++++++++ src/lib/canister_methods/index.ts | 2 + 5 files changed, 168 insertions(+), 78 deletions(-) create mode 100644 src/lib/canister_methods/http_gateway.ts diff --git a/examples/motoko_examples/http_counter/src/index.ts b/examples/motoko_examples/http_counter/src/index.ts index f212e6fb44..4bcad112c4 100644 --- a/examples/motoko_examples/http_counter/src/index.ts +++ b/examples/motoko_examples/http_counter/src/index.ts @@ -1,23 +1,20 @@ import { blob, - bool, Canister, - Func, + HeaderField, + HttpRequest, + HttpResponse, ic, init, nat, - nat16, None, - Opt, query, Record, Some, StableBTreeMap, + StreamingCallbackHttpResponse, text, - Tuple, - update, - Variant, - Vec + update } from 'azle'; const Token = Record({ @@ -25,48 +22,13 @@ const Token = Record({ arbitrary_data: text }); -const StreamingCallbackHttpResponse = Record({ - body: blob, - token: Opt(Token) -}); - -export const Callback = Func([text], StreamingCallbackHttpResponse, 'query'); - -const CallbackStrategy = Record({ - callback: Callback, - token: Token -}); - -const StreamingStrategy = Variant({ - Callback: CallbackStrategy -}); - -type HeaderField = [text, text]; -const HeaderField = Tuple(text, text); - -const HttpResponse = Record({ - status_code: nat16, - headers: Vec(HeaderField), - body: blob, - streaming_strategy: Opt(StreamingStrategy), - upgrade: Opt(bool) -}); - -const HttpRequest = Record({ - method: text, - url: text, - headers: Vec(HeaderField), - body: blob, - certificate_version: Opt(nat16) -}); - let stableStorage = StableBTreeMap(0); export default Canister({ init: init([], () => { stableStorage.insert('counter', 0n); }), - http_request: query([HttpRequest], HttpResponse, (req) => { + http_request: query([HttpRequest], HttpResponse(Token), (req) => { console.log('Hello from http_request'); if (req.method === 'GET') { @@ -141,7 +103,7 @@ export default Canister({ upgrade: None }; }), - http_request_update: update([HttpRequest], HttpResponse, (req) => { + http_request_update: update([HttpRequest], HttpResponse(Token), (req) => { if (req.method === 'POST') { const counterOpt = stableStorage.get('counter'); const counter = @@ -195,38 +157,42 @@ export default Canister({ upgrade: None }; }), - http_streaming: query([Token], StreamingCallbackHttpResponse, (token) => { - console.log('Hello from http_streaming'); - switch (token.arbitrary_data) { - case 'start': { - return { - body: encode(' is '), - token: Some({ arbitrary_data: 'next' }) - }; - } - case 'next': { - const counterOpt = stableStorage.get('counter'); - const counter = - 'None' in counterOpt - ? ic.trap('counter does not exist') - : counterOpt.Some; + http_streaming: query( + [Token], + StreamingCallbackHttpResponse(Token), + (token) => { + console.log('Hello from http_streaming'); + switch (token.arbitrary_data) { + case 'start': { + return { + body: encode(' is '), + token: Some({ arbitrary_data: 'next' }) + }; + } + case 'next': { + const counterOpt = stableStorage.get('counter'); + const counter = + 'None' in counterOpt + ? ic.trap('counter does not exist') + : counterOpt.Some; - return { - body: encode(`${counter}`), - token: Some({ arbitrary_data: 'last' }) - }; - } - case 'last': { - return { - body: encode(' streaming\n'), - token: None - }; - } - default: { - return ic.trap('unreachable'); + return { + body: encode(`${counter}`), + token: Some({ arbitrary_data: 'last' }) + }; + } + case 'last': { + return { + body: encode(' streaming\n'), + token: None + }; + } + default: { + return ic.trap('unreachable'); + } } } - }) + ) }); function isGzip(x: HeaderField): boolean { diff --git a/src/lib/candid/types/constructed/variant.ts b/src/lib/candid/types/constructed/variant.ts index 67ba5df80b..12bd89a26b 100644 --- a/src/lib/candid/types/constructed/variant.ts +++ b/src/lib/candid/types/constructed/variant.ts @@ -5,6 +5,14 @@ import { IDL } from '@dfinity/candid'; import { decode } from '../../serde/decode'; import { encode } from '../../serde/encode'; +export type Variant< + T extends { + [K in keyof T]: any; + } +> = RequireExactlyOne<{ + [K in keyof T]: TypeMapping; +}>; + export function Variant< T extends { [K in keyof T]: CandidType; @@ -12,9 +20,7 @@ export function Variant< >(obj: T) { return { ...obj, - tsType: {} as RequireExactlyOne<{ - [K in keyof T]: TypeMapping; - }>, + tsType: {} as Variant, toBytes(data: any) { return encode(this, data); }, diff --git a/src/lib/candid/types/reference/func.ts b/src/lib/candid/types/reference/func.ts index 34ba5f9173..5bad7a0f17 100644 --- a/src/lib/candid/types/reference/func.ts +++ b/src/lib/candid/types/reference/func.ts @@ -5,6 +5,7 @@ import { encode } from '../../serde/encode'; import { decode } from '../../serde/decode'; import { Serializable } from '../../../stable_structures/stable_b_tree_map'; +export type Func = [Principal, string]; type Mode = 'query' | 'update' | 'oneway'; const modeToCandid = { @@ -19,7 +20,7 @@ export function Func( mode: Mode ) { return { - tsType: {} as [Principal, string], + tsType: {} as Func, toBytes(data: any) { return encode(this, data); }, diff --git a/src/lib/canister_methods/http_gateway.ts b/src/lib/canister_methods/http_gateway.ts new file mode 100644 index 0000000000..5251b25a66 --- /dev/null +++ b/src/lib/canister_methods/http_gateway.ts @@ -0,0 +1,115 @@ +// See https://internetcomputer.org/docs/current/references/http-gateway-protocol-spec#canister-http-interface + +import { blob } from '../candid/types/constructed/blob'; +import { bool } from '../candid/types/primitive/bool'; +import { Func } from '../candid/types/reference/func'; +import { nat16 } from '../candid/types/primitive/nats/nat16'; +import { Opt } from '../candid/types/constructed/opt'; +import { Record } from '../candid/types/constructed/record'; +import { text } from '../candid/types/primitive/text'; +import { Tuple } from '../candid/types/constructed/tuple'; +import { + RequireExactlyOne, + Variant +} from '../candid/types/constructed/variant'; +import { Vec } from '../candid/types/constructed/vec'; +import { CandidType } from '../candid'; + +export const HeaderField = Tuple(text, text); +export type HeaderField = typeof HeaderField.tsType; + +export const HttpRequest = Record({ + /** The HTTP method in all upper case letters, e.g. "GET" */ + method: text, + /** + * The URL from the HTTP request line, i.e. without protocol or hostname, + * and includes query parameters + */ + url: text, + /** The headers of the HTTP request */ + headers: Vec(HeaderField), + /** + * The body of the HTTP request (without any content encodings processed by + * the HTTP Gateway) + */ + body: blob, + /** + * Indicates the maximum supported version of [response verification]( + * https://internetcomputer.org/docs/current/references/http-gateway-protocol-spec#response-verification). + * - A value of `2` will request the current standard of [response + * verification](https://internetcomputer.org/docs/current/references/http-gateway-protocol-spec#response-verification), + * while a missing version or a value of `1` will request the [legacy + * standard](https://internetcomputer.org/docs/current/references/http-gateway-protocol-spec#legacy-response-verification). + * - Current HTTP Gateway implementations will always request version `2`, + * but older HTTP Gateways may still request version `1`. + */ + certificate_version: Opt(nat16) +}); +export type HttpRequest = typeof HttpRequest.tsType; + +export const HttpUpdateRequest = Record({ + method: text, + url: text, + headers: Vec(HeaderField), + body: blob +}); +export type HttpUpdateRequest = typeof HttpUpdateRequest.tsType; + +export function StreamingCallbackHttpResponse( + token: Token +) { + return Record({ + body: blob, + token: Opt(token) + }); +} +type StreamingCallbackHttpResponse = { + body: Uint8Array; + token: Opt; +}; + +function Callback(token: Token) { + return Func([token], Opt(StreamingCallbackHttpResponse(token)), 'query'); +} +type Callback = Func; + +function CallbackStrategy(token: Token) { + return Record({ + callback: Callback(token), + token + }); +} +export type CallbackStrategy = { + callback: Callback; + token: Token; +}; + +function StreamingStrategy(token: Token) { + const thing = Variant({ + Callback: CallbackStrategy(token) + }); + + thing.tsType; + + return thing; +} +export type StreamingStrategy = Variant<{ + Callback: CallbackStrategy; +}>; + +export function HttpResponse(token: Token) { + return Record({ + status_code: nat16, + headers: Vec(HeaderField), + body: blob, + upgrade: Opt(bool), + streaming_strategy: Opt(StreamingStrategy(token)) + }); +} +export type HttpResponse = { + status_code: number; + headers: HeaderField[]; + body: Uint8Array; + upgrade: Opt; + streaming_strategy: Opt>; +}; diff --git a/src/lib/canister_methods/index.ts b/src/lib/canister_methods/index.ts index 4f1821bd0e..db04c6a62f 100644 --- a/src/lib/canister_methods/index.ts +++ b/src/lib/canister_methods/index.ts @@ -1 +1,3 @@ +export * from './http_gateway'; export * from './methods'; +export * from './types'; From 2c293c8c796eeca36ba795ba86d9a112bff2f7b1 Mon Sep 17 00:00:00 2001 From: Dan Steren Date: Wed, 27 Dec 2023 15:21:23 -0700 Subject: [PATCH 03/22] Add HttpRequestMethodArb --- .../canister_methods/query_method_arb.ts | 4 +- property_tests/arbitraries/http/body_arb.ts | 5 + .../arbitraries/http/headers_arb.ts | 15 +++ .../arbitraries/http/request_arb.ts | 101 ++++++++++++++++++ .../arbitraries/http/response_arb.ts | 86 +++++++++++++++ .../http_request/test/fletch.ts | 56 ++++++++++ .../http_request/test/generate_body.ts | 95 ++++++++++++++++ .../http_request/test/generate_tests.ts | 47 ++++++++ .../http_request/test/test.ts | 37 +++++++ src/lib/canister_methods/http_gateway.ts | 11 +- 10 files changed, 447 insertions(+), 10 deletions(-) create mode 100644 property_tests/arbitraries/http/body_arb.ts create mode 100644 property_tests/arbitraries/http/headers_arb.ts create mode 100644 property_tests/arbitraries/http/request_arb.ts create mode 100644 property_tests/arbitraries/http/response_arb.ts create mode 100644 property_tests/tests/canister_methods/http_request/test/fletch.ts create mode 100644 property_tests/tests/canister_methods/http_request/test/generate_body.ts create mode 100644 property_tests/tests/canister_methods/http_request/test/generate_tests.ts create mode 100644 property_tests/tests/canister_methods/http_request/test/test.ts diff --git a/property_tests/arbitraries/canister_methods/query_method_arb.ts b/property_tests/arbitraries/canister_methods/query_method_arb.ts index 6570cdd437..1e48c894ba 100644 --- a/property_tests/arbitraries/canister_methods/query_method_arb.ts +++ b/property_tests/arbitraries/canister_methods/query_method_arb.ts @@ -51,6 +51,7 @@ export function QueryMethodArb< ReturnTypeAgentResponseValue >; callbackLocation?: CallbackLocation; + name?: string; } ) { return fc @@ -66,7 +67,7 @@ export function QueryMethodArb< ) .map( ([ - functionName, + defaultFunctionName, paramTypes, returnType, defaultCallbackLocation, @@ -74,6 +75,7 @@ export function QueryMethodArb< ]): QueryMethod => { const callbackLocation = constraints.callbackLocation ?? defaultCallbackLocation; + const functionName = constraints.name ?? defaultFunctionName; const imports = new Set([ 'query', diff --git a/property_tests/arbitraries/http/body_arb.ts b/property_tests/arbitraries/http/body_arb.ts new file mode 100644 index 0000000000..ae8a711cb6 --- /dev/null +++ b/property_tests/arbitraries/http/body_arb.ts @@ -0,0 +1,5 @@ +import fc from 'fast-check'; + +export function BodyArb() { + return fc.json().map((json) => new Uint8Array(Buffer.from(json, 'utf-8'))); +} diff --git a/property_tests/arbitraries/http/headers_arb.ts b/property_tests/arbitraries/http/headers_arb.ts new file mode 100644 index 0000000000..e1fedeef9c --- /dev/null +++ b/property_tests/arbitraries/http/headers_arb.ts @@ -0,0 +1,15 @@ +// See https://developers.cloudflare.com/rules/transform/request-header-modification/reference/header-format/ + +import fc from 'fast-check'; + +export function HttpHeadersArb() { + const HeaderNameArb = fc.stringMatching(/^[a-zA-Z0-9_-]+$/); + + const HeaderValueArb = fc + .stringMatching(/^[a-zA-Z0-9_ :;.,\/"'?!(){}[\]@<>=\-+*#$&`|~^%]+$/) + .map((value) => value.trim()); + + const HeaderArb = fc.tuple(HeaderNameArb, HeaderValueArb); + + return fc.array(HeaderArb); +} diff --git a/property_tests/arbitraries/http/request_arb.ts b/property_tests/arbitraries/http/request_arb.ts new file mode 100644 index 0000000000..694e1edb75 --- /dev/null +++ b/property_tests/arbitraries/http/request_arb.ts @@ -0,0 +1,101 @@ +import fc from 'fast-check'; +import { HttpHeadersArb } from './headers_arb'; +import { BodyArb } from './body_arb'; +import { HttpRequest, None, Some } from '../../../src/lib'; +import { CandidValueAndMeta } from '../candid/candid_value_and_meta_arb'; +import { blobToSrcLiteral } from '../candid/to_src_literal/blob'; +import { stringToSrcLiteral } from '../candid/to_src_literal/string'; + +type RequestMethod = + // | 'CONNECT' // Results in WARN hyper::proto::h1::role: unexpected content-length found, canceling + | 'DELETE' + | 'GET' + // | 'HEAD' // Results in the body getting dropped + | 'OPTIONS' + | 'PATCH' + | 'POST' + | 'PUT' + | 'TRACE'; + +const RequestMethodArb = fc.constantFrom( + 'GET', + 'POST', + 'PUT', + 'DELETE', + 'OPTIONS', + 'TRACE', + 'PATCH' +); + +const UrlArb = fc.webUrl({ withQueryParameters: true }).map((url) => { + const parsedUrl = new URL(url); + return parsedUrl.pathname + parsedUrl.search + parsedUrl.hash; +}); + +function HttpRequestValueArb() { + return fc + .tuple( + RequestMethodArb, + UrlArb, + HttpHeadersArb(), + BodyArb(), + fc + .option(fc.integer({ min: 0, max: 2 ** 16 - 1 })) + .map((optCertVer) => { + return optCertVer === null ? None : Some(optCertVer); + }) + ) + .map(([method, url, headers, body, certificate_version]) => { + return { + method, + url, + headers, + body, + certificate_version + }; + }); +} + +export function HttpRequestArb(): fc.Arbitrary< + CandidValueAndMeta +> { + return HttpRequestValueArb().map((httpRequest) => { + const headerStrings = httpRequest.headers + .map( + ([name, value]) => + `[${stringToSrcLiteral(name)},${stringToSrcLiteral(value)}]` + ) + .join(','); + + const bodySrc = blobToSrcLiteral(httpRequest.body); + + const certificateVersion = + 'Some' in httpRequest.certificate_version + ? `Some(${httpRequest.certificate_version.Some})` + : `None`; + + const optImport = + 'Some' in httpRequest.certificate_version ? 'Some' : 'None'; + + return { + value: { + agentArgumentValue: httpRequest, + agentResponseValue: httpRequest, + runtimeCandidTypeObject: HttpRequest + }, + src: { + candidTypeAnnotation: 'HttpRequest', + candidTypeObject: 'HttpRequest', + variableAliasDeclarations: [], + imports: new Set(['HttpRequest', optImport]), + valueLiteral: `{ + method: '${httpRequest.method}', + url: '${httpRequest.url}', + headers: [${headerStrings}], + body: ${bodySrc}, + certificate_version: ${certificateVersion} + }` + } + }; + }); +} diff --git a/property_tests/arbitraries/http/response_arb.ts b/property_tests/arbitraries/http/response_arb.ts new file mode 100644 index 0000000000..0de5562a70 --- /dev/null +++ b/property_tests/arbitraries/http/response_arb.ts @@ -0,0 +1,86 @@ +import fc from 'fast-check'; +import { HttpResponse, None } from '../../../src/lib'; +import { HttpHeadersArb } from './headers_arb'; +import { BodyArb } from './body_arb'; +import { CandidValueAndMeta } from '../candid/candid_value_and_meta_arb'; +import { CorrespondingJSType } from '../candid/corresponding_js_type'; +import { blobToSrcLiteral } from '../candid/to_src_literal/blob'; +import { stringToSrcLiteral } from '../candid/to_src_literal/string'; + +export type HttpResponseAgentResponseValue = { + status: number; + headers: [string, string][]; + body: string; +}; + +// The ic replica doesn't support returning status codes in the 1xx range. +const StatusCodeArb = fc.integer({ min: 200, max: 599 }); + +export function HttpResponseValueArb() { + return fc + .tuple(StatusCodeArb, HttpHeadersArb(), BodyArb()) + .map(([status_code, headers, body]): HttpResponse => { + const thing: HttpResponse = { + status_code, + headers, + body, + upgrade: None, + streaming_strategy: None + }; + return thing; + }); +} +export function HttpResponseArb( + token: CandidValueAndMeta +): fc.Arbitrary< + CandidValueAndMeta, HttpResponseAgentResponseValue> +> { + return HttpResponseValueArb().map((response) => { + const lowerCasedHeaders = response.headers.map<[string, string]>( + ([name, value]) => [name.toLowerCase(), value] + ); + + const agentResponseValue = { + status: response.status_code, + headers: lowerCasedHeaders, + body: new TextDecoder().decode(response.body) + }; + + const headerStrings = response.headers + .map( + ([name, value]) => + `[${stringToSrcLiteral(name)},${stringToSrcLiteral(value)}]` + ) + .join(','); + + const bodySrc = blobToSrcLiteral(response.body); + + return { + value: { + agentArgumentValue: response, + agentResponseValue: agentResponseValue, + runtimeCandidTypeObject: HttpResponse( + token.value.runtimeCandidTypeObject + ) + }, + src: { + candidTypeAnnotation: `HttpResponse<${token.src.candidTypeAnnotation}>`, + candidTypeObject: `HttpResponse(${token.src.candidTypeObject})`, + variableAliasDeclarations: token.src.variableAliasDeclarations, + imports: new Set([ + 'HttpResponse', + 'bool', + 'None', + ...token.src.imports + ]), + valueLiteral: `{ + status_code: ${response.status_code}, + headers: [${headerStrings}], + body: ${bodySrc}, + upgrade: None, + streaming_strategy: None + }` + } + }; + }); +} diff --git a/property_tests/tests/canister_methods/http_request/test/fletch.ts b/property_tests/tests/canister_methods/http_request/test/fletch.ts new file mode 100644 index 0000000000..323575bc6e --- /dev/null +++ b/property_tests/tests/canister_methods/http_request/test/fletch.ts @@ -0,0 +1,56 @@ +import { execSync } from 'child_process'; +import { HttpRequest } from 'azle'; +import { getCanisterId } from 'azle/test'; + +/** + * A synchronous "fetch" for canisters. + */ +export function fletch(canisterName: string, options: HttpRequest) { + const canisterId = getCanisterId(canisterName); + + const requestHeaders = toCurlHeadersString(options.headers); + + const curlCommand = `curl\ + --silent\ + --include\ + -X ${options.method}\ + ${requestHeaders}\ + --data "${options.body.join(',')}"\ + "${canisterId}.localhost:8000${options.url}" \ + --resolve "${canisterId}.localhost:8000:127.0.0.1"`; + + const responseBuffer = execSync(curlCommand); + + return toResponse(responseBuffer); +} + +function toCurlHeadersString(headers: [string, string][]) { + return headers + .map(([name, value]) => `-H ${singleQuotedString(`${name}: ${value}`)}`) + .join(' '); +} + +function singleQuotedString(input: string) { + const singleQuoteEscapedString = input.replace(/'/g, "'\\''"); + + return `'${singleQuoteEscapedString}'`; +} + +function toResponse(buffer: Buffer) { + const responseString = new TextDecoder().decode(buffer); + + const [statusCodeAndHeaders, body] = responseString.split('\r\n\r\n'); + + const [statusCodeString, ...responseHeaderStrings] = + statusCodeAndHeaders.split('\r\n'); + + const status = Number(statusCodeString.split(' ')[1]); + + const headers = responseHeaderStrings.map((header) => header.split(': ')); + + return { + status, + headers, + body + }; +} diff --git a/property_tests/tests/canister_methods/http_request/test/generate_body.ts b/property_tests/tests/canister_methods/http_request/test/generate_body.ts new file mode 100644 index 0000000000..182117424e --- /dev/null +++ b/property_tests/tests/canister_methods/http_request/test/generate_body.ts @@ -0,0 +1,95 @@ +import { HttpRequest } from 'azle'; +import { Named } from 'azle/property_tests'; +import { CandidReturnType } from 'azle/property_tests/arbitraries/candid/candid_return_type_arb'; +import { CandidValueAndMeta } from 'azle/property_tests/arbitraries/candid/candid_value_and_meta_arb'; + +export function generateBody( + namedParams: Named>[], + returnType: CandidValueAndMeta +): string { + const { name: requestParamName, value: requestValueObject } = + namedParams[0]; + const request = requestValueObject.value.agentArgumentValue; + + const httpMethodCheck = generateHttpMethodCheck( + request.method, + requestParamName + ); + const urlCheck = generateUrlCheck(request.url, requestParamName); + const headersMap = generateHeadersMap(request.headers, requestParamName); + const headerChecks = generateHeaderChecks(request.headers); + + return ` + ${httpMethodCheck} + ${urlCheck} + ${headersMap} + ${headerChecks} + + return ${returnType.src.valueLiteral}; + `; +} + +function generateHttpMethodCheck(method: string, requestParamName: string) { + return /*TS*/ ` + if (${requestParamName}.method !== '${method}') { + throw new Error( + \`Unexpected req.method. Expected ${method} but received \${${requestParamName}.method}\` + ); + } + `; +} + +function generateUrlCheck(url: string, requestParamName: string) { + return /*TS*/ ` + if (${requestParamName}.url !== '${escape(url)}') { + throw new Error( + \`Unexpected req.url. Expected '${escape( + url + )}' but received \${${requestParamName}.url}\` + ); + } + `; +} + +function generateHeadersMap( + headers: [string, string][], + requestParamName: string +) { + return headers.length === 0 + ? '' + : /*TS*/ ` + const headers = (${requestParamName}.headers as [string, string][]).reduce<{ + [key: string]: string} + >((prev, [name, value]) => ({[name]: value, ...prev}), {}); + `; +} + +function generateHeaderChecks(headers: [string, string][]) { + return headers + .map( + ([name, value]) => ` + if (headers['${escape(name).toLowerCase()}'] !== '${escape( + value + )}') { + throw new Error( + \`Unexpected value for header '${escape( + name + )}'. Expected '${escape( + value + )}' but received '\${headers['${escape( + name + ).toLowerCase()}']}'\` + ); + } + ` + ) + .join('\n'); +} + +function escape(input: string) { + return input + .replace(/\\/g, '\\\\') // Escape backslashes + .replace(/`/g, '\\`') // Escape backticks + .replace(/'/g, "\\'") // Escape single quotes + .replace(/"/g, '\\"'); // Escape double quotes +} diff --git a/property_tests/tests/canister_methods/http_request/test/generate_tests.ts b/property_tests/tests/canister_methods/http_request/test/generate_tests.ts new file mode 100644 index 0000000000..867befdcf5 --- /dev/null +++ b/property_tests/tests/canister_methods/http_request/test/generate_tests.ts @@ -0,0 +1,47 @@ +import { HttpRequest, HttpResponse } from 'azle'; +import { deepEqual, Named } from 'azle/property_tests'; +import { CandidValueAndMeta } from 'azle/property_tests/arbitraries/candid/candid_value_and_meta_arb'; +import { HttpResponseAgentResponseValue } from 'azle/property_tests/arbitraries/http/response_arb'; +import { Test } from 'azle/test'; + +import { fletch } from './fletch'; + +export function generateTests( + functionName: string, + params: Named>[], + returnType: CandidValueAndMeta< + HttpResponse, + HttpResponseAgentResponseValue + > +): Test[][] { + const request = params[0].value.value.agentArgumentValue; + const expectedResponse = returnType.value.agentResponseValue; + + return [ + [ + { + name: functionName, + test: async () => { + const response = fletch('canister', request); + const filteredHeaders = response.headers.filter( + ([name]) => + name !== 'x-ic-streaming-response' && + name !== 'content-length' && + name !== 'date' + ); + const processedResponse = { + status: response.status, + headers: filteredHeaders, + body: response.body + }; + const valuesAreEqual = deepEqual( + processedResponse, + expectedResponse + ); + + return { Ok: valuesAreEqual }; + } + } + ] + ]; +} diff --git a/property_tests/tests/canister_methods/http_request/test/test.ts b/property_tests/tests/canister_methods/http_request/test/test.ts new file mode 100644 index 0000000000..5faf83fa1f --- /dev/null +++ b/property_tests/tests/canister_methods/http_request/test/test.ts @@ -0,0 +1,37 @@ +import fc from 'fast-check'; + +import { HttpRequest } from 'azle'; +import { runPropTests } from 'azle/property_tests'; +import { + CanisterArb, + CanisterConfig +} from 'azle/property_tests/arbitraries/canister_arb'; +import { HttpRequestArb } from 'azle/property_tests/arbitraries/http/request_arb'; +import { HttpResponseArb } from 'azle/property_tests/arbitraries/http/response_arb'; +import { QueryMethodArb } from 'azle/property_tests/arbitraries/canister_methods/query_method_arb'; +import { RecordArb } from 'azle/property_tests/arbitraries/candid/constructed/record_arb'; + +import { generateBody } from './generate_body'; +import { generateTests } from './generate_tests'; + +const CanisterConfigArb = RecordArb() + .chain((record) => { + const HttpRequestMethodArb = QueryMethodArb( + fc.tuple(HttpRequestArb()), + HttpResponseArb(record), + { + name: 'http_request', + generateBody, + generateTests + } + ); + + return HttpRequestMethodArb; + }) + .map((httpRequestMethod): CanisterConfig => { + return { + queryMethods: [httpRequestMethod] + }; + }); + +runPropTests(CanisterArb(CanisterConfigArb)); diff --git a/src/lib/canister_methods/http_gateway.ts b/src/lib/canister_methods/http_gateway.ts index 5251b25a66..8d3b1e3f71 100644 --- a/src/lib/canister_methods/http_gateway.ts +++ b/src/lib/canister_methods/http_gateway.ts @@ -8,10 +8,7 @@ import { Opt } from '../candid/types/constructed/opt'; import { Record } from '../candid/types/constructed/record'; import { text } from '../candid/types/primitive/text'; import { Tuple } from '../candid/types/constructed/tuple'; -import { - RequireExactlyOne, - Variant -} from '../candid/types/constructed/variant'; +import { Variant } from '../candid/types/constructed/variant'; import { Vec } from '../candid/types/constructed/vec'; import { CandidType } from '../candid'; @@ -85,13 +82,9 @@ export type CallbackStrategy = { }; function StreamingStrategy(token: Token) { - const thing = Variant({ + return Variant({ Callback: CallbackStrategy(token) }); - - thing.tsType; - - return thing; } export type StreamingStrategy = Variant<{ Callback: CallbackStrategy; From 98b670b1f5ce204a18aef512d3109e21869a2ce2 Mon Sep 17 00:00:00 2001 From: Dan Steren Date: Thu, 28 Dec 2023 12:33:03 -0700 Subject: [PATCH 04/22] Escape Interpolation starts And remove TS syntax highlight comments. It gets confused by the backticks and starts highlighting weird stuff. --- .../canister_methods/http_request/test/generate_body.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/property_tests/tests/canister_methods/http_request/test/generate_body.ts b/property_tests/tests/canister_methods/http_request/test/generate_body.ts index 182117424e..eb0572f916 100644 --- a/property_tests/tests/canister_methods/http_request/test/generate_body.ts +++ b/property_tests/tests/canister_methods/http_request/test/generate_body.ts @@ -30,7 +30,7 @@ export function generateBody( } function generateHttpMethodCheck(method: string, requestParamName: string) { - return /*TS*/ ` + return ` if (${requestParamName}.method !== '${method}') { throw new Error( \`Unexpected req.method. Expected ${method} but received \${${requestParamName}.method}\` @@ -40,7 +40,7 @@ function generateHttpMethodCheck(method: string, requestParamName: string) { } function generateUrlCheck(url: string, requestParamName: string) { - return /*TS*/ ` + return ` if (${requestParamName}.url !== '${escape(url)}') { throw new Error( \`Unexpected req.url. Expected '${escape( @@ -57,7 +57,7 @@ function generateHeadersMap( ) { return headers.length === 0 ? '' - : /*TS*/ ` + : ` const headers = (${requestParamName}.headers as [string, string][]).reduce<{ [key: string]: string} >((prev, [name, value]) => ({[name]: value, ...prev}), {}); @@ -89,6 +89,7 @@ function generateHeaderChecks(headers: [string, string][]) { function escape(input: string) { return input .replace(/\\/g, '\\\\') // Escape backslashes + .replace(/\$\{/g, '\\${') // Escape interpolation starts .replace(/`/g, '\\`') // Escape backticks .replace(/'/g, "\\'") // Escape single quotes .replace(/"/g, '\\"'); // Escape double quotes From 94d95f0c5e486e1bc40c51419e42bee284e1b958 Mon Sep 17 00:00:00 2001 From: Dan Steren Date: Thu, 28 Dec 2023 13:01:58 -0700 Subject: [PATCH 05/22] Remove body for 204s When returning a 204 No Content status code the body is dropped. --- property_tests/arbitraries/http/response_arb.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/property_tests/arbitraries/http/response_arb.ts b/property_tests/arbitraries/http/response_arb.ts index 0de5562a70..a766190c68 100644 --- a/property_tests/arbitraries/http/response_arb.ts +++ b/property_tests/arbitraries/http/response_arb.ts @@ -43,7 +43,10 @@ export function HttpResponseArb( const agentResponseValue = { status: response.status_code, headers: lowerCasedHeaders, - body: new TextDecoder().decode(response.body) + body: + response.status_code === 204 // No Content + ? '' + : new TextDecoder().decode(response.body) }; const headerStrings = response.headers From f4bc5867dcd6b1a0ca56bc2dedd3c52372535a05 Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Tue, 23 Jan 2024 13:46:10 -0700 Subject: [PATCH 06/22] sort headers --- .../http_request/test/generate_tests.ts | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/property_tests/tests/canister_methods/http_request/test/generate_tests.ts b/property_tests/tests/canister_methods/http_request/test/generate_tests.ts index 867befdcf5..b98f7277f0 100644 --- a/property_tests/tests/canister_methods/http_request/test/generate_tests.ts +++ b/property_tests/tests/canister_methods/http_request/test/generate_tests.ts @@ -23,20 +23,29 @@ export function generateTests( name: functionName, test: async () => { const response = fletch('canister', request); - const filteredHeaders = response.headers.filter( - ([name]) => - name !== 'x-ic-streaming-response' && - name !== 'content-length' && - name !== 'date' - ); + const filteredHeaders = response.headers + .filter( + ([name]) => + name !== 'x-ic-streaming-response' && + name !== 'content-length' && + name !== 'date' + ) + .sort(); const processedResponse = { status: response.status, headers: filteredHeaders, body: response.body }; + const filteredExpectedHeaders = + expectedResponse.headers.sort(); + const processedExpectedResponse = { + status: expectedResponse.status, + headers: filteredExpectedHeaders, + body: expectedResponse.body + }; const valuesAreEqual = deepEqual( processedResponse, - expectedResponse + processedExpectedResponse ); return { Ok: valuesAreEqual }; From b563a2df65dbe52e9a4bc2826fa12b67ce03d390 Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Tue, 23 Jan 2024 16:04:18 -0700 Subject: [PATCH 07/22] remove special characters as per https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6 --- property_tests/arbitraries/http/headers_arb.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/property_tests/arbitraries/http/headers_arb.ts b/property_tests/arbitraries/http/headers_arb.ts index e1fedeef9c..87a7f93685 100644 --- a/property_tests/arbitraries/http/headers_arb.ts +++ b/property_tests/arbitraries/http/headers_arb.ts @@ -6,7 +6,7 @@ export function HttpHeadersArb() { const HeaderNameArb = fc.stringMatching(/^[a-zA-Z0-9_-]+$/); const HeaderValueArb = fc - .stringMatching(/^[a-zA-Z0-9_ :;.,\/"'?!(){}[\]@<>=\-+*#$&`|~^%]+$/) + .stringMatching(/^[a-zA-Z0-9_ .'!-+*#$&`|~^%]+$/) .map((value) => value.trim()); const HeaderArb = fc.tuple(HeaderNameArb, HeaderValueArb); From 3933d895e3bb7223a2e6c22e94e4a5441fc0fa2c Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Wed, 24 Jan 2024 12:56:58 -0700 Subject: [PATCH 08/22] make sure headers have unique keys --- property_tests/arbitraries/http/headers_arb.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/property_tests/arbitraries/http/headers_arb.ts b/property_tests/arbitraries/http/headers_arb.ts index 87a7f93685..f368b799e9 100644 --- a/property_tests/arbitraries/http/headers_arb.ts +++ b/property_tests/arbitraries/http/headers_arb.ts @@ -11,5 +11,5 @@ export function HttpHeadersArb() { const HeaderArb = fc.tuple(HeaderNameArb, HeaderValueArb); - return fc.array(HeaderArb); + return fc.uniqueArray(HeaderArb, { selector: ([name, _]) => name }); } From dace8e7e042abc3d9f0336c63368c643e7972c30 Mon Sep 17 00:00:00 2001 From: Dan Steren Date: Fri, 29 Dec 2023 13:33:41 -0700 Subject: [PATCH 09/22] Add HttpRequestUpdateMethodArb --- .github/workflows/test.yml | 1 + .../canister_methods/update_method_arb.ts | 4 +- .../http_request_update/dfx.json | 16 + .../http_request_update/package-lock.json | 996 ++++++++++++++++++ .../http_request_update/package.json | 12 + .../http_request_update/test/fletch.ts | 56 + .../http_request_update/test/generate_body.ts | 98 ++ .../test/generate_tests.ts | 63 ++ .../http_request_update/test/test.ts | 78 ++ .../http_request_update/tsconfig.json | 10 + 10 files changed, 1333 insertions(+), 1 deletion(-) create mode 100644 property_tests/tests/canister_methods/http_request_update/dfx.json create mode 100644 property_tests/tests/canister_methods/http_request_update/package-lock.json create mode 100644 property_tests/tests/canister_methods/http_request_update/package.json create mode 100644 property_tests/tests/canister_methods/http_request_update/test/fletch.ts create mode 100644 property_tests/tests/canister_methods/http_request_update/test/generate_body.ts create mode 100644 property_tests/tests/canister_methods/http_request_update/test/generate_tests.ts create mode 100644 property_tests/tests/canister_methods/http_request_update/test/test.ts create mode 100644 property_tests/tests/canister_methods/http_request_update/tsconfig.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7aa856c38a..bf80998590 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -134,6 +134,7 @@ jobs: "property_tests/tests/blob", "property_tests/tests/bool", "property_tests/tests/canister_methods/http_request", + "property_tests/tests/canister_methods/http_request_update", "property_tests/tests/canister_methods/init", "property_tests/tests/canister_methods/post_upgrade", "property_tests/tests/canister_methods/pre_upgrade", diff --git a/property_tests/arbitraries/canister_methods/update_method_arb.ts b/property_tests/arbitraries/canister_methods/update_method_arb.ts index 6a964492d7..7c58dfdba5 100644 --- a/property_tests/arbitraries/canister_methods/update_method_arb.ts +++ b/property_tests/arbitraries/canister_methods/update_method_arb.ts @@ -51,6 +51,7 @@ export function UpdateMethodArb< ReturnTypeAgentResponseValue >; callbackLocation?: CallbackLocation; + name?: string; } ) { return fc @@ -66,7 +67,7 @@ export function UpdateMethodArb< ) .map( ([ - functionName, + defaultFunctionName, paramTypes, returnType, defaultCallbackLocation, @@ -74,6 +75,7 @@ export function UpdateMethodArb< ]): UpdateMethod => { const callbackLocation = constraints.callbackLocation ?? defaultCallbackLocation; + const functionName = constraints.name ?? defaultFunctionName; const imports = new Set([ 'update', diff --git a/property_tests/tests/canister_methods/http_request_update/dfx.json b/property_tests/tests/canister_methods/http_request_update/dfx.json new file mode 100644 index 0000000000..6ab9fbd7e4 --- /dev/null +++ b/property_tests/tests/canister_methods/http_request_update/dfx.json @@ -0,0 +1,16 @@ +{ + "canisters": { + "canister": { + "type": "custom", + "main": "src/index.ts", + "candid": "src/index.did", + "build": "npx azle canister", + "wasm": ".azle/canister/canister.wasm", + "gzip": true, + "declarations": { + "output": "test/dfx_generated/canister", + "node_compatibility": true + } + } + } +} diff --git a/property_tests/tests/canister_methods/http_request_update/package-lock.json b/property_tests/tests/canister_methods/http_request_update/package-lock.json new file mode 100644 index 0000000000..7205533208 --- /dev/null +++ b/property_tests/tests/canister_methods/http_request_update/package-lock.json @@ -0,0 +1,996 @@ +{ + "name": "http_request_update", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "azle": "^0.18.5" + }, + "devDependencies": { + "ts-node": "^10.9.1", + "typescript": "^5.2.2" + } + }, + "node_modules/@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@dfinity/principal": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@dfinity/principal/-/principal-0.19.3.tgz", + "integrity": "sha512-+nixVvdGt7ECxRvLXDXsvU9q9sSPssBtDQ4bXa149SK6gcYcmZ6lfWIi3DJNqj3tGROxILVBsguel9tECappsA==", + "dependencies": { + "@noble/hashes": "^1.3.1" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.5.tgz", + "integrity": "sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.5.tgz", + "integrity": "sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.5.tgz", + "integrity": "sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz", + "integrity": "sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.5.tgz", + "integrity": "sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.5.tgz", + "integrity": "sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.5.tgz", + "integrity": "sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.5.tgz", + "integrity": "sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz", + "integrity": "sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.5.tgz", + "integrity": "sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.5.tgz", + "integrity": "sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.5.tgz", + "integrity": "sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.5.tgz", + "integrity": "sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.5.tgz", + "integrity": "sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.5.tgz", + "integrity": "sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz", + "integrity": "sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.5.tgz", + "integrity": "sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.5.tgz", + "integrity": "sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.5.tgz", + "integrity": "sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.5.tgz", + "integrity": "sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.5.tgz", + "integrity": "sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.5.tgz", + "integrity": "sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@swc/core": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.93.tgz", + "integrity": "sha512-690GRr1wUGmGYZHk7fUduX/JUwViMF2o74mnZYIWEcJaCcd9MQfkhsxPBtjeg6tF+h266/Cf3RPYhsFBzzxXcA==", + "hasInstallScript": true, + "dependencies": { + "@swc/counter": "^0.1.1", + "@swc/types": "^0.1.5" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.3.93", + "@swc/core-darwin-x64": "1.3.93", + "@swc/core-linux-arm-gnueabihf": "1.3.93", + "@swc/core-linux-arm64-gnu": "1.3.93", + "@swc/core-linux-arm64-musl": "1.3.93", + "@swc/core-linux-x64-gnu": "1.3.93", + "@swc/core-linux-x64-musl": "1.3.93", + "@swc/core-win32-arm64-msvc": "1.3.93", + "@swc/core-win32-ia32-msvc": "1.3.93", + "@swc/core-win32-x64-msvc": "1.3.93" + }, + "peerDependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.93.tgz", + "integrity": "sha512-gEKgk7FVIgltnIfDO6GntyuQBBlAYg5imHpRgLxB1zSI27ijVVkksc6QwISzFZAhKYaBWIsFSVeL9AYSziAF7A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.93.tgz", + "integrity": "sha512-ZQPxm/fXdDQtn3yrYSL/gFfA8OfZ5jTi33yFQq6vcg/Y8talpZ+MgdSlYM0FkLrZdMTYYTNFiuBQuuvkA+av+Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.93.tgz", + "integrity": "sha512-OYFMMI2yV+aNe3wMgYhODxHdqUB/jrK0SEMHHS44GZpk8MuBXEF+Mcz4qjkY5Q1EH7KVQqXb/gVWwdgTHpjM2A==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.93.tgz", + "integrity": "sha512-BT4dT78odKnJMNiq5HdjBsv29CiIdcCcImAPxeFqAeFw1LL6gh9nzI8E96oWc+0lVT5lfhoesCk4Qm7J6bty8w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.93.tgz", + "integrity": "sha512-yH5fWEl1bktouC0mhh0Chuxp7HEO4uCtS/ly1Vmf18gs6wZ8DOOkgAEVv2dNKIryy+Na++ljx4Ym7C8tSJTrLw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.93.tgz", + "integrity": "sha512-OFUdx64qvrGJhXKEyxosHxgoUVgba2ztYh7BnMiU5hP8lbI8G13W40J0SN3CmFQwPP30+3oEbW7LWzhKEaYjlg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.93.tgz", + "integrity": "sha512-4B8lSRwEq1XYm6xhxHhvHmKAS7pUp1Q7E33NQ2TlmFhfKvCOh86qvThcjAOo57x8DRwmpvEVrqvpXtYagMN6Ig==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.93.tgz", + "integrity": "sha512-BHShlxtkven8ZjjvZ5QR6sC5fZCJ9bMujEkiha6W4cBUTY7ce7qGFyHmQd+iPC85d9kD/0cCiX/Xez8u0BhO7w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.93.tgz", + "integrity": "sha512-nEwNWnz4JzYAK6asVvb92yeylfxMYih7eMQOnT7ZVlZN5ba9WF29xJ6kcQKs9HRH6MvWhz9+wRgv3FcjlU6HYA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.93", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.93.tgz", + "integrity": "sha512-jibQ0zUr4kwJaQVwgmH+svS04bYTPnPw/ZkNInzxS+wFAtzINBYcU8s2PMWbDb2NGYiRSEeoSGyAvS9H+24JFA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz", + "integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==" + }, + "node_modules/@swc/types": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", + "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" + }, + "node_modules/@types/node": { + "version": "20.8.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.7.tgz", + "integrity": "sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==", + "peer": true, + "dependencies": { + "undici-types": "~5.25.1" + } + }, + "node_modules/@types/uuid": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.6.tgz", + "integrity": "sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew==" + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "node_modules/azle": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/azle/-/azle-0.18.5.tgz", + "integrity": "sha512-9lLhVKwexVCd8OOYMXgjk06L+Mu0q29Rpchmz07HYV6idwF5kg+8QCxzytpU9GpnUeoADTQNkEkyLABtkd2hCA==", + "dependencies": { + "@dfinity/candid": "github:demergent-labs/candid#minimum_viable", + "@dfinity/principal": "^0.19.0", + "@swc/core": "^1.3.86", + "@types/uuid": "^9.0.4", + "buffer": "^6.0.3", + "esbuild": "^0.19.3", + "fs-extra": "10.0.1", + "js-sha256": "0.9.0", + "text-encoding": "0.7.0", + "ts-node": "10.3.1", + "typescript": "^5.2.2", + "uuid": "^9.0.1" + }, + "bin": { + "azle": "bin.js" + } + }, + "node_modules/azle/node_modules/@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "dependencies": { + "@cspotcode/source-map-consumer": "0.8.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/azle/node_modules/@dfinity/candid": { + "version": "0.19.2", + "resolved": "git+ssh://git@github.com/demergent-labs/candid.git#5797fa906b1a7cc30c161dbb0eb919283ce2f80d", + "license": "Apache-2.0", + "peerDependencies": { + "@dfinity/principal": "^0.19.2" + } + }, + "node_modules/azle/node_modules/ts-node": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.3.1.tgz", + "integrity": "sha512-Yw3W2mYzhHfCHOICGNJqa0i+rbL0rAyg7ZIHxU+K4pgY8gd2Lh1j+XbHCusJMykbj6RZMJVOY0MlHVd+GOivcw==", + "dependencies": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/esbuild": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz", + "integrity": "sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.19.5", + "@esbuild/android-arm64": "0.19.5", + "@esbuild/android-x64": "0.19.5", + "@esbuild/darwin-arm64": "0.19.5", + "@esbuild/darwin-x64": "0.19.5", + "@esbuild/freebsd-arm64": "0.19.5", + "@esbuild/freebsd-x64": "0.19.5", + "@esbuild/linux-arm": "0.19.5", + "@esbuild/linux-arm64": "0.19.5", + "@esbuild/linux-ia32": "0.19.5", + "@esbuild/linux-loong64": "0.19.5", + "@esbuild/linux-mips64el": "0.19.5", + "@esbuild/linux-ppc64": "0.19.5", + "@esbuild/linux-riscv64": "0.19.5", + "@esbuild/linux-s390x": "0.19.5", + "@esbuild/linux-x64": "0.19.5", + "@esbuild/netbsd-x64": "0.19.5", + "@esbuild/openbsd-x64": "0.19.5", + "@esbuild/sunos-x64": "0.19.5", + "@esbuild/win32-arm64": "0.19.5", + "@esbuild/win32-ia32": "0.19.5", + "@esbuild/win32-x64": "0.19.5" + } + }, + "node_modules/fs-extra": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "node_modules/text-encoding": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", + "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", + "deprecated": "no longer maintained" + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.25.3", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", + "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", + "peer": true + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/property_tests/tests/canister_methods/http_request_update/package.json b/property_tests/tests/canister_methods/http_request_update/package.json new file mode 100644 index 0000000000..e5b0aa2544 --- /dev/null +++ b/property_tests/tests/canister_methods/http_request_update/package.json @@ -0,0 +1,12 @@ +{ + "scripts": { + "test": "ts-node --transpile-only --ignore=false test/test.ts" + }, + "dependencies": { + "azle": "^0.18.5" + }, + "devDependencies": { + "ts-node": "^10.9.1", + "typescript": "^5.2.2" + } +} diff --git a/property_tests/tests/canister_methods/http_request_update/test/fletch.ts b/property_tests/tests/canister_methods/http_request_update/test/fletch.ts new file mode 100644 index 0000000000..323575bc6e --- /dev/null +++ b/property_tests/tests/canister_methods/http_request_update/test/fletch.ts @@ -0,0 +1,56 @@ +import { execSync } from 'child_process'; +import { HttpRequest } from 'azle'; +import { getCanisterId } from 'azle/test'; + +/** + * A synchronous "fetch" for canisters. + */ +export function fletch(canisterName: string, options: HttpRequest) { + const canisterId = getCanisterId(canisterName); + + const requestHeaders = toCurlHeadersString(options.headers); + + const curlCommand = `curl\ + --silent\ + --include\ + -X ${options.method}\ + ${requestHeaders}\ + --data "${options.body.join(',')}"\ + "${canisterId}.localhost:8000${options.url}" \ + --resolve "${canisterId}.localhost:8000:127.0.0.1"`; + + const responseBuffer = execSync(curlCommand); + + return toResponse(responseBuffer); +} + +function toCurlHeadersString(headers: [string, string][]) { + return headers + .map(([name, value]) => `-H ${singleQuotedString(`${name}: ${value}`)}`) + .join(' '); +} + +function singleQuotedString(input: string) { + const singleQuoteEscapedString = input.replace(/'/g, "'\\''"); + + return `'${singleQuoteEscapedString}'`; +} + +function toResponse(buffer: Buffer) { + const responseString = new TextDecoder().decode(buffer); + + const [statusCodeAndHeaders, body] = responseString.split('\r\n\r\n'); + + const [statusCodeString, ...responseHeaderStrings] = + statusCodeAndHeaders.split('\r\n'); + + const status = Number(statusCodeString.split(' ')[1]); + + const headers = responseHeaderStrings.map((header) => header.split(': ')); + + return { + status, + headers, + body + }; +} diff --git a/property_tests/tests/canister_methods/http_request_update/test/generate_body.ts b/property_tests/tests/canister_methods/http_request_update/test/generate_body.ts new file mode 100644 index 0000000000..fa8d4a1513 --- /dev/null +++ b/property_tests/tests/canister_methods/http_request_update/test/generate_body.ts @@ -0,0 +1,98 @@ +import { HttpRequest } from 'azle'; +import { Named } from 'azle/property_tests'; +import { CandidReturnType } from 'azle/property_tests/arbitraries/candid/candid_return_type_arb'; +import { CandidValueAndMeta } from 'azle/property_tests/arbitraries/candid/candid_value_and_meta_arb'; + +export function generateBody( + namedParams: Named>[], + returnType: CandidValueAndMeta +): string { + const { name: requestParamName, value: requestValueObject } = + namedParams[0]; + const request = requestValueObject.value.agentArgumentValue; + + const httpMethodCheck = generateHttpMethodCheck( + request.method, + requestParamName + ); + const urlCheck = generateUrlCheck(request.url, requestParamName); + const headersMap = generateHeadersMap(request.headers, requestParamName); + const headerChecks = generateHeaderChecks(request.headers); + + return ` + state++; + + ${httpMethodCheck} + ${urlCheck} + ${headersMap} + ${headerChecks} + + return ${returnType.src.valueLiteral}; + `; +} + +function generateHttpMethodCheck(method: string, requestParamName: string) { + return ` + if (${requestParamName}.method !== '${method}') { + throw new Error( + \`Unexpected req.method. Expected ${method} but received \${${requestParamName}.method}\` + ); + } + `; +} + +function generateUrlCheck(url: string, requestParamName: string) { + return ` + if (${requestParamName}.url !== '${escape(url)}') { + throw new Error( + \`Unexpected req.url. Expected '${escape( + url + )}' but received \${${requestParamName}.url}\` + ); + } + `; +} + +function generateHeadersMap( + headers: [string, string][], + requestParamName: string +) { + return headers.length === 0 + ? '' + : ` + const headers = (${requestParamName}.headers as [string, string][]).reduce<{ + [key: string]: string} + >((prev, [name, value]) => ({[name]: value, ...prev}), {}); + `; +} + +function generateHeaderChecks(headers: [string, string][]) { + return headers + .map( + ([name, value]) => ` + if (headers['${escape(name).toLowerCase()}'] !== '${escape( + value + )}') { + throw new Error( + \`Unexpected value for header '${escape( + name + )}'. Expected '${escape( + value + )}' but received '\${headers['${escape( + name + ).toLowerCase()}']}'\` + ); + } + ` + ) + .join('\n'); +} + +function escape(input: string) { + return input + .replace(/\\/g, '\\\\') // Escape backslashes + .replace(/\$\{/g, '\\${') // Escape interpolation starts + .replace(/`/g, '\\`') // Escape backticks + .replace(/'/g, "\\'") // Escape single quotes + .replace(/"/g, '\\"'); // Escape double quotes +} diff --git a/property_tests/tests/canister_methods/http_request_update/test/generate_tests.ts b/property_tests/tests/canister_methods/http_request_update/test/generate_tests.ts new file mode 100644 index 0000000000..e1d76b5f0b --- /dev/null +++ b/property_tests/tests/canister_methods/http_request_update/test/generate_tests.ts @@ -0,0 +1,63 @@ +import { HttpRequest, HttpResponse } from 'azle'; +import { deepEqual, Named } from 'azle/property_tests'; +import { CandidValueAndMeta } from 'azle/property_tests/arbitraries/candid/candid_value_and_meta_arb'; +import { HttpResponseAgentResponseValue } from 'azle/property_tests/arbitraries/http/response_arb'; +import { Test } from 'azle/test'; + +import { fletch } from './fletch'; + +export function generateTests( + functionName: string, + params: Named>[], + returnType: CandidValueAndMeta< + HttpResponse, + HttpResponseAgentResponseValue + > +): Test[][] { + const request = params[0].value.value.agentArgumentValue; + const expectedResponse = returnType.value.agentResponseValue; + + return [ + [ + { + name: 'get state before http_request', + test: async () => { + const TODO = true; + // TODO: check that the state was 0 + return { Ok: TODO }; + } + }, + { + name: functionName, + test: async () => { + const response = fletch('canister', request); + const filteredHeaders = response.headers.filter( + ([name]) => + name !== 'x-ic-streaming-response' && + name !== 'content-length' && + name !== 'date' + ); + const processedResponse = { + status: response.status, + headers: filteredHeaders, + body: response.body + }; + const valuesAreEqual = deepEqual( + processedResponse, + expectedResponse + ); + + return { Ok: valuesAreEqual }; + } + }, + { + name: 'get state after calling http_request', + test: async () => { + const TODO = true; + // TODO: check that the state is one more than it was before. + return { Ok: TODO }; + } + } + ] + ]; +} diff --git a/property_tests/tests/canister_methods/http_request_update/test/test.ts b/property_tests/tests/canister_methods/http_request_update/test/test.ts new file mode 100644 index 0000000000..58dd7923b7 --- /dev/null +++ b/property_tests/tests/canister_methods/http_request_update/test/test.ts @@ -0,0 +1,78 @@ +import fc from 'fast-check'; + +import { HttpRequest } from 'azle'; +import { runPropTests } from 'azle/property_tests'; +import { + CanisterArb, + CanisterConfig +} from 'azle/property_tests/arbitraries/canister_arb'; +import { HttpRequestArb } from 'azle/property_tests/arbitraries/http/request_arb'; +import { HttpResponseArb } from 'azle/property_tests/arbitraries/http/response_arb'; +import { QueryMethod } from 'azle/property_tests/arbitraries/canister_methods/query_method_arb'; +import { RecordArb } from 'azle/property_tests/arbitraries/candid/constructed/record_arb'; + +import { generateBody } from './generate_body'; +import { generateTests } from './generate_tests'; +import { UpdateMethodArb } from '../../../../arbitraries/canister_methods/update_method_arb'; + +const HttpRequestUpdateMethodArb = RecordArb().chain((record) => { + const HttpRequestMethodArb = UpdateMethodArb( + fc.tuple(HttpRequestArb()), + HttpResponseArb(record), + { + name: 'http_request_update', + generateBody, + generateTests + } + ); + + return HttpRequestMethodArb; +}); + +const CanisterConfigArb = HttpRequestUpdateMethodArb.map( + (httpRequestUpdateMethod): CanisterConfig => { + const httpRequestMethod = generateHttpRequestMethod(); + const getStateMethod = generateGetStateMethod(); + + return { + queryMethods: [httpRequestMethod, getStateMethod], + updateMethods: [httpRequestUpdateMethod] + }; + } +); + +runPropTests(CanisterArb(CanisterConfigArb)); + +function generateHttpRequestMethod(): QueryMethod { + return { + imports: new Set([ + 'query', + 'HttpRequest', + 'HttpResponse', + 'Null', + 'Some' + ]), + globalDeclarations: [], + sourceCode: /*TS*/ `http_request: query([HttpRequest], HttpResponse(Null), () => { + return { + status_code: 204, + headers: [], + body: new Uint8Array(), + streaming_strategy: None, + upgrade: Some(true) + }; + })`, + tests: [] + }; +} + +function generateGetStateMethod(): QueryMethod { + return { + imports: new Set(['query', 'nat8']), + globalDeclarations: ['let state: number = 0;'], + sourceCode: /*TS*/ `get_state: query([], nat8, () => { + return state; + })`, + tests: [] + }; +} diff --git a/property_tests/tests/canister_methods/http_request_update/tsconfig.json b/property_tests/tests/canister_methods/http_request_update/tsconfig.json new file mode 100644 index 0000000000..d20bea88d9 --- /dev/null +++ b/property_tests/tests/canister_methods/http_request_update/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "strict": true, + "target": "ES2020", + "moduleResolution": "node", + "allowJs": true, + "outDir": "HACK_BECAUSE_OF_ALLOW_JS", + "esModuleInterop": true + } +} From 2f40bccc154b49bcbb066c4675bf8bb9f8893fb7 Mon Sep 17 00:00:00 2001 From: Dan Steren Date: Fri, 29 Dec 2023 15:42:28 -0700 Subject: [PATCH 10/22] Add pre/post http_request tests --- .../test/generate_tests.ts | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/property_tests/tests/canister_methods/http_request_update/test/generate_tests.ts b/property_tests/tests/canister_methods/http_request_update/test/generate_tests.ts index e1d76b5f0b..acebd69a3b 100644 --- a/property_tests/tests/canister_methods/http_request_update/test/generate_tests.ts +++ b/property_tests/tests/canister_methods/http_request_update/test/generate_tests.ts @@ -1,5 +1,5 @@ import { HttpRequest, HttpResponse } from 'azle'; -import { deepEqual, Named } from 'azle/property_tests'; +import { deepEqual, Named, getActor } from 'azle/property_tests'; import { CandidValueAndMeta } from 'azle/property_tests/arbitraries/candid/candid_value_and_meta_arb'; import { HttpResponseAgentResponseValue } from 'azle/property_tests/arbitraries/http/response_arb'; import { Test } from 'azle/test'; @@ -20,11 +20,15 @@ export function generateTests( return [ [ { - name: 'get state before http_request', + name: 'get state before calling http_request', test: async () => { - const TODO = true; - // TODO: check that the state was 0 - return { Ok: TODO }; + const actor = getActor(__dirname); + + const result = await actor['get_state'](); + + return { + Ok: deepEqual(result, 0) + }; } }, { @@ -53,9 +57,13 @@ export function generateTests( { name: 'get state after calling http_request', test: async () => { - const TODO = true; - // TODO: check that the state is one more than it was before. - return { Ok: TODO }; + const actor = getActor(__dirname); + + const result = await actor['get_state'](); + + return { + Ok: deepEqual(result, 1) + }; } } ] From 476098cc859274ab7af0baedc0f33e956add9791 Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Wed, 24 Jan 2024 14:21:46 -0700 Subject: [PATCH 11/22] filter out empty request headers --- .../canister_methods/http_request_update/test/generate_body.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/property_tests/tests/canister_methods/http_request_update/test/generate_body.ts b/property_tests/tests/canister_methods/http_request_update/test/generate_body.ts index fa8d4a1513..4ea22e06e2 100644 --- a/property_tests/tests/canister_methods/http_request_update/test/generate_body.ts +++ b/property_tests/tests/canister_methods/http_request_update/test/generate_body.ts @@ -68,6 +68,7 @@ function generateHeadersMap( function generateHeaderChecks(headers: [string, string][]) { return headers + .filter(([_, value]) => value !== '') // An empty header value is the same as a none existent header .map( ([name, value]) => ` if (headers['${escape(name).toLowerCase()}'] !== '${escape( From bf70d5fc0d973a010db6b06ee169771dcb0bbc0c Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Thu, 25 Jan 2024 13:26:24 -0700 Subject: [PATCH 12/22] property encode the arbitrary path --- .../arbitraries/http/request_arb.ts | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/property_tests/arbitraries/http/request_arb.ts b/property_tests/arbitraries/http/request_arb.ts index 694e1edb75..0120f5aac2 100644 --- a/property_tests/arbitraries/http/request_arb.ts +++ b/property_tests/arbitraries/http/request_arb.ts @@ -27,16 +27,36 @@ const RequestMethodArb = fc.constantFrom( 'PATCH' ); -const UrlArb = fc.webUrl({ withQueryParameters: true }).map((url) => { +const UrlPathArb = fc.webUrl({ withQueryParameters: true }).map((url) => { const parsedUrl = new URL(url); - return parsedUrl.pathname + parsedUrl.search + parsedUrl.hash; + // fc.webUrl gives us a path that is already encoded, but only partly. I + // have found it leaving $ and @ unencoded. So to be safe we have to decode + // all of the values (to prevent % from being encoded) and then reencode the + // values to encode symbols such as $ and @ + const pathname = parsedUrl.pathname + .split('/') + .map((value) => encodeString(value)) + .join('/'); + const search = encodeString(parsedUrl.search, '?'); + const hash = encodeString(parsedUrl.hash, '#'); + return `${pathname}${search}${hash}`; }); +function encodeString(search: string, prefix: string = ''): string { + if (search.length === 0) { + return search; + } + + return `${prefix}${encodeURIComponent( + decodeURIComponent(search.slice(prefix.length)) + )}`; +} + function HttpRequestValueArb() { return fc .tuple( RequestMethodArb, - UrlArb, + UrlPathArb, HttpHeadersArb(), BodyArb(), fc From 48b7e10daafd8ddae04b3cdbfe7daa6a3f6a36e6 Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Thu, 25 Jan 2024 15:28:09 -0700 Subject: [PATCH 13/22] apply same header filter as in http_update --- .../tests/canister_methods/http_request/test/generate_body.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/property_tests/tests/canister_methods/http_request/test/generate_body.ts b/property_tests/tests/canister_methods/http_request/test/generate_body.ts index eb0572f916..4f7cef8f12 100644 --- a/property_tests/tests/canister_methods/http_request/test/generate_body.ts +++ b/property_tests/tests/canister_methods/http_request/test/generate_body.ts @@ -66,6 +66,7 @@ function generateHeadersMap( function generateHeaderChecks(headers: [string, string][]) { return headers + .filter(([_, value]) => value !== '') // An empty header value is the same as a none existent header .map( ([name, value]) => ` if (headers['${escape(name).toLowerCase()}'] !== '${escape( From 489e545e64822265b54717a52cff006437008867 Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Fri, 26 Jan 2024 15:21:27 -0700 Subject: [PATCH 14/22] update http_counter example to use fetch reenable it --- .../http_counter/src/index.did | 4 +- .../http_counter/test/tests.ts | 91 +++++++++---------- 2 files changed, 43 insertions(+), 52 deletions(-) diff --git a/examples/motoko_examples/http_counter/src/index.did b/examples/motoko_examples/http_counter/src/index.did index f4dc2c9977..93e7830bc9 100644 --- a/examples/motoko_examples/http_counter/src/index.did +++ b/examples/motoko_examples/http_counter/src/index.did @@ -1,5 +1,5 @@ service: () -> { - http_request: (record {url:text; method:text; body:vec nat8; headers:vec record {text; text}; certificate_version:opt nat16}) -> (record {body:vec nat8; headers:vec record {text; text}; upgrade:opt bool; streaming_strategy:opt variant {Callback:record {token:record {arbitrary_data:text}; callback:func (text) -> (record {token:opt record {arbitrary_data:text}; body:vec nat8}) query}}; status_code:nat16}) query; - http_request_update: (record {url:text; method:text; body:vec nat8; headers:vec record {text; text}; certificate_version:opt nat16}) -> (record {body:vec nat8; headers:vec record {text; text}; upgrade:opt bool; streaming_strategy:opt variant {Callback:record {token:record {arbitrary_data:text}; callback:func (text) -> (record {token:opt record {arbitrary_data:text}; body:vec nat8}) query}}; status_code:nat16}); + http_request: (record {url:text; method:text; body:vec nat8; headers:vec record {text; text}; certificate_version:opt nat16}) -> (record {body:vec nat8; headers:vec record {text; text}; upgrade:opt bool; streaming_strategy:opt variant {Callback:record {token:record {arbitrary_data:text}; callback:func (record {arbitrary_data:text}) -> (opt record {token:opt record {arbitrary_data:text}; body:vec nat8}) query}}; status_code:nat16}) query; + http_request_update: (record {url:text; method:text; body:vec nat8; headers:vec record {text; text}; certificate_version:opt nat16}) -> (record {body:vec nat8; headers:vec record {text; text}; upgrade:opt bool; streaming_strategy:opt variant {Callback:record {token:record {arbitrary_data:text}; callback:func (record {arbitrary_data:text}) -> (opt record {token:opt record {arbitrary_data:text}; body:vec nat8}) query}}; status_code:nat16}); http_streaming: (record {arbitrary_data:text}) -> (record {token:opt record {arbitrary_data:text}; body:vec nat8}) query; } diff --git a/examples/motoko_examples/http_counter/test/tests.ts b/examples/motoko_examples/http_counter/test/tests.ts index 4453730ea7..ed44e5bb74 100644 --- a/examples/motoko_examples/http_counter/test/tests.ts +++ b/examples/motoko_examples/http_counter/test/tests.ts @@ -1,5 +1,7 @@ import { Test } from 'azle/test'; import { execSync } from 'child_process'; +import * as dns from 'node:dns'; +dns.setDefaultResultOrder('ipv4first'); const skip = true; @@ -7,73 +9,67 @@ export function getTests(): Test[] { return [ { name: 'init get count', - skip, test: async () => { return { - Ok: getCount() === getExpectedGetCountResult(0) + Ok: (await getCount()) === getExpectedGetCountResult(0) }; } }, { name: 'first increment', - skip, test: async () => { return { - Ok: count() === getExpectedCountResult(1) + Ok: (await count()) === getExpectedCountResult(1) }; } }, { name: 'second increment', - skip, test: async () => { return { - Ok: count() === getExpectedCountResult(2) + Ok: (await count()) === getExpectedCountResult(2) }; } }, { name: 'get count', - skip, test: async () => { return { - Ok: getCount() === getExpectedGetCountResult(2) + Ok: (await getCount()) === getExpectedGetCountResult(2) }; } }, { name: 'gzipped increment', - skip, test: async () => { return { - Ok: countGzip() === 'update' + Ok: (await countGzip()) === 'update' }; } }, { name: 'get gzipped count', - skip, test: async () => { return { - Ok: getCountGzip() === 'query' + Ok: (await getCountGzip()) === 'query' }; } }, { name: 'get streaming count', - skip, test: async () => { return { - Ok: getCountStream() === getExpectedGetCountStreamResult(3) + Ok: + (await getCountStream()) === + getExpectedGetCountStreamResult(3) }; } }, { name: 'final get count', - skip, test: async () => { return { - Ok: getCount() === getExpectedGetCountResult(3) + Ok: (await getCount()) === getExpectedGetCountResult(3) }; } } @@ -84,49 +80,44 @@ function getCanisterID(): string { return execSync(`dfx canister id http_counter`).toString().trim(); } -function count(): string { +function getUrl(): string { const canister_id = getCanisterID(); - return execSync( - `curl --silent -X POST "${canister_id}.localhost:8000/" --resolve "${canister_id}.localhost:8000:127.0.0.1"` - ) - .toString() - .trim(); + return `http://${canister_id}.localhost:8000/`; } -function countGzip(): string { - const canister_id = getCanisterID(); - return execSync( - `curl --compressed --silent -X POST "${canister_id}.localhost:8000/" --resolve "${canister_id}.localhost:8000:127.0.0.1"` - ) - .toString() - .trim(); +async function count(): Promise { + const response = await fetch(getUrl(), { + method: 'POST', + headers: [['accept-encoding', '']] + }); + return (await response.text()).trim(); } -function getCount(): string { - const canister_id = getCanisterID(); - return execSync( - `curl --silent "${canister_id}.localhost:8000/" --resolve "${canister_id}.localhost:8000:127.0.0.1"` - ) - .toString() - .trim(); +async function countGzip(): Promise { + const response = await fetch(getUrl(), { + method: 'POST' + }); + return (await response.text()).trim(); } -function getCountStream(): string { - const canister_id = getCanisterID(); - return execSync( - `curl --silent "${canister_id}.localhost:8000/stream" --resolve "${canister_id}.localhost:8000:127.0.0.1"` - ) - .toString() - .trim(); +async function getCount(): Promise { + const response = await fetch(getUrl(), { + headers: [['accept-encoding', '']] + }); + return (await response.text()).trim(); } -function getCountGzip(): string { - const canister_id = getCanisterID(); - return execSync( - `curl --compressed --silent "${canister_id}.localhost:8000/" --resolve "${canister_id}.localhost:8000:127.0.0.1"` - ) - .toString() - .trim(); +async function getCountStream(): Promise { + const streamUrl = `${getUrl()}stream`; + const response = await fetch(streamUrl, { + headers: [['accept-encoding', '']] + }); + return (await response.text()).trim(); +} + +async function getCountGzip(): Promise { + const response = await fetch(getUrl()); + return (await response.text()).trim(); } function getExpectedGetCountResult(expectedCount: number): string { From b56f365fe9ba83ddda1a05bc96e03de2ac687705 Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Mon, 29 Jan 2024 13:54:12 -0700 Subject: [PATCH 15/22] change to fetch for the property tests --- .../arbitraries/http/request_arb.ts | 16 +++++-- .../http_request/test/fletch.ts | 47 +++++++++++++++++-- .../http_request/test/generate_body.ts | 36 ++++++++++---- .../http_request/test/generate_tests.ts | 9 ++-- .../http_request_update/test/fletch.ts | 47 +++++++++++++++++-- .../http_request_update/test/generate_body.ts | 36 ++++++++++---- .../test/generate_tests.ts | 25 ++++++---- 7 files changed, 175 insertions(+), 41 deletions(-) diff --git a/property_tests/arbitraries/http/request_arb.ts b/property_tests/arbitraries/http/request_arb.ts index 0120f5aac2..01aa6881fb 100644 --- a/property_tests/arbitraries/http/request_arb.ts +++ b/property_tests/arbitraries/http/request_arb.ts @@ -65,15 +65,23 @@ function HttpRequestValueArb() { return optCertVer === null ? None : Some(optCertVer); }) ) - .map(([method, url, headers, body, certificate_version]) => { - return { + .map( + ([ method, url, headers, body, certificate_version - }; - }); + ]): HttpRequest => { + return { + method, + url, + headers, + body, + certificate_version + }; + } + ); } export function HttpRequestArb(): fc.Arbitrary< diff --git a/property_tests/tests/canister_methods/http_request/test/fletch.ts b/property_tests/tests/canister_methods/http_request/test/fletch.ts index 323575bc6e..6d89bf8ec0 100644 --- a/property_tests/tests/canister_methods/http_request/test/fletch.ts +++ b/property_tests/tests/canister_methods/http_request/test/fletch.ts @@ -1,11 +1,52 @@ import { execSync } from 'child_process'; import { HttpRequest } from 'azle'; import { getCanisterId } from 'azle/test'; +import * as dns from 'node:dns'; +dns.setDefaultResultOrder('ipv4first'); + +type HttpResponse = { + status: number; + headers: string[][]; + body: string; +}; + +/** + * An asynchronous "fetch" for canisters. + */ +export async function fletch( + canisterName: string, + request: HttpRequest +): Promise { + if (request.method === 'TRACE' || request.method == 'PATCH') { + return fletchSync(canisterName, request); + } + const canisterId = getCanisterId(canisterName); + + const { method, body, headers, url: path } = request; + + const url = `http://${canisterId}.localhost:8000${path}`; + + const fetchOptions = { + method, + headers, + body: method === 'GET' ? undefined : body + }; + + return await toResponse(await fetch(url, fetchOptions)); +} + +async function toResponse(response: Response): Promise { + const headers: [string, string][] = Array.from(response.headers.entries()); + const status = response.status; + const body = await response.text(); + + return { status, headers, body }; +} /** * A synchronous "fetch" for canisters. */ -export function fletch(canisterName: string, options: HttpRequest) { +export function fletchSync(canisterName: string, options: HttpRequest) { const canisterId = getCanisterId(canisterName); const requestHeaders = toCurlHeadersString(options.headers); @@ -21,7 +62,7 @@ export function fletch(canisterName: string, options: HttpRequest) { const responseBuffer = execSync(curlCommand); - return toResponse(responseBuffer); + return toResponseSync(responseBuffer); } function toCurlHeadersString(headers: [string, string][]) { @@ -36,7 +77,7 @@ function singleQuotedString(input: string) { return `'${singleQuoteEscapedString}'`; } -function toResponse(buffer: Buffer) { +function toResponseSync(buffer: Buffer) { const responseString = new TextDecoder().decode(buffer); const [statusCodeAndHeaders, body] = responseString.split('\r\n\r\n'); diff --git a/property_tests/tests/canister_methods/http_request/test/generate_body.ts b/property_tests/tests/canister_methods/http_request/test/generate_body.ts index 4f7cef8f12..a8d0c9a541 100644 --- a/property_tests/tests/canister_methods/http_request/test/generate_body.ts +++ b/property_tests/tests/canister_methods/http_request/test/generate_body.ts @@ -66,12 +66,32 @@ function generateHeadersMap( function generateHeaderChecks(headers: [string, string][]) { return headers - .filter(([_, value]) => value !== '') // An empty header value is the same as a none existent header - .map( - ([name, value]) => ` - if (headers['${escape(name).toLowerCase()}'] !== '${escape( - value - )}') { + .map(([name, value]) => { + if (value === '') { + return generateEmptyHeaderCheck(name); + } + return generateNonEmptyHeaderCheck(name, value); + }) + .join('\n'); +} + +function generateEmptyHeaderCheck(name: string) { + return `if (headers['${escape(name).toLowerCase()}'] !== undefined) { + throw new Error( + \`Unexpected value for header '${escape( + name + )}'. Expected undefined but received '\${headers['${escape( + name + ).toLowerCase()}']}'\` + ); + } + `; +} + +function generateNonEmptyHeaderCheck(name: string, value: string): string { + return `if (headers['${escape(name).toLowerCase()}'] !== '${escape( + value + )}') { throw new Error( \`Unexpected value for header '${escape( name @@ -82,9 +102,7 @@ function generateHeaderChecks(headers: [string, string][]) { ).toLowerCase()}']}'\` ); } - ` - ) - .join('\n'); + `; } function escape(input: string) { diff --git a/property_tests/tests/canister_methods/http_request/test/generate_tests.ts b/property_tests/tests/canister_methods/http_request/test/generate_tests.ts index b98f7277f0..b49d79eb40 100644 --- a/property_tests/tests/canister_methods/http_request/test/generate_tests.ts +++ b/property_tests/tests/canister_methods/http_request/test/generate_tests.ts @@ -22,7 +22,7 @@ export function generateTests( { name: functionName, test: async () => { - const response = fletch('canister', request); + const response = await fletch('canister', request); const filteredHeaders = response.headers .filter( ([name]) => @@ -36,12 +36,11 @@ export function generateTests( headers: filteredHeaders, body: response.body }; - const filteredExpectedHeaders = + const sortedExpectedHeaders = expectedResponse.headers.sort(); const processedExpectedResponse = { - status: expectedResponse.status, - headers: filteredExpectedHeaders, - body: expectedResponse.body + ...expectedResponse, + headers: sortedExpectedHeaders }; const valuesAreEqual = deepEqual( processedResponse, diff --git a/property_tests/tests/canister_methods/http_request_update/test/fletch.ts b/property_tests/tests/canister_methods/http_request_update/test/fletch.ts index 323575bc6e..6d89bf8ec0 100644 --- a/property_tests/tests/canister_methods/http_request_update/test/fletch.ts +++ b/property_tests/tests/canister_methods/http_request_update/test/fletch.ts @@ -1,11 +1,52 @@ import { execSync } from 'child_process'; import { HttpRequest } from 'azle'; import { getCanisterId } from 'azle/test'; +import * as dns from 'node:dns'; +dns.setDefaultResultOrder('ipv4first'); + +type HttpResponse = { + status: number; + headers: string[][]; + body: string; +}; + +/** + * An asynchronous "fetch" for canisters. + */ +export async function fletch( + canisterName: string, + request: HttpRequest +): Promise { + if (request.method === 'TRACE' || request.method == 'PATCH') { + return fletchSync(canisterName, request); + } + const canisterId = getCanisterId(canisterName); + + const { method, body, headers, url: path } = request; + + const url = `http://${canisterId}.localhost:8000${path}`; + + const fetchOptions = { + method, + headers, + body: method === 'GET' ? undefined : body + }; + + return await toResponse(await fetch(url, fetchOptions)); +} + +async function toResponse(response: Response): Promise { + const headers: [string, string][] = Array.from(response.headers.entries()); + const status = response.status; + const body = await response.text(); + + return { status, headers, body }; +} /** * A synchronous "fetch" for canisters. */ -export function fletch(canisterName: string, options: HttpRequest) { +export function fletchSync(canisterName: string, options: HttpRequest) { const canisterId = getCanisterId(canisterName); const requestHeaders = toCurlHeadersString(options.headers); @@ -21,7 +62,7 @@ export function fletch(canisterName: string, options: HttpRequest) { const responseBuffer = execSync(curlCommand); - return toResponse(responseBuffer); + return toResponseSync(responseBuffer); } function toCurlHeadersString(headers: [string, string][]) { @@ -36,7 +77,7 @@ function singleQuotedString(input: string) { return `'${singleQuoteEscapedString}'`; } -function toResponse(buffer: Buffer) { +function toResponseSync(buffer: Buffer) { const responseString = new TextDecoder().decode(buffer); const [statusCodeAndHeaders, body] = responseString.split('\r\n\r\n'); diff --git a/property_tests/tests/canister_methods/http_request_update/test/generate_body.ts b/property_tests/tests/canister_methods/http_request_update/test/generate_body.ts index 4ea22e06e2..bfde1e54ba 100644 --- a/property_tests/tests/canister_methods/http_request_update/test/generate_body.ts +++ b/property_tests/tests/canister_methods/http_request_update/test/generate_body.ts @@ -68,12 +68,32 @@ function generateHeadersMap( function generateHeaderChecks(headers: [string, string][]) { return headers - .filter(([_, value]) => value !== '') // An empty header value is the same as a none existent header - .map( - ([name, value]) => ` - if (headers['${escape(name).toLowerCase()}'] !== '${escape( - value - )}') { + .map(([name, value]) => { + if (value === '') { + return generateEmptyHeaderCheck(name); + } + return generateNonEmptyHeaderCheck(name, value); + }) + .join('\n'); +} + +function generateEmptyHeaderCheck(name: string) { + return `if (headers['${escape(name).toLowerCase()}'] !== undefined) { + throw new Error( + \`Unexpected value for header '${escape( + name + )}'. Expected undefined but received '\${headers['${escape( + name + ).toLowerCase()}']}'\` + ); + } + `; +} + +function generateNonEmptyHeaderCheck(name: string, value: string): string { + return `if (headers['${escape(name).toLowerCase()}'] !== '${escape( + value + )}') { throw new Error( \`Unexpected value for header '${escape( name @@ -84,9 +104,7 @@ function generateHeaderChecks(headers: [string, string][]) { ).toLowerCase()}']}'\` ); } - ` - ) - .join('\n'); + `; } function escape(input: string) { diff --git a/property_tests/tests/canister_methods/http_request_update/test/generate_tests.ts b/property_tests/tests/canister_methods/http_request_update/test/generate_tests.ts index acebd69a3b..6984c6d439 100644 --- a/property_tests/tests/canister_methods/http_request_update/test/generate_tests.ts +++ b/property_tests/tests/canister_methods/http_request_update/test/generate_tests.ts @@ -34,21 +34,30 @@ export function generateTests( { name: functionName, test: async () => { - const response = fletch('canister', request); - const filteredHeaders = response.headers.filter( - ([name]) => - name !== 'x-ic-streaming-response' && - name !== 'content-length' && - name !== 'date' - ); + const response = await fletch('canister', request); + + const filteredHeaders = response.headers + .filter( + ([name]) => + name !== 'x-ic-streaming-response' && + name !== 'content-length' && + name !== 'date' + ) + .sort(); + const sortedExpectedHeaders = + expectedResponse.headers.sort(); const processedResponse = { status: response.status, headers: filteredHeaders, body: response.body }; + const processedExpectedResponse = { + ...expectedResponse, + headers: sortedExpectedHeaders + }; const valuesAreEqual = deepEqual( processedResponse, - expectedResponse + processedExpectedResponse ); return { Ok: valuesAreEqual }; From 15ad07eabc9f1ded1d504b52086b8618c528e4f9 Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Mon, 12 Feb 2024 13:37:20 -0700 Subject: [PATCH 16/22] pr fixes --- examples/motoko_examples/http_counter/dfx.json | 4 ---- examples/motoko_examples/http_counter/test/pretest.ts | 4 ---- property_tests/arbitraries/http/response_arb.ts | 3 +-- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/examples/motoko_examples/http_counter/dfx.json b/examples/motoko_examples/http_counter/dfx.json index 3cb0c71e2e..d6ae71e51d 100644 --- a/examples/motoko_examples/http_counter/dfx.json +++ b/examples/motoko_examples/http_counter/dfx.json @@ -7,10 +7,6 @@ "build": "npx azle http_counter", "wasm": ".azle/http_counter/http_counter.wasm", "gzip": true, - "declarations": { - "output": "test/dfx_generated/http_counter", - "node_compatibility": true - }, "metadata": [ { "name": "candid:service", diff --git a/examples/motoko_examples/http_counter/test/pretest.ts b/examples/motoko_examples/http_counter/test/pretest.ts index 96c5b798ab..33c2c77cd3 100644 --- a/examples/motoko_examples/http_counter/test/pretest.ts +++ b/examples/motoko_examples/http_counter/test/pretest.ts @@ -10,10 +10,6 @@ async function pretest() { execSync(`dfx deploy http_counter`, { stdio: 'inherit' }); - - execSync(`dfx generate http_counter`, { - stdio: 'inherit' - }); } pretest(); diff --git a/property_tests/arbitraries/http/response_arb.ts b/property_tests/arbitraries/http/response_arb.ts index a766190c68..6341dd7c49 100644 --- a/property_tests/arbitraries/http/response_arb.ts +++ b/property_tests/arbitraries/http/response_arb.ts @@ -20,14 +20,13 @@ export function HttpResponseValueArb() { return fc .tuple(StatusCodeArb, HttpHeadersArb(), BodyArb()) .map(([status_code, headers, body]): HttpResponse => { - const thing: HttpResponse = { + return { status_code, headers, body, upgrade: None, streaming_strategy: None }; - return thing; }); } export function HttpResponseArb( From 2cec535a2a048073d34775a9da9a98c78621cceb Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Mon, 12 Feb 2024 16:55:59 -0700 Subject: [PATCH 17/22] pr fixes --- .../motoko_examples/http_counter/src/index.ts | 3 - src/lib/canister_methods/http_gateway.ts | 108 ------------------ src/lib/canister_methods/index.ts | 1 - src/lib/server.ts | 87 +++++++++----- 4 files changed, 58 insertions(+), 141 deletions(-) delete mode 100644 src/lib/canister_methods/http_gateway.ts diff --git a/examples/motoko_examples/http_counter/src/index.ts b/examples/motoko_examples/http_counter/src/index.ts index 4bcad112c4..3ccb9829fc 100644 --- a/examples/motoko_examples/http_counter/src/index.ts +++ b/examples/motoko_examples/http_counter/src/index.ts @@ -29,8 +29,6 @@ export default Canister({ stableStorage.insert('counter', 0n); }), http_request: query([HttpRequest], HttpResponse(Token), (req) => { - console.log('Hello from http_request'); - if (req.method === 'GET') { if (req.headers.find(isGzip) === undefined) { if (req.url === '/stream') { @@ -161,7 +159,6 @@ export default Canister({ [Token], StreamingCallbackHttpResponse(Token), (token) => { - console.log('Hello from http_streaming'); switch (token.arbitrary_data) { case 'start': { return { diff --git a/src/lib/canister_methods/http_gateway.ts b/src/lib/canister_methods/http_gateway.ts deleted file mode 100644 index 8d3b1e3f71..0000000000 --- a/src/lib/canister_methods/http_gateway.ts +++ /dev/null @@ -1,108 +0,0 @@ -// See https://internetcomputer.org/docs/current/references/http-gateway-protocol-spec#canister-http-interface - -import { blob } from '../candid/types/constructed/blob'; -import { bool } from '../candid/types/primitive/bool'; -import { Func } from '../candid/types/reference/func'; -import { nat16 } from '../candid/types/primitive/nats/nat16'; -import { Opt } from '../candid/types/constructed/opt'; -import { Record } from '../candid/types/constructed/record'; -import { text } from '../candid/types/primitive/text'; -import { Tuple } from '../candid/types/constructed/tuple'; -import { Variant } from '../candid/types/constructed/variant'; -import { Vec } from '../candid/types/constructed/vec'; -import { CandidType } from '../candid'; - -export const HeaderField = Tuple(text, text); -export type HeaderField = typeof HeaderField.tsType; - -export const HttpRequest = Record({ - /** The HTTP method in all upper case letters, e.g. "GET" */ - method: text, - /** - * The URL from the HTTP request line, i.e. without protocol or hostname, - * and includes query parameters - */ - url: text, - /** The headers of the HTTP request */ - headers: Vec(HeaderField), - /** - * The body of the HTTP request (without any content encodings processed by - * the HTTP Gateway) - */ - body: blob, - /** - * Indicates the maximum supported version of [response verification]( - * https://internetcomputer.org/docs/current/references/http-gateway-protocol-spec#response-verification). - * - A value of `2` will request the current standard of [response - * verification](https://internetcomputer.org/docs/current/references/http-gateway-protocol-spec#response-verification), - * while a missing version or a value of `1` will request the [legacy - * standard](https://internetcomputer.org/docs/current/references/http-gateway-protocol-spec#legacy-response-verification). - * - Current HTTP Gateway implementations will always request version `2`, - * but older HTTP Gateways may still request version `1`. - */ - certificate_version: Opt(nat16) -}); -export type HttpRequest = typeof HttpRequest.tsType; - -export const HttpUpdateRequest = Record({ - method: text, - url: text, - headers: Vec(HeaderField), - body: blob -}); -export type HttpUpdateRequest = typeof HttpUpdateRequest.tsType; - -export function StreamingCallbackHttpResponse( - token: Token -) { - return Record({ - body: blob, - token: Opt(token) - }); -} -type StreamingCallbackHttpResponse = { - body: Uint8Array; - token: Opt; -}; - -function Callback(token: Token) { - return Func([token], Opt(StreamingCallbackHttpResponse(token)), 'query'); -} -type Callback = Func; - -function CallbackStrategy(token: Token) { - return Record({ - callback: Callback(token), - token - }); -} -export type CallbackStrategy = { - callback: Callback; - token: Token; -}; - -function StreamingStrategy(token: Token) { - return Variant({ - Callback: CallbackStrategy(token) - }); -} -export type StreamingStrategy = Variant<{ - Callback: CallbackStrategy; -}>; - -export function HttpResponse(token: Token) { - return Record({ - status_code: nat16, - headers: Vec(HeaderField), - body: blob, - upgrade: Opt(bool), - streaming_strategy: Opt(StreamingStrategy(token)) - }); -} -export type HttpResponse = { - status_code: number; - headers: HeaderField[]; - body: Uint8Array; - upgrade: Opt; - streaming_strategy: Opt>; -}; diff --git a/src/lib/canister_methods/index.ts b/src/lib/canister_methods/index.ts index db04c6a62f..7a76972c1a 100644 --- a/src/lib/canister_methods/index.ts +++ b/src/lib/canister_methods/index.ts @@ -1,3 +1,2 @@ -export * from './http_gateway'; export * from './methods'; export * from './types'; diff --git a/src/lib/server.ts b/src/lib/server.ts index 14d95249ab..ce82269201 100644 --- a/src/lib/server.ts +++ b/src/lib/server.ts @@ -1,6 +1,7 @@ import { blob, bool, + CandidType, Canister, Func, ic, @@ -27,38 +28,65 @@ import { IncomingMessageForServer } from 'http'; const httpMessageParser = require('http-message-parser'); -// TODO double-check all of these -export const Token = Record({ - // add whatever fields you'd like - arbitrary_data: text -}); - -export const StreamingCallbackHttpResponse = Record({ - body: blob, - token: Opt(Token) -}); +export const DefaultToken = Record({}); +export function StreamingCallbackHttpResponse( + token: Token +) { + return Record({ + body: blob, + token: Opt(token) + }); +} +type StreamingCallbackHttpResponse = { + body: Uint8Array; + token: Opt; +}; -export const Callback = Func([text], StreamingCallbackHttpResponse, 'query'); +function Callback(token: Token) { + return Func([token], Opt(StreamingCallbackHttpResponse(token)), 'query'); +} +type Callback = Func; -export const CallbackStrategy = Record({ - callback: Callback, - token: Token -}); +function CallbackStrategy(token: Token) { + return Record({ + callback: Callback(token), + token + }); +} +export type CallbackStrategy = { + callback: Callback; + token: Token; +}; + +function StreamingStrategy(token: Token) { + return Variant({ + Callback: CallbackStrategy(token) + }); +} -export const StreamingStrategy = Variant({ - Callback: CallbackStrategy -}); +export type StreamingStrategy = Variant<{ + Callback: CallbackStrategy; +}>; export type HeaderField = [text, text]; export const HeaderField = Tuple(text, text); -export const HttpResponse = Record({ - status_code: nat16, - headers: Vec(HeaderField), - body: blob, - streaming_strategy: Opt(StreamingStrategy), - upgrade: Opt(bool) -}); +export function HttpResponse(token?: Token) { + return Record({ + status_code: nat16, + headers: Vec(HeaderField), + body: blob, + streaming_strategy: Opt(StreamingStrategy(token ?? DefaultToken)), + upgrade: Opt(bool) + }); +} +export type HttpResponse = { + status_code: number; + headers: HeaderField[]; + body: Uint8Array; + upgrade: Opt; + streaming_strategy: Opt>; +}; export const HttpRequest = Record({ method: text, @@ -67,6 +95,7 @@ export const HttpRequest = Record({ body: blob, certificate_version: Opt(nat16) }); +export type HttpRequest = typeof HttpRequest.tsType; let server: NodeServer; @@ -80,7 +109,7 @@ export function Server(serverInit: () => NodeServer | Promise) { }), http_request: query( [HttpRequest], - Manual(HttpResponse), + Manual(HttpResponse()), async (httpRequest) => { await httpHandler(httpRequest, true); }, @@ -90,7 +119,7 @@ export function Server(serverInit: () => NodeServer | Promise) { ), http_request_update: update( [HttpRequest], - Manual(HttpResponse), + Manual(HttpResponse()), async (httpRequest) => { await httpHandler(httpRequest, false); }, @@ -120,7 +149,7 @@ export async function httpHandler( streaming_strategy: None, upgrade: Some(true) }, - HttpResponse + HttpResponse() ); return; @@ -222,7 +251,7 @@ export async function httpHandler( None: null } }, - HttpResponse + HttpResponse() ); } } From bd170cb5330dcb6043576734e3e882679a2f0ae7 Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Tue, 13 Feb 2024 11:38:36 -0700 Subject: [PATCH 18/22] encoding fix --- property_tests/arbitraries/http/request_arb.ts | 8 ++++---- .../http_request_update/test/generate_body.ts | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/property_tests/arbitraries/http/request_arb.ts b/property_tests/arbitraries/http/request_arb.ts index 01aa6881fb..55f6366564 100644 --- a/property_tests/arbitraries/http/request_arb.ts +++ b/property_tests/arbitraries/http/request_arb.ts @@ -42,13 +42,13 @@ const UrlPathArb = fc.webUrl({ withQueryParameters: true }).map((url) => { return `${pathname}${search}${hash}`; }); -function encodeString(search: string, prefix: string = ''): string { - if (search.length === 0) { - return search; +function encodeString(text: string, prefix: string = ''): string { + if (text.length === 0) { + return text; } return `${prefix}${encodeURIComponent( - decodeURIComponent(search.slice(prefix.length)) + decodeURIComponent(text.slice(prefix.length)) )}`; } diff --git a/property_tests/tests/canister_methods/http_request_update/test/generate_body.ts b/property_tests/tests/canister_methods/http_request_update/test/generate_body.ts index bfde1e54ba..9d7b05bfd9 100644 --- a/property_tests/tests/canister_methods/http_request_update/test/generate_body.ts +++ b/property_tests/tests/canister_methods/http_request_update/test/generate_body.ts @@ -43,7 +43,9 @@ function generateHttpMethodCheck(method: string, requestParamName: string) { function generateUrlCheck(url: string, requestParamName: string) { return ` - if (${requestParamName}.url !== '${escape(url)}') { + if (decodeURIComponent(${requestParamName}.url) !== decodeURIComponent('${escape( + url + )}')) { throw new Error( \`Unexpected req.url. Expected '${escape( url From 406f24c966c3ce07379eff6b9ecb44c16af2333f Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Tue, 13 Feb 2024 14:52:35 -0700 Subject: [PATCH 19/22] update tests for 407 and 205 statuses --- .../arbitraries/http/response_arb.ts | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/property_tests/arbitraries/http/response_arb.ts b/property_tests/arbitraries/http/response_arb.ts index 6341dd7c49..fb558bd78e 100644 --- a/property_tests/arbitraries/http/response_arb.ts +++ b/property_tests/arbitraries/http/response_arb.ts @@ -14,11 +14,20 @@ export type HttpResponseAgentResponseValue = { }; // The ic replica doesn't support returning status codes in the 1xx range. -const StatusCodeArb = fc.integer({ min: 200, max: 599 }); +const StatusCodeArb = fc + .integer({ min: 200, max: 599 }) + .filter((status) => status !== 407); // TODO Node's fetch doesn't handle 407 the same as other status, so we're filtering it out until we can figure out why export function HttpResponseValueArb() { return fc - .tuple(StatusCodeArb, HttpHeadersArb(), BodyArb()) + .tuple(StatusCodeArb, HttpHeadersArb()) + .chain(([statusCode, headers]) => { + return fc.tuple( + fc.constant(statusCode), + fc.constant(headers), + hasBody(statusCode) ? BodyArb() : fc.constant(new Uint8Array()) + ); + }) .map(([status_code, headers, body]): HttpResponse => { return { status_code, @@ -86,3 +95,10 @@ export function HttpResponseArb( }; }); } +function hasBody(statusCode: number): boolean { + if (statusCode === 205) { + return false; + } else { + return true; + } +} From 6ef0aa4283126fd996e4e8b6ddafd0e91de188de Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Tue, 13 Feb 2024 15:09:39 -0700 Subject: [PATCH 20/22] add all status codes that can't have bodies --- property_tests/arbitraries/http/response_arb.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/property_tests/arbitraries/http/response_arb.ts b/property_tests/arbitraries/http/response_arb.ts index fb558bd78e..0c6b969378 100644 --- a/property_tests/arbitraries/http/response_arb.ts +++ b/property_tests/arbitraries/http/response_arb.ts @@ -96,7 +96,14 @@ export function HttpResponseArb( }); } function hasBody(statusCode: number): boolean { - if (statusCode === 205) { + // The following code must not have content according to the http spec + // https://www.rfc-editor.org/rfc/rfc9110.html + if ( + statusCode < 200 || + statusCode === 204 || + statusCode === 205 || + statusCode === 304 + ) { return false; } else { return true; From 8c42508c68c0ab46894f9460121c77b44006aeb0 Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Wed, 14 Feb 2024 09:04:41 -0700 Subject: [PATCH 21/22] pr fixes --- src/lib/server.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/server.ts b/src/lib/server.ts index ce82269201..e6e3b6d7c6 100644 --- a/src/lib/server.ts +++ b/src/lib/server.ts @@ -37,17 +37,17 @@ export function StreamingCallbackHttpResponse( token: Opt(token) }); } -type StreamingCallbackHttpResponse = { +export type StreamingCallbackHttpResponse = { body: Uint8Array; token: Opt; }; -function Callback(token: Token) { +export function Callback(token: Token) { return Func([token], Opt(StreamingCallbackHttpResponse(token)), 'query'); } -type Callback = Func; +export type Callback = Func; -function CallbackStrategy(token: Token) { +export function CallbackStrategy(token: Token) { return Record({ callback: Callback(token), token @@ -58,7 +58,7 @@ export type CallbackStrategy = { token: Token; }; -function StreamingStrategy(token: Token) { +export function StreamingStrategy(token: Token) { return Variant({ Callback: CallbackStrategy(token) }); From ee215d6e80b587e87453864a605150337c546c37 Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Wed, 14 Feb 2024 12:20:38 -0700 Subject: [PATCH 22/22] fixes --- .../tests/canister_methods/http_request/test/generate_body.ts | 4 +++- src/lib/server.ts | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/property_tests/tests/canister_methods/http_request/test/generate_body.ts b/property_tests/tests/canister_methods/http_request/test/generate_body.ts index a8d0c9a541..a16c71ebc7 100644 --- a/property_tests/tests/canister_methods/http_request/test/generate_body.ts +++ b/property_tests/tests/canister_methods/http_request/test/generate_body.ts @@ -41,7 +41,9 @@ function generateHttpMethodCheck(method: string, requestParamName: string) { function generateUrlCheck(url: string, requestParamName: string) { return ` - if (${requestParamName}.url !== '${escape(url)}') { + if (decodeURIComponent(${requestParamName}.url) !== decodeURIComponent('${escape( + url + )}')) { throw new Error( \`Unexpected req.url. Expected '${escape( url diff --git a/src/lib/server.ts b/src/lib/server.ts index e6e3b6d7c6..3b8df21068 100644 --- a/src/lib/server.ts +++ b/src/lib/server.ts @@ -42,10 +42,10 @@ export type StreamingCallbackHttpResponse = { token: Opt; }; -export function Callback(token: Token) { +function Callback(token: Token) { return Func([token], Opt(StreamingCallbackHttpResponse(token)), 'query'); } -export type Callback = Func; +type Callback = Func; export function CallbackStrategy(token: Token) { return Record({