├── .babelrc
├── .chglog
├── CHANGELOG.tpl.md
└── config.yml
├── .eslintignore
├── .eslintrc.js
├── .github
├── auto-merge.yml
├── dependabot.yml
└── workflows
│ ├── dependabot-automerge.yml
│ ├── node.js.yml
│ └── publish-npm.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── __tests__
├── js-hello.test.js
├── js-status.test.js
├── js-transform.test.js
├── pdk.test.js
└── pluing.test.js
├── bin
└── kong-js-pluginserver
├── cli.js
├── examples
├── js-goodbye
│ ├── index.js
│ └── package.json
├── js-graphql
│ ├── index.js
│ └── package.json
├── js-hello.js
├── js-status.js
├── js-transform.js
├── package.json
└── ts-hello.ts
├── kong
├── client
│ ├── index.d.ts
│ └── tls.d.ts
├── cluster.d.ts
├── ctx
│ ├── index.d.ts
│ └── shared.d.ts
├── enterprise_edition
│ ├── index.d.ts
│ └── jwe.d.ts
├── index.d.ts
├── ip.d.ts
├── log.d.ts
├── nginx
│ ├── index.d.ts
│ └── shared.d.ts
├── node.d.ts
├── plugin.d.ts
├── request.d.ts
├── response.d.ts
├── router.d.ts
├── service
│ ├── index.d.ts
│ ├── request.d.ts
│ └── response.d.ts
├── telemetry.d.ts
└── vault.d.ts
├── lib
├── mod.js
└── pipe.js
├── listener.js
├── package-lock.json
├── package.json
├── pdk.js
├── plugin_test.js
└── server.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env",
5 | {
6 | "targets": {
7 | "node": "current"
8 | }
9 | }
10 | ]
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/.chglog/CHANGELOG.tpl.md:
--------------------------------------------------------------------------------
1 | {{ if .Versions -}}
2 |
3 | ## [Unreleased]
4 |
5 | {{ if .Unreleased.CommitGroups -}}
6 | {{ range .Unreleased.CommitGroups -}}
7 | ### {{ .Title }}
8 | {{ range .Commits -}}
9 | - {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
10 | {{ end }}
11 | {{ end -}}
12 | {{ end -}}
13 | {{ end -}}
14 |
15 | {{ range .Versions }}
16 |
17 | ## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }}
18 | {{ range .CommitGroups -}}
19 | ### {{ lower .Title }}
20 | {{ range .Commits -}}
21 | - {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} [{{ .Hash.Short }}]({{ $.Info.RepositoryURL }}/commit/{{ .Hash.Long }})
22 | {{ end }}
23 | {{ end -}}
24 |
25 | {{- if .NoteGroups -}}
26 | {{ range .NoteGroups -}}
27 | ### {{ .Title }}
28 | {{ range .Notes }}
29 | {{ .Body }}
30 | {{ end }}
31 | {{ end -}}
32 | {{ end -}}
33 | {{ end -}}
34 |
35 | {{- if .Versions }}
36 | [Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD
37 | {{ range .Versions -}}
38 | {{ if .Tag.Previous -}}
39 | [{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}
40 | {{ end -}}
41 | {{ end -}}
42 | {{ end -}}
43 |
--------------------------------------------------------------------------------
/.chglog/config.yml:
--------------------------------------------------------------------------------
1 | style: github
2 | template: CHANGELOG.tpl.md
3 | info:
4 | title: CHANGELOG
5 | repository_url: https://github.com/Kong/kong-js-pdk
6 | options:
7 | sort: "semver"
8 | commits:
9 | filters:
10 | Type:
11 | - feat
12 | - fix
13 | - refactor
14 | commit_groups:
15 | title_maps:
16 | feat: Features
17 | fix: Bug Fixes
18 | perf: Performance Improvements
19 | refactor: Code Refactoring
20 | header:
21 | pattern: "^(\\w*)(?:\\([\\w\\$\\.\\-\\*\\s\\/]*\\))?:?\\s(.*)$"
22 | pattern_maps:
23 | - Type
24 | - Subject
25 | notes:
26 | keywords:
27 | - BREAKING CHANGE
28 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /config/
3 | /dist/
4 | /*.js
5 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // https://eslint.org/docs/user-guide/configuring
2 |
3 | module.exports = {
4 | root: true,
5 | parserOptions: {
6 | parser: 'babel-eslint'
7 | },
8 | env: {
9 | browser: false,
10 | },
11 | extends: [
12 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md
13 | 'standard'
14 | ],
15 | plugins: [
16 | ],
17 | // add your custom rules here
18 | rules: {
19 | // allow async-await
20 | 'generator-star-spacing': 'off',
21 | // allow debugger during development
22 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.github/auto-merge.yml:
--------------------------------------------------------------------------------
1 | - match:
2 | dependency_type: all
3 | update_type: "semver:minor" # includes patch updates!
4 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: github-actions
9 | directory: /
10 | schedule:
11 | interval: weekly
12 | - package-ecosystem: npm
13 | directory: /
14 | schedule:
15 | interval: weekly
16 | - package-ecosystem: npm
17 | directory: /examples
18 | schedule:
19 | interval: weekly
20 |
--------------------------------------------------------------------------------
/.github/workflows/dependabot-automerge.yml:
--------------------------------------------------------------------------------
1 | name: dependabot-auto-merge
2 |
3 | on:
4 | pull_request_target:
5 |
6 | jobs:
7 | auto-merge:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v3
11 | - uses: ahmadnassri/action-dependabot-auto-merge@v2
12 | with:
13 | target: minor
14 | # needs push access to repo
15 | github-token: ${{ secrets.DEPENDABOT_AUTOMERGE_PUSH_TOKEN }}
16 | command: "squash and merge"
17 |
--------------------------------------------------------------------------------
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | branches: [ master ]
9 | pull_request:
10 | branches: [ master ]
11 |
12 | jobs:
13 | build:
14 | runs-on: ubuntu-latest
15 | strategy:
16 | matrix:
17 | node-version: [12.x, 14.x, 16.x]
18 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
19 |
20 | steps:
21 | - uses: actions/checkout@v3
22 | - name: Use Node.js ${{ matrix.node-version }}
23 | uses: actions/setup-node@v3
24 | with:
25 | node-version: ${{ matrix.node-version }}
26 | cache: 'npm'
27 | - run: npm i
28 | - run: npm run build --if-present
29 | - run: npm test
30 | - uses: codecov/codecov-action@v3
31 | with:
32 | flags: node-${{ matrix.node-version }}
33 | token: ${{ secrets.CODECOV_TOKEN }}
34 |
--------------------------------------------------------------------------------
/.github/workflows/publish-npm.yml:
--------------------------------------------------------------------------------
1 | name: npm-publish
2 | on:
3 | push:
4 | branches:
5 | - master # Change this to your default branch
6 | jobs:
7 | npm-publish:
8 | name: npm-publish
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout repository
12 | uses: actions/checkout@v3
13 | - name: Publish if version has been updated
14 | uses: pascalgn/npm-publish-action@1.3.9
15 | with: # All of theses inputs are optional
16 | tag_name: "%s"
17 | tag_message: "%s"
18 | create_tag: "true"
19 | commit_pattern: "^release: (\\S+)"
20 | workspace: "."
21 | publish_command: "yarn"
22 | publish_args: "--non-interactive"
23 | env: # More info about the environment variables in the README
24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Leave this as is, it's automatically generated
25 | NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} # You need to set this in your repo settings
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | /dist/
4 | npm-debug.log*
5 | coverage/
6 | # Editor directories and files
7 | .idea
8 | .vscode
9 | *.suo
10 | *.ntvs*
11 | *.njsproj
12 | *.sln
13 | *.vim
14 | *.swp
15 |
16 | examples/package-lock.json
17 | examples/node_modules
18 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | ## [Unreleased]
3 |
4 |
5 |
6 | ## [0.6.0] - 2024-10-10
7 | ### bug fixes
8 | - incorrect doc ([#316](https://github.com/Kong/kong-js-pdk/issues/316)) [ebc91ba](https://github.com/Kong/kong-js-pdk/commit/ebc91ba775bbc28ca81c284f41a5f96eacf130a3)
9 |
10 |
11 |
12 | ## [0.5.5] - 2023-02-15
13 |
14 |
15 | ## [0.5.4] - 2022-11-08
16 | ### bug fixes
17 | - ignore file starts with `.` ([#232](https://github.com/Kong/kong-js-pdk/issues/232)) [72903d5](https://github.com/Kong/kong-js-pdk/commit/72903d59c75056c48ae4e488725d08e9e9ebdfd6)
18 | - function signature ([#162](https://github.com/Kong/kong-js-pdk/issues/162)) [79de00f](https://github.com/Kong/kong-js-pdk/commit/79de00f36b989cdb4fe9979acc3b4be474d328b5)
19 | - function signature for kong/kong[#8623](https://github.com/Kong/kong-js-pdk/issues/8623) ([#161](https://github.com/Kong/kong-js-pdk/issues/161)) [2977f80](https://github.com/Kong/kong-js-pdk/commit/2977f80602db47ba743a97878141f92e14871b17)
20 | - use version defined in package.json ([#148](https://github.com/Kong/kong-js-pdk/issues/148)) [e9090c8](https://github.com/Kong/kong-js-pdk/commit/e9090c849ec71ba03f2c37ef43d1c2ed78b1adee)
21 |
22 | ### features
23 | - add a reference graphQL server plugin [94b2d67](https://github.com/Kong/kong-js-pdk/commit/94b2d67bc8dda23036ca9014c5a5b924b4474792)
24 |
25 |
26 |
27 | ## [0.5.3] - 2022-02-22
28 | ### bug fixes
29 | - fix the assignment of mocked log functions ([#143](https://github.com/Kong/kong-js-pdk/issues/143)) [1b4aa12](https://github.com/Kong/kong-js-pdk/commit/1b4aa124d483b104ff783812fe50c261f4db8ce4)
30 |
31 |
32 |
33 | ## [0.5.2] - 2022-02-17
34 | ### bug fixes
35 | - exit immediately from kong.response.{error,exit} ([#141](https://github.com/Kong/kong-js-pdk/issues/141)) [c0fab3b](https://github.com/Kong/kong-js-pdk/commit/c0fab3b097f590af547eca648e44e00a0e4c4c0e)
36 | - conditionally merging the serviceResponse only if the response.status is undefined ([#131](https://github.com/Kong/kong-js-pdk/issues/131)) [c9eed50](https://github.com/Kong/kong-js-pdk/commit/c9eed50e29bceb59db5a17506dcf086cc906f050)
37 |
38 |
39 |
40 | ## [0.5.1] - 2022-01-25
41 |
42 |
43 | ## [0.5.0] - 2021-12-09
44 | ### bug fixes
45 | - correct error constructor arguments ([#100](https://github.com/Kong/kong-js-pdk/issues/100)) [9375614](https://github.com/Kong/kong-js-pdk/commit/9375614c7c3d5b198ee858a174c065a98b658f95)
46 |
47 | ### features
48 | - allow plugin loading from discrete npm packages ([#101](https://github.com/Kong/kong-js-pdk/issues/101)) [ec76dc8](https://github.com/Kong/kong-js-pdk/commit/ec76dc83132d388aeecca41fb5e756c0b40fbd26)
49 |
50 |
51 |
52 | ## [0.4.4] - 2021-11-22
53 | ### bug fixes
54 | - remove unnecessary promise wrappers ([#81](https://github.com/Kong/kong-js-pdk/issues/81)) [32d2c8b](https://github.com/Kong/kong-js-pdk/commit/32d2c8bc46734bedbd242850fa0c69584c624d22)
55 | - remove unused import and use relative import in bin ([#80](https://github.com/Kong/kong-js-pdk/issues/80)) [65da804](https://github.com/Kong/kong-js-pdk/commit/65da804ab70ab2846b7b9a629281b65e171b0373)
56 |
57 |
58 |
59 | ## [0.4.3] - 2021-10-21
60 | ### bug fixes
61 | - rename msgpack.pack to msgpack.encode ([#77](https://github.com/Kong/kong-js-pdk/issues/77)) [8fc0ff5](https://github.com/Kong/kong-js-pdk/commit/8fc0ff5ef9bcee5d446bdf3b19a5457c98c69f1a)
62 |
63 |
64 |
65 | ## [0.4.2] - 2021-08-13
66 | ### bug fixes
67 | - allow to concat buffer larger than 64k ([#34](https://github.com/Kong/kong-js-pdk/issues/34)) [f0b0ce5](https://github.com/Kong/kong-js-pdk/commit/f0b0ce5f1a2ae5857402a3db931ba1f7b0bb8df0)
68 | - add kong.response.error in plugin_test ([#35](https://github.com/Kong/kong-js-pdk/issues/35)) [4a469f1](https://github.com/Kong/kong-js-pdk/commit/4a469f1d77327890c0bb6217a418bcdc64acbc34)
69 |
70 |
71 |
72 | ## [0.4.0] - 2021-08-06
73 | ### features
74 | - adds Version as named property in GetPluginInfo function in GetPluginInfo function([#20](https://github.com/Kong/kong-js-pdk/issues/20)) [383e44e](https://github.com/Kong/kong-js-pdk/commit/383e44e50ade0a74b390a0a822659591280dae3a)
75 |
76 |
77 |
78 | ## [0.3.4] - 2021-06-15
79 | ### bug fixes
80 | - server to generate correct Step functions ([#11](https://github.com/Kong/kong-js-pdk/issues/11)) [46208f5](https://github.com/Kong/kong-js-pdk/commit/46208f5c5c3968a82bddfd185b47dc8b34d8cb92)
81 | - remove kong.table TS definitions ([#12](https://github.com/Kong/kong-js-pdk/issues/12)) [128bc58](https://github.com/Kong/kong-js-pdk/commit/128bc5850d9cdf4f51124623d394093914326f3e)
82 |
83 |
84 |
85 | ## [0.3.3] - 2021-05-19
86 | ### bug fixes
87 | - reorganize dependencies ([#8](https://github.com/Kong/kong-js-pdk/issues/8)) [167e56c](https://github.com/Kong/kong-js-pdk/commit/167e56c2b1de07efd345bfddafff8ae7201e7a9b)
88 |
89 |
90 |
91 | ## [0.3.2] - 2021-05-19
92 | ### features
93 | - add tooling to test plugin code ([#7](https://github.com/Kong/kong-js-pdk/issues/7)) [82d01a6](https://github.com/Kong/kong-js-pdk/commit/82d01a68885c5b049dc72aaf0a969cc476bbb38d)
94 |
95 |
96 |
97 | ## [0.3.1] - 2021-05-13
98 | ### bug fixes
99 | - use standarlized error for instance not found exception [32960ff](https://github.com/Kong/kong-js-pdk/commit/32960ff1015f2cc85d2ad147d4fc31a1ba543b7d)
100 |
101 |
102 |
103 | ## [0.3.0] - 2021-05-07
104 | ### bug fixes
105 | - popup PDK errors and add response phase [9152fc1](https://github.com/Kong/kong-js-pdk/commit/9152fc187420c66d2421af8ecdffbfe0617ea482)
106 | - indent and style for package.json [883d1d7](https://github.com/Kong/kong-js-pdk/commit/883d1d7778b224cc5a836b78f74f125209f420ad)
107 |
108 |
109 |
110 | ## 0.1.0 - 2021-03-15
111 | ### features
112 | - allow to import TypeScript plugin directly [c375a11](https://github.com/Kong/kong-js-pdk/commit/c375a11587af296ffeca5b103fa6e8c51e79d1a4)
113 | - support write plugin in typescript [f63bb91](https://github.com/Kong/kong-js-pdk/commit/f63bb9182cc422f9a80c89abc59f6725dc6b426c)
114 |
115 |
116 | [Unreleased]: https://github.com/Kong/kong-js-pdk/compare/0.6.0...HEAD
117 | [0.6.0]: https://github.com/Kong/kong-js-pdk/compare/0.5.5...0.6.0
118 | [0.5.5]: https://github.com/Kong/kong-js-pdk/compare/0.5.4...0.5.5
119 | [0.5.4]: https://github.com/Kong/kong-js-pdk/compare/0.5.3...0.5.4
120 | [0.5.3]: https://github.com/Kong/kong-js-pdk/compare/0.5.2...0.5.3
121 | [0.5.2]: https://github.com/Kong/kong-js-pdk/compare/0.5.1...0.5.2
122 | [0.5.1]: https://github.com/Kong/kong-js-pdk/compare/0.5.0...0.5.1
123 | [0.5.0]: https://github.com/Kong/kong-js-pdk/compare/0.4.4...0.5.0
124 | [0.4.4]: https://github.com/Kong/kong-js-pdk/compare/0.4.3...0.4.4
125 | [0.4.3]: https://github.com/Kong/kong-js-pdk/compare/0.4.2...0.4.3
126 | [0.4.2]: https://github.com/Kong/kong-js-pdk/compare/0.4.0...0.4.2
127 | [0.4.0]: https://github.com/Kong/kong-js-pdk/compare/0.3.4...0.4.0
128 | [0.3.4]: https://github.com/Kong/kong-js-pdk/compare/0.3.3...0.3.4
129 | [0.3.3]: https://github.com/Kong/kong-js-pdk/compare/0.3.2...0.3.3
130 | [0.3.2]: https://github.com/Kong/kong-js-pdk/compare/0.3.1...0.3.2
131 | [0.3.1]: https://github.com/Kong/kong-js-pdk/compare/0.3.0...0.3.1
132 | [0.3.0]: https://github.com/Kong/kong-js-pdk/compare/0.1.0...0.3.0
133 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright 2021 Kong Inc.
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # kong-js-pdk
2 |
3 | [](https://github.com/Kong/kong-js-pdk/actions/workflows/node.js.yml)
4 | [](https://www.npmjs.com/package/kong-pdk)
5 | [](https://codecov.io/gh/Kong/kong-js-pdk)
6 |
7 | Plugin server and PDK (Plugin Development Kit) for Javascript language support in Kong.
8 |
9 | Requires Kong >= 2.3.0.
10 |
11 | ## Documentation
12 |
13 | See in [Kong Docs](https://docs.konghq.com/gateway-oss/latest/external-plugins/#developing-javascript-plugins).
14 |
15 | ## TODO
16 |
17 | - Better API design for user land (without async/await?)
18 | - Dedicated server per plugin
19 | - Rewrite with typescript
20 |
--------------------------------------------------------------------------------
/__tests__/js-hello.test.js:
--------------------------------------------------------------------------------
1 | const plugin = require('../examples/js-hello');
2 |
3 | const {
4 | PluginTest,
5 | Request
6 | } = require("../plugin_test")
7 |
8 |
9 | test('Set headers in response', async () => {
10 | let r = new Request()
11 |
12 | r
13 | .useURL("http://example.com")
14 | .useMethod("GET")
15 | .useHeaders({
16 | "Host": "example.com",
17 | })
18 |
19 | let t = new PluginTest(r)
20 |
21 | await t.Run(plugin, {
22 | "message": "test",
23 | })
24 |
25 | expect(t.response.headers.get('x-hello-from-javascript'))
26 | .toBe('Javascript says test to example.com')
27 | });
28 |
--------------------------------------------------------------------------------
/__tests__/js-status.test.js:
--------------------------------------------------------------------------------
1 | const plugin = require('../examples/js-status');
2 |
3 | const {
4 | PluginTest,
5 | Request
6 | } = require("../plugin_test")
7 |
8 |
9 | test('Should succeed with status 200 when header is present', async () => {
10 | let r = new Request()
11 |
12 | r
13 | .useURL("http://example.com")
14 | .useMethod("GET")
15 | .useHeaders({
16 | "userId": "test-id",
17 | })
18 |
19 | let t = new PluginTest(r)
20 |
21 | await t.Run(plugin, {
22 | "message": "test",
23 | })
24 |
25 | expect(t.response.status).toBe(200)
26 | expect(t.response.headers.get('x-welcome'))
27 | .toBe('Javascript says test to test-id')
28 | });
29 |
30 | test('Should exit with status 403 when header is missing', async () => {
31 | let r = new Request()
32 |
33 | r
34 | .useURL("http://example.com")
35 | .useMethod("GET")
36 |
37 | let t = new PluginTest(r)
38 |
39 | await t.Run(plugin, {
40 | "message": "test",
41 | })
42 |
43 | expect(t.response.status).toBe(403)
44 | });
45 |
--------------------------------------------------------------------------------
/__tests__/js-transform.test.js:
--------------------------------------------------------------------------------
1 | const plugin = require('../examples/js-transform');
2 |
3 | const {
4 | PluginTest,
5 | Request
6 | } = require("../plugin_test")
7 |
8 |
9 | test('Set headers in response', async () => {
10 | let r = new Request()
11 |
12 | r
13 | .useURL("http://example.com")
14 | .useMethod("GET")
15 | .useHeaders({
16 | "Host": "example.com",
17 | })
18 | .useBody("all lower case")
19 |
20 | let t = new PluginTest(r)
21 |
22 | await t.Run(plugin, {})
23 |
24 | expect(t.serviceRequest.body)
25 | .toBe('ALL LOWER CASE')
26 |
27 | expect(t.serviceRequest.url.search)
28 | .toBe('?js-transform=v0.1.0')
29 |
30 | expect(t.response.status)
31 | .toBe(200)
32 |
33 | expect(t.serviceResponse.body)
34 | .toBe("OK")
35 |
36 | expect(t.response.body)
37 | .toBe(
38 | `Response body from upstream:
39 | OK
40 | Body size: 2
41 | `)
42 |
43 | })
44 |
--------------------------------------------------------------------------------
/__tests__/pdk.test.js:
--------------------------------------------------------------------------------
1 | const PDK = require('../pdk')
2 | const PipePair = require('../lib/pipe')
3 |
4 | test('kong js pdk', async () => {
5 | let [_, child] = new PipePair().getPair()
6 | const pdk = new PDK(child)
7 | expect('' + pdk).toBe('[object KongPDK]')
8 | expect(new pdk.Error).toBeInstanceOf(Error)
9 |
10 | {
11 | const error = new pdk.Error('this is an error')
12 | expect('' + error).toBe('PDKError: this is an error')
13 | }
14 |
15 | {
16 | const error = new PDK.Error('this is an error')
17 | expect('' + error).toBe('PDKError: this is an error')
18 | }
19 | })
20 |
--------------------------------------------------------------------------------
/__tests__/pluing.test.js:
--------------------------------------------------------------------------------
1 | const plugin = require('../examples/js-goodbye')
2 |
3 | const {promisify} = require('util')
4 | const sleep = promisify(setTimeout)
5 | const {
6 | PluginTest,
7 | Request
8 | } = require('../plugin_test')
9 |
10 | test('plugin interface', async () => {
11 | let r = new Request()
12 | const start = Date.now() / 1000
13 | await sleep(1000)
14 |
15 | r
16 | .useURL("http://example.com")
17 | .useMethod("GET")
18 | .useHeaders({
19 | "Host": "example.com",
20 | })
21 |
22 | let t = new PluginTest(r)
23 | const {mod, instance} = await t.Run(plugin, {
24 | "message": "test",
25 | })
26 |
27 | expect(mod.getLastStartInstanceTime()).toBeGreaterThan(start)
28 | expect(mod.getName()).toBe('goodbye')
29 | expect(mod.getPhases()).toEqual(expect.arrayContaining(['access']))
30 | expect(mod.getPriority()).toBe(plugin.Priority)
31 | expect(mod.getVersion()).toBe(plugin.Version)
32 | expect(mod.getSchema()).toMatchObject(plugin.Schema)
33 |
34 | expect(instance.getName()).toBe('goodbye')
35 | expect(instance.isExpired(10)).toBe(false)
36 |
37 | const lastCloseInstanceTime = mod.lastCloseInstanceTime + 0
38 | instance.close()
39 | await sleep(1000)
40 |
41 | expect(mod.getLastCloseInstanceTime()).toBeGreaterThan(lastCloseInstanceTime)
42 |
43 | expect(instance.getConfig()).toMatchObject({message: 'test'})
44 | expect(instance.getStartTime()).toBeLessThan(Date.now()/1000)
45 |
46 | instance.resetExpireTs()
47 | expect(instance.getLastUsedTime()).toBeGreaterThan(0)
48 | });
49 |
--------------------------------------------------------------------------------
/bin/kong-js-pluginserver:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | 'use strict'
4 |
5 | let path = require('path')
6 |
7 | const msgpack = require("@msgpack/msgpack")
8 | const logger = require('node-color-log')
9 |
10 | let cli = require('../cli')
11 | let Server = require('../server')
12 | let Listener = require('../listener')
13 |
14 | const opts = cli.parse()
15 |
16 | logger.setLevel(opts.logLevel)
17 |
18 | const ps = new Server(opts.pluginsDirectory, logger)
19 | if (opts.DumpPluginInfo) {
20 | ps.GetPluginInfo(opts.DumpPluginInfo)
21 | .then((v) => {
22 | process.stdout.write(msgpack.encode(v))
23 | })
24 | } else if (opts.DumpAllPlugins) {
25 | Promise.all(function *() {
26 | for (const plugin of ps.getPlugins().keys()) {
27 | yield ps.GetPluginInfo(plugin)
28 | }
29 | }())
30 | .then((values) => {
31 | console.log(JSON.stringify(values))
32 | })
33 | } else {
34 | let l = new Listener(ps, path.join(opts.kongPrefix, opts.sockName))
35 | l.serve()
36 | }
37 |
38 | ps.close()
39 |
--------------------------------------------------------------------------------
/cli.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const {
4 | Command,
5 | Option
6 | } = require('commander')
7 |
8 | const {version} = require('./package.json')
9 |
10 | const program = new Command()
11 | program.version(version, '--version')
12 |
13 | const logLevel = ["info", "debug"]
14 |
15 | function increaseVerbosity(dummyValue, previous) {
16 | return previous + 1;
17 | }
18 |
19 | function parse(dedicated) {
20 | program
21 | .option('-p, --kong-prefix, -kong-prefix ',
22 | 'unix domain socket path to listen',
23 | '/usr/local/kong')
24 | .option('-v, --verbose', 'verbose logging', increaseVerbosity, 0)
25 | .option('--sock-name ', 'socket name to listen on', 'js_pluginserver.sock')
26 |
27 | if (dedicated) {
28 | program.option('--dump, -dump', 'dump current plugin info into stdout')
29 | } else {
30 | program
31 | .requiredOption('-d, --plugins-directory, -plugins-directory ',
32 | 'plugins directory for .js files')
33 | .option('--dump-plugin-info, -dump-plugin-info ',
34 | 'dump specific plugin info into stdout')
35 | .option('--dump-all-plugins, -dump-all-plugins', 'dump all plugins info into stdout')
36 | }
37 |
38 | program.parse(process.argv)
39 |
40 | let opts = program.opts()
41 | opts.verbose = opts.verbose || 0
42 | opts.logLevel = logLevel[opts.verbose > 1 ? 1 : opts.verbose]
43 |
44 | return opts
45 | }
46 |
47 | module.exports.parse = parse
48 |
--------------------------------------------------------------------------------
/examples/js-goodbye/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is an example plugin that add a header to the response
4 |
5 | class KongPlugin {
6 | constructor(config) {
7 | this.config = config
8 | this.message = config.message || 'goodbye'
9 | }
10 |
11 | async access(kong) {
12 | await Promise.all([
13 | kong.response.setHeader('x-goodbye-from-javascript', `Javascript says ${this.message}`),
14 | kong.response.setHeader('x-javascript-pid', process.pid),
15 | ])
16 | }
17 | }
18 |
19 | module.exports = {
20 | Plugin: KongPlugin,
21 | Name: 'goodbye',
22 | Schema: [
23 | { message: { type: 'string' } },
24 | ],
25 | Version: '0.1.0',
26 | Priority: 0,
27 | }
28 |
--------------------------------------------------------------------------------
/examples/js-goodbye/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "js-goodbye",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC"
12 | }
13 |
--------------------------------------------------------------------------------
/examples/js-graphql/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var { graphql, buildSchema } = require('graphql');
3 |
4 | // Construct a schema, using GraphQL schema language
5 | var schema = buildSchema(`
6 | type Query {
7 | hello: String
8 | }
9 | `);
10 |
11 | // The rootValue provides a resolver function for each API endpoint
12 | var rootValue = {
13 | hello: () => {
14 | return 'Hello world!';
15 | },
16 | };
17 |
18 | // This is an example plugin that add a header to the response
19 |
20 | class KongPlugin {
21 | constructor(config) {
22 | this.config = config
23 | }
24 |
25 | async access(kong) {
26 | let body = await kong.request.getRawBody()
27 |
28 | let response = await graphql({
29 | schema,
30 | source: body,
31 | rootValue
32 | })
33 |
34 | await kong.response.exit(200, response)
35 | }
36 | }
37 |
38 | module.exports = {
39 | Plugin: KongPlugin,
40 | Name: 'js-graphql',
41 | Schema: [
42 | { message: { type: "string" } },
43 | ],
44 | Version: '0.1.0',
45 | Priority: 0,
46 | }
47 |
--------------------------------------------------------------------------------
/examples/js-graphql/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kong-plugin-js-graphql",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "graphql": "^16.4.0"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/examples/js-hello.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is an example plugin that add a header to the response
4 |
5 | class KongPlugin {
6 | constructor(config) {
7 | this.config = config
8 | }
9 |
10 | async access(kong) {
11 | let host = await kong.request.getHeader("host")
12 | if (host === undefined) {
13 | return await kong.log.err("unable to get header for request")
14 | }
15 |
16 | let message = this.config.message || "hello"
17 |
18 | // the following can be "parallel"ed
19 | await Promise.all([
20 | kong.response.setHeader("x-hello-from-javascript", "Javascript says " + message + " to " + host),
21 | kong.response.setHeader("x-javascript-pid", process.pid),
22 | ])
23 | }
24 | }
25 |
26 | module.exports = {
27 | Plugin: KongPlugin,
28 | Schema: [
29 | { message: { type: "string" } },
30 | ],
31 | Version: '0.1.0',
32 | Priority: 0,
33 | }
34 |
--------------------------------------------------------------------------------
/examples/js-status.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is an example plugin that sets the response status based on the existence of a header
4 |
5 | class KongPlugin {
6 | constructor(config) {
7 | this.config = config
8 | }
9 |
10 | async access(kong) {
11 | let userId = await kong.request.getHeader("userId")
12 | if (!userId) {
13 | return kong.response.exit(403);
14 | }
15 |
16 | let message = this.config.message || "hello"
17 |
18 | await Promise.all([
19 | kong.response.setHeader("x-welcome", "Javascript says " + message + " to " + userId),
20 | ])
21 | }
22 | }
23 |
24 | module.exports = {
25 | Plugin: KongPlugin,
26 | Schema: [
27 | { message: { type: "string" } },
28 | ],
29 | Version: '0.1.0',
30 | Priority: 0,
31 | }
32 |
--------------------------------------------------------------------------------
/examples/js-transform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is an example plugin that appends a string in response body
4 |
5 | class KongPlugin {
6 | constructor(config) {
7 | this.config = config
8 | }
9 |
10 | async access(kong) {
11 | // Only need the following if response handler not exist
12 | // buffered proxying is automatically turned on for plugin
13 | // with a response handler
14 | // await kong.service.request.enableBuffering()
15 |
16 | let requestBody = await kong.request.getRawBody()
17 | // convert it to uppercase
18 | requestBody = requestBody.replace(/(.)/g, function(v) { return v.toUpperCase(); })
19 |
20 | await Promise.all([
21 | // append "?js-transform=v0.1.0" to request line to upstream
22 | kong.service.request.setQuery({
23 | "js-transform": "v0.1.0",
24 | }),
25 | // set the transformed request body to upstream
26 | kong.service.request.setRawBody(requestBody),
27 | ])
28 | }
29 |
30 | // Note: by defining reponse handler implictly turns on buffered proxying
31 | // on the Route/Service and may break connections like WebSocket
32 | // and has performance penalty
33 | async response(kong) {
34 | if (await kong.response.getSource() == "service") {
35 | let body = await kong.service.response.getRawBody()
36 |
37 | body = "Response body from upstream:\n" + body + "\nBody size: " + body.length + "\n"
38 |
39 | await kong.response.exit(await kong.response.getStatus(), body)
40 | }
41 | }
42 | }
43 |
44 | module.exports = {
45 | Plugin: KongPlugin,
46 | Schema: [{
47 | message: {
48 | type: "string"
49 | }
50 | }, ],
51 | Version: '0.1.0',
52 | Priority: 0,
53 | }
54 |
--------------------------------------------------------------------------------
/examples/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "test": "jest"
4 | },
5 | "devDependencies": {
6 | "jest": "^29.2.2"
7 | },
8 | "dependencies": {
9 | "kong-pdk": "file:.."
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/examples/ts-hello.ts:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import kong from "../kong"
4 |
5 | // This is an example plugin that add a header to the response
6 |
7 | class KongPlugin {
8 | config: any;
9 | constructor(config: any) {
10 | this.config = config
11 | }
12 |
13 | async access(kong: kong) {
14 | let host = await kong.request.getHeader("host")
15 | if (host === undefined) {
16 | return await kong.log.err("unable to get header for request")
17 | }
18 |
19 | let message = this.config.message || "hello"
20 |
21 | // the following can be "parallel"ed
22 | await Promise.all([
23 | kong.response.setHeader("x-hello-from-javascript", "Javascript says " + message + " to " + host),
24 | kong.response.setHeader("x-javascript-pid", process.pid),
25 | ])
26 | }
27 | }
28 |
29 | module.exports = {
30 | Plugin: KongPlugin,
31 | Name: 'hello',
32 | Schema: [
33 | { message: { type: "string" } },
34 | ],
35 | Version: '0.1.0',
36 | Priority: 0,
37 | }
38 |
--------------------------------------------------------------------------------
/kong/client/index.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/client.lua
3 |
4 | import type tls from "./tls"
5 |
6 | export default interface client {
7 |
8 | tls: tls;
9 |
10 | /**
11 | * -- assuming `credential` and `consumer` have been set by some authentication code
12 | * kong.client.authenticate(consumer, credentials)
13 | * @param consumer The consumer to set. If no
14 | value is provided, then any existing value will be cleared.
15 | * @param credential The credential to set. If
16 | no value is provided, then any existing value will be cleared.
17 | */
18 | authenticate(consumer: Array | object, credential: Array | object): Promise;
19 |
20 | /**
21 | * -- assuming `consumer_id` is provided by some code
22 | * kong.client.authenticate_consumer_group_by_consumer_id(consumer_id)
23 | * @param consumer_id The consumer id to use for setting the consumer group.
24 | If no value is provided, the current consumer group is not changed.
25 | */
26 | authenticateConsumerGroupByConsumerId(consumer_id: string): Promise;
27 |
28 | /**
29 | * local consumer = kong.client.get_consumer()
30 | * if consumer then
31 | * consumer_id = consumer.id
32 | * else
33 | * -- request not authenticated yet, or a credential
34 | * -- without a consumer (external auth)
35 | * end
36 | * @returns The authenticated consumer entity.
37 | */
38 | getConsumer(): Promise | object>;
39 |
40 | /**
41 | * local group = kong.client.get_consumer_group()
42 | * @returns The authenticated consumer group. Returns `nil` if no
43 | consumer group has been authenticated for the current request.
44 | */
45 | getConsumerGroup(): Promise | object>;
46 |
47 | /**
48 | * local groups = kong.client.get_consumer_groups()
49 | * @returns The authenticated consumer groups. Returns `nil` if no
50 | consumer groups has been authenticated for the current request.
51 | */
52 | getConsumerGroups(): Promise | object>;
53 |
54 | /**
55 | * local credential = kong.client.get_credential()
56 | * if credential then
57 | * consumer_id = credential.consumer_id
58 | * else
59 | * -- request not authenticated yet
60 | * end
61 | * @returns The authenticated credential.
62 | */
63 | getCredential(): Promise;
64 |
65 | /**
66 | * -- Given a client with IP 127.0.0.1 making connection through
67 | * -- a load balancer with IP 10.0.0.1 to Kong answering the request for
68 | * -- https://username:password@example.com:1234/v1/movies
69 | * kong.client.get_forwarded_ip() -- "127.0.0.1"
70 | * -- Note: This example assumes that 10.0.0.1 is one of the trusted IPs, and that
71 | * -- the load balancer adds the right headers matching with the configuration
72 | * -- of `real_ip_header`, e.g. `proxy_protocol`.
73 | * @returns The remote IP address of the client making the request,
74 | considering forwarded addresses.
75 | */
76 | getForwardedIp(): Promise;
77 |
78 | /**
79 | * -- [client]:40000 <-> 80:[balancer]:30000 <-> 80:[kong]:20000 <-> 80:[service]
80 | * kong.client.get_forwarded_port() -- 40000
81 | * -- Note: This example assumes that [balancer] is one of the trusted IPs, and that
82 | * -- the load balancer adds the right headers matching with the configuration
83 | * -- of `real_ip_header`, e.g. `proxy_protocol`.
84 | * @returns The remote client port, considering forwarded ports.
85 | */
86 | getForwardedPort(): Promise;
87 |
88 | /**
89 | * -- Given a client with IP 127.0.0.1 making connection through
90 | * -- a load balancer with IP 10.0.0.1 to Kong answering the request for
91 | * -- https://example.com:1234/v1/movies
92 | * kong.client.get_ip() -- "10.0.0.1"
93 | * @returns The remote IP address of the client making the request.
94 | */
95 | getIp(): Promise;
96 |
97 | /**
98 | * -- [client]:40000 <-> 80:[balancer]:30000 <-> 80:[kong]:20000 <-> 80:[service]
99 | * kong.client.get_port() -- 30000
100 | * @returns The remote client port.
101 | */
102 | getPort(): Promise;
103 |
104 | /**
105 | * kong.client.get_protocol() -- "http"
106 | * @param allow_terminated? If set, the `X-Forwarded-Proto` header is checked when checking for HTTPS.
107 | * @returns Can be one of `"http"`, `"https"`, `"tcp"`, `"tls"` or `nil`.
108 | * @returns `nil` if successful, or an error message if it fails.
109 | */
110 | getProtocol(allow_terminated?: boolean): Promise<[ret_1: string, err: string]>;
111 |
112 | /**
113 | * local consumer_id = "john_doe"
114 | * local consumer = kong.client.load_consumer(consumer_id, true)
115 | * @param consumer_id The consumer ID to look up.
116 | * @param search_by_username? If truthy,
117 | and if the consumer is not found by ID,
118 | then a second search by username will be performed.
119 | * @returns Consumer entity or `nil`.
120 | * @returns `nil` if successful, or an error message if it fails.
121 | */
122 | loadConsumer(consumer_id: string, search_by_username?: boolean): Promise<[ret_1: Array | object, err: string]>;
123 |
124 | /**
125 | * -- assuming `group` is provided by some code
126 | * kong.client.set_authenticated_consumer_group(group)
127 | * @param group The consumer group to set. If no
128 | value is provided, then any existing value will be cleared.
129 | this value should be a table with metadata of the group like its `id` and `name`.
130 | */
131 | setAuthenticatedConsumerGroup(group: Array | object): Promise;
132 |
133 | /**
134 | * kong.client.set_authenticated_consumer_groups({
135 | * {
136 | * id = "fed2bf38-10c4-404e-8d45-a2b0f521464d",
137 | * name = "my-group",
138 | * },
139 | * {
140 | * id = "736bb9d9-98f2-46d5-97fc-d7361d9488ee",
141 | * name = "my-other-group",
142 | * }
143 | * })
144 | * @param groups The consumer groups to set. If no
145 | value is provided, then any existing value will be cleared.
146 | This value should be a sequence-like table of tables, with each item
147 | having at least an `id` and a `name`.
148 | */
149 | setAuthenticatedConsumerGroups(groups: Array | object): Promise;
150 |
151 | }
152 |
--------------------------------------------------------------------------------
/kong/client/tls.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/client/tls.lua
3 |
4 |
5 | export default interface tls {
6 |
7 |
8 | /**
9 | * local res, err = kong.client.tls.disable_session_reuse()
10 | * if not res then
11 | * -- do something with err
12 | * end
13 | * @returns Returns `true` if successful, `nil` if it fails.
14 | * @returns Returns `nil` if successful, or an error message if it fails.
15 | */
16 | disableSessionReuse(): Promise<[ret_1: boolean, err: string]>;
17 |
18 | /**
19 | * local cert, err = kong.client.tls.get_full_client_certificate_chain()
20 | * if err then
21 | * -- do something with err
22 | * end
23 | * if not cert then
24 | * -- client did not complete mTLS
25 | * end
26 | * -- do something with cert
27 | * @returns Returns a PEM-encoded client certificate if the mTLS
28 | handshake was completed, or `nil` if an error occurred or the client did
29 | not present its certificate.
30 | * @returns Returns `nil` if successful, or an error message if it fails.
31 | */
32 | getFullClientCertificateChain(): Promise<[ret_1: string, err: string]>;
33 |
34 | /**
35 | * local x509_lib = require "resty.openssl.x509"
36 | * local chain_lib = require "resty.openssl.x509.chain"
37 | * local res, err
38 | * local chain = chain_lib.new()
39 | * -- err check
40 | * local x509, err = x509_lib.new(pem_cert, "PEM")
41 | * -- err check
42 | * res, err = chain:add(x509)
43 | * -- err check
44 | * -- `chain.ctx` is the raw data of the chain, i.e. `STACK_OF(X509) *`
45 | * res, err = kong.client.tls.request_client_certificate(chain.ctx)
46 | * if not res then
47 | * -- do something with err
48 | * end
49 | * @param ca_certs? The CA certificate chain opaque pointer
50 | * @returns Returns `true` if successful, or `nil` if it fails.
51 | * @returns Returns `nil` if successful, or an error message if it fails.
52 | */
53 | requestClientCertificate(ca_certs?: cdata): Promise<[ret_1: boolean, err: string]>;
54 |
55 | /**
56 | * kong.client.tls.set_client_verify("FAILED:unknown CA")
57 | */
58 | setClientVerify(): Promise;
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/kong/cluster.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/cluster.lua
3 |
4 |
5 | export default interface cluster {
6 |
7 |
8 | /**
9 | * local id, err = kong.cluster.get_id()
10 | * if err then
11 | * -- handle error
12 | * end
13 | * if not id then
14 | * -- no cluster ID is available
15 | * end
16 | * -- use id here
17 | * @returns The v4 UUID used by this cluster as its ID.
18 | * @returns An error message.
19 | */
20 | getId(): Promise<[ret_1: string, ret_2: string]>;
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/kong/ctx/index.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/ctx.lua
3 |
4 | import type shared from "./shared"
5 |
6 | export default interface ctx {
7 |
8 | shared: shared;
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/kong/ctx/shared.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/ctx/shared.lua
3 |
4 |
5 | export default interface shared {
6 |
7 |
8 | /**
9 | *
10 | * @param k key for the ctx data
11 | * @returns the per-request context data in ngx.ctx
12 | */
13 | get(k: string): Promise;
14 |
15 | /**
16 | *
17 | * @param k key for the ctx data
18 | * @param v value for the ctx data
19 | */
20 | set(k: string, v: string): Promise;
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/kong/enterprise_edition/index.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/enterprise_edition.lua
3 |
4 | import type jwe from "./jwe"
5 |
6 | export default interface enterpriseEdition {
7 |
8 | jwe: jwe;
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/kong/enterprise_edition/jwe.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/enterprise_edition/jwe.lua
3 |
4 |
5 | export default interface jwe {
6 |
7 |
8 | /**
9 | *
10 | * @param token JWE encrypted JWT token
11 | * @returns A table containing JWT token parts decoded, or nil
12 | * @returns Error message, or nil
13 | */
14 | decode(token: string): Promise<[ret_1: string, ret_2: string]>;
15 |
16 | /**
17 | *
18 | * @param key Private key
19 | * @param token JWE encrypted JWT token
20 | * @returns JWT token payload in plaintext, or nil
21 | * @returns Error message, or nil
22 | */
23 | decrypt(key: any, token: string): Promise<[ret_1: string, ret_2: string]>;
24 |
25 | /**
26 | *
27 | * @param alg Algorithm used for key management
28 | * @param enc Encryption algorithm used for content encryption
29 | * @param key Public key
30 | * @param plaintext Plaintext
31 | * @param options? Options (optional), default: nil
32 | * @returns JWE encrypted JWT token, or nil
33 | * @returns Error message, or nil
34 | */
35 | encrypt(alg: string, enc: string, key: any, plaintext: string, options?: Array | object): Promise<[ret_1: string, ret_2: string]>;
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/kong/index.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk.lua
3 |
4 | import type client from "./client"
5 | import type cluster from "./cluster"
6 | import type ctx from "./ctx"
7 | import type enterpriseEdition from "./enterpriseEdition"
8 | import type ip from "./ip"
9 | import type log from "./log"
10 | import type nginx from "./nginx"
11 | import type node from "./node"
12 | import type plugin from "./plugin"
13 | import type request from "./request"
14 | import type response from "./response"
15 | import type router from "./router"
16 | import type service from "./service"
17 | import type telemetry from "./telemetry"
18 | import type vault from "./vault"
19 |
20 | export default interface kong {
21 |
22 | client: client;
23 | cluster: cluster;
24 | ctx: ctx;
25 | enterpriseEdition: enterpriseEdition;
26 | ip: ip;
27 | log: log;
28 | nginx: nginx;
29 | node: node;
30 | plugin: plugin;
31 | request: request;
32 | response: response;
33 | router: router;
34 | service: service;
35 | telemetry: telemetry;
36 | vault: vault;
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/kong/ip.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/ip.lua
3 |
4 |
5 | export default interface ip {
6 |
7 |
8 | /**
9 | * if kong.ip.is_trusted("1.1.1.1") then
10 | * kong.log("The IP is trusted")
11 | * end
12 | * @param address A string representing an IP address.
13 | * @returns `true` if the IP is trusted, `false` otherwise.
14 | */
15 | isTrusted(address: string): Promise;
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/kong/log.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/log.lua
3 |
4 |
5 | export default interface log {
6 |
7 |
8 | /**
9 | * kong.log.warn("something require attention")
10 | * kong.log.err("something failed: ", err)
11 | * kong.log.alert("something requires immediate action")
12 | * @param ...varargs All params will be concatenated and stringified before being sent to the log.
13 | * @returns Throws an error on invalid inputs.
14 | */
15 | alert(...varargs: any): Promise;
16 |
17 | /**
18 | * kong.log.warn("something require attention")
19 | * kong.log.err("something failed: ", err)
20 | * kong.log.alert("something requires immediate action")
21 | * @param ...varargs All params will be concatenated and stringified before being sent to the log.
22 | * @returns Throws an error on invalid inputs.
23 | */
24 | crit(...varargs: any): Promise;
25 |
26 | /**
27 | * kong.log.warn("something require attention")
28 | * kong.log.err("something failed: ", err)
29 | * kong.log.alert("something requires immediate action")
30 | * @param ...varargs All params will be concatenated and stringified before being sent to the log.
31 | * @returns Throws an error on invalid inputs.
32 | */
33 | debug(...varargs: any): Promise;
34 |
35 | /**
36 | * kong.log.deprecation("hello ", "world")
37 | * kong.log.deprecation("hello ", "world", { after = "2.5.0" })
38 | * kong.log.deprecation("hello ", "world", { removal = "3.0.0" })
39 | * kong.log.deprecation("hello ", "world", { after = "2.5.0", removal = "3.0.0" })
40 | * kong.log.deprecation("hello ", "world", { trace = true })
41 | * @param ...varargs all params will be concatenated and stringified before being sent to the log
42 | (if the last param is a table, it is considered as a deprecation metadata)
43 | * @returns throws an error on invalid inputs.
44 | */
45 | deprecation(...varargs: any): Promise;
46 |
47 | /**
48 | * kong.log.warn("something require attention")
49 | * kong.log.err("something failed: ", err)
50 | * kong.log.alert("something requires immediate action")
51 | * @param ...varargs All params will be concatenated and stringified before being sent to the log.
52 | * @returns Throws an error on invalid inputs.
53 | */
54 | err(...varargs: any): Promise;
55 |
56 | /**
57 | * kong.log.warn("something require attention")
58 | * kong.log.err("something failed: ", err)
59 | * kong.log.alert("something requires immediate action")
60 | * @param ...varargs All params will be concatenated and stringified before being sent to the log.
61 | * @returns Throws an error on invalid inputs.
62 | */
63 | info(...varargs: any): Promise;
64 |
65 | /**
66 | * kong.log.warn("something require attention")
67 | * kong.log.err("something failed: ", err)
68 | * kong.log.alert("something requires immediate action")
69 | * @param ...varargs All params will be concatenated and stringified before being sent to the log.
70 | * @returns Throws an error on invalid inputs.
71 | */
72 | notice(...varargs: any): Promise;
73 |
74 | /**
75 | *
76 | */
77 | serialize(): Promise;
78 |
79 | /**
80 | * -- Adds a new value to the serialized table
81 | * kong.log.set_serialize_value("my_new_value", 1)
82 | * assert(kong.log.serialize().my_new_value == 1)
83 | * -- Value can be a table
84 | * kong.log.set_serialize_value("my", { new = { value = 2 } })
85 | * assert(kong.log.serialize().my.new.value == 2)
86 | * -- It is possible to change an existing serialized value
87 | * kong.log.set_serialize_value("my_new_value", 3)
88 | * assert(kong.log.serialize().my_new_value == 3)
89 | * -- Unset an existing value by setting it to nil
90 | * kong.log.set_serialize_value("my_new_value", nil)
91 | * assert(kong.log.serialize().my_new_value == nil)
92 | * -- Dots in the key are interpreted as table accesses
93 | * kong.log.set_serialize_value("my.new.value", 4)
94 | * assert(kong.log.serialize().my.new_value == 4)
95 | * @param key The name of the field.
96 | * @param value Value to be set. When a table is used, its keys must be numbers, strings, or booleans, and its values can be numbers, strings, or other tables like itself, recursively.
97 | * @param options Can contain two entries: options.mode can be `set` (the default, always sets), `add` (only add if entry does not already exist) and `replace` (only change value if it already exists).
98 | * @returns The request information table.
99 | */
100 | setSerializeValue(key: string, value: any, options: Array | object): Promise | object>;
101 |
102 | /**
103 | * kong.log.warn("something require attention")
104 | * kong.log.err("something failed: ", err)
105 | * kong.log.alert("something requires immediate action")
106 | * @param ...varargs All params will be concatenated and stringified before being sent to the log.
107 | * @returns Throws an error on invalid inputs.
108 | */
109 | warn(...varargs: any): Promise;
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/kong/nginx/index.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/nginx.lua
3 |
4 | import type shared from "./shared"
5 |
6 | export default interface nginx {
7 |
8 | shared: shared;
9 |
10 | /**
11 | *
12 | * @param k key for the ctx data
13 | * @returns the per-request context data in ngx.ctx
14 | */
15 | getCtx(k: string): Promise;
16 |
17 | /**
18 | * local nginx_statistics = kong.nginx.get_statistics()
19 | * @returns Nginx connections and requests statistics
20 | */
21 | getStatistics(): Promise | object>;
22 |
23 | /**
24 | *
25 | * @returns the subsystem name
26 | */
27 | getSubsystem(): Promise;
28 |
29 | /**
30 | *
31 | * @returns the TLSv1 version string
32 | */
33 | getTls1_versionStr(): Promise;
34 |
35 | /**
36 | *
37 | * @returns get NGINX variable value
38 | */
39 | getVar(): Promise;
40 |
41 | /**
42 | *
43 | * @returns ret_1
44 | */
45 | reqStartTime(): Promise;
46 |
47 | /**
48 | *
49 | * @param k key for the ctx data
50 | * @param any value for the ctx data
51 | */
52 | setCtx(k: string, any: string): Promise;
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/kong/nginx/shared.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/nginx/shared.lua
3 |
4 |
5 | export default interface shared {
6 |
7 |
8 | /**
9 | *
10 | * @param k key for the ctx data
11 | * @returns the per-request context data in ngx.ctx
12 | */
13 | get(k: string): Promise;
14 |
15 | /**
16 | *
17 | * @param k key for the ctx data
18 | * @param v value for the ctx data
19 | */
20 | set(k: string, v: string): Promise;
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/kong/node.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/node.lua
3 |
4 |
5 | export default interface node {
6 |
7 |
8 | /**
9 | * local hostname = kong.node.get_hostname()
10 | * @returns The local machine hostname.
11 | */
12 | getHostname(): Promise;
13 |
14 | /**
15 | * local id = kong.node.get_id()
16 | * @returns The v4 UUID used by this node as its ID.
17 | */
18 | getId(): Promise;
19 |
20 | /**
21 | * local res = kong.node.get_memory_stats()
22 | * -- res will have the following structure:
23 | * {
24 | * lua_shared_dicts = {
25 | * kong = {
26 | * allocated_slabs = 12288,
27 | * capacity = 24576
28 | * },
29 | * kong_db_cache = {
30 | * allocated_slabs = 12288,
31 | * capacity = 12288
32 | * }
33 | * },
34 | * workers_lua_vms = {
35 | * {
36 | * http_allocated_gc = 1102,
37 | * pid = 18004
38 | * },
39 | * {
40 | * http_allocated_gc = 1102,
41 | * pid = 18005
42 | * }
43 | * }
44 | * }
45 | * local res = kong.node.get_memory_stats("k", 1)
46 | * -- res will have the following structure:
47 | * {
48 | * lua_shared_dicts = {
49 | * kong = {
50 | * allocated_slabs = "12.0 KiB",
51 | * capacity = "24.0 KiB",
52 | * },
53 | * kong_db_cache = {
54 | * allocated_slabs = "12.0 KiB",
55 | * capacity = "12.0 KiB",
56 | * }
57 | * },
58 | * workers_lua_vms = {
59 | * {
60 | * http_allocated_gc = "1.1 KiB",
61 | * pid = 18004
62 | * },
63 | * {
64 | * http_allocated_gc = "1.1 KiB",
65 | * pid = 18005
66 | * }
67 | * }
68 | * }
69 | * @param unit? The unit that memory is reported in. Can be
70 | any of `b/B`, `k/K`, `m/M`, or `g/G` for bytes, kibibytes, mebibytes,
71 | or gibibytes, respectively. Defaults to `b` (bytes).
72 | * @param scale? The number of digits to the right of the decimal
73 | point. Defaults to 2.
74 | * @returns A table containing memory usage statistics for this node.
75 | If `unit` is `b/B` (the default), reported values are Lua numbers.
76 | Otherwise, reported values are strings with the unit as a suffix.
77 | */
78 | getMemoryStats(unit?: string, scale?: number): Promise | object>;
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/kong/plugin.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/plugin.lua
3 |
4 |
5 | export default interface plugin {
6 |
7 |
8 | /**
9 | * kong.plugin.get_id() -- "123e4567-e89b-12d3-a456-426614174000"
10 | * @returns The ID of the running plugin
11 | */
12 | getId(): Promise;
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/kong/request.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/request.lua
3 |
4 |
5 | export default interface request {
6 |
7 |
8 | /**
9 | * local body, err, mimetype = kong.request.get_body()
10 | * body.name -- "John Doe"
11 | * body.age -- "42"
12 | * @param mimetype? The MIME type.
13 | * @param max_args? Sets a limit on the maximum number of parsed
14 | * @param max_allowed_file_size? the max allowed file size to be read from
15 | arguments.
16 | * @returns A table representation of the body.
17 | * @returns An error message.
18 | * @returns mimetype The MIME type used.
19 | */
20 | getBody(mimetype?: string, max_args?: number, max_allowed_file_size?: number): Promise<[ret_1: Array | object, ret_2: string, ret_3: string]>;
21 |
22 | /**
23 | * kong.request.get_forwarded_host() -- "example.com"
24 | * @returns The forwarded host.
25 | */
26 | getForwardedHost(): Promise;
27 |
28 | /**
29 | * kong.request.get_forwarded_path() -- /path
30 | * @returns The forwarded path.
31 | */
32 | getForwardedPath(): Promise;
33 |
34 | /**
35 | * kong.request.get_forwarded_port() -- 1234
36 | * @returns The forwarded port.
37 | */
38 | getForwardedPort(): Promise;
39 |
40 | /**
41 | * kong.request.get_forwarded_prefix() -- /prefix
42 | * @returns The forwarded path prefix or `nil` if the prefix was
43 | not stripped.
44 | */
45 | getForwardedPrefix(): Promise;
46 |
47 | /**
48 | * kong.request.get_forwarded_scheme() -- "https"
49 | * @returns The forwarded scheme.
50 | */
51 | getForwardedScheme(): Promise;
52 |
53 | /**
54 | * -- Given a request with the following headers:
55 | * -- Host: foo.com
56 | * -- X-Custom-Header: bla
57 | * -- X-Another: foo bar
58 | * -- X-Another: baz
59 | * kong.request.get_header("Host") -- "foo.com"
60 | * kong.request.get_header("x-custom-header") -- "bla"
61 | * kong.request.get_header("X-Another") -- "foo bar"
62 | * @param name the name of the header to be returned
63 | * @returns the value of the header or nil if not present
64 | */
65 | getHeader(name: string): Promise;
66 |
67 | /**
68 | * -- Given a request with the following headers:
69 | * -- Host: foo.com
70 | * -- X-Custom-Header: bla
71 | * -- X-Another: foo bar
72 | * -- X-Another: baz
73 | * local headers = kong.request.get_headers()
74 | * headers.host -- "foo.com"
75 | * headers.x_custom_header -- "bla"
76 | * headers.x_another[1] -- "foo bar"
77 | * headers["X-Another"][2] -- "baz"
78 | * @param max_headers? Sets a limit on the maximum number of
79 | parsed headers.
80 | * @returns The request headers in table form.
81 | */
82 | getHeaders(max_headers?: number): Promise | object>;
83 |
84 | /**
85 | * -- Given a request to https://example.com:1234/v1/movies
86 | * kong.request.get_host() -- "example.com"
87 | * @returns The hostname.
88 | */
89 | getHost(): Promise;
90 |
91 | /**
92 | * kong.request.get_http_version() -- 1.1
93 | * @returns The HTTP version as a Lua number.
94 | */
95 | getHttpVersion(): Promise;
96 |
97 | /**
98 | * kong.request.get_method() -- "GET"
99 | * @returns The request method.
100 | */
101 | getMethod(): Promise;
102 |
103 | /**
104 | * -- Given a request to https://example.com/t/Abc%20123%C3%B8%2f/parent/..//test/./
105 | * kong.request.get_path() -- "/t/Abc 123ø%2F/test/"
106 | * @returns the path
107 | */
108 | getPath(): Promise;
109 |
110 | /**
111 | * -- Given a request to https://example.com:1234/v1/movies?movie=foo
112 | * kong.request.get_path_with_query() -- "/v1/movies?movie=foo"
113 | * @returns The path with the query string.
114 | */
115 | getPathWithQuery(): Promise;
116 |
117 | /**
118 | * -- Given a request to https://example.com:1234/v1/movies
119 | * kong.request.get_port() -- 1234
120 | * @returns The port.
121 | */
122 | getPort(): Promise;
123 |
124 | /**
125 | * -- Given a request GET /test?foo=hello%20world&bar=baz&zzz&blo=&bar=bla&bar
126 | * for k, v in pairs(kong.request.get_query()) do
127 | * kong.log.inspect(k, v)
128 | * end
129 | * -- Will print
130 | * -- "foo" "hello world"
131 | * -- "bar" {"baz", "bla", true}
132 | * -- "zzz" true
133 | * -- "blo" ""
134 | * @param max_args? Sets a limit on the maximum number of parsed
135 | arguments.
136 | * @returns A table representation of the query string.
137 | */
138 | getQuery(max_args?: number): Promise | object>;
139 |
140 | /**
141 | * -- Given a request GET /test?foo=hello%20world&bar=baz&zzz&blo=&bar=bla&bar
142 | * kong.request.get_query_arg("foo") -- "hello world"
143 | * kong.request.get_query_arg("bar") -- "baz"
144 | * kong.request.get_query_arg("zzz") -- true
145 | * kong.request.get_query_arg("blo") -- ""
146 | * @returns The value of the argument.
147 | */
148 | getQueryArg(): Promise;
149 |
150 | /**
151 | * -- Given a body with payload "Hello, Earth!":
152 | * kong.request.get_raw_body():gsub("Earth", "Mars") -- "Hello, Mars!"
153 | * @returns The plain request body or nil if it does not fit into
154 | the NGINX temporary buffer.
155 | * @returns An error message.
156 | */
157 | getRawBody(): Promise<[ret_1: Buffer, ret_2: string]>;
158 |
159 | /**
160 | * -- Given a request to https://example.com/t/Abc%20123%C3%B8%2f/parent/..//test/./?movie=foo
161 | * kong.request.get_raw_path() -- "/t/Abc%20123%C3%B8%2f/parent/..//test/./"
162 | * @returns The path.
163 | */
164 | getRawPath(): Promise;
165 |
166 | /**
167 | * -- Given a request to https://example.com/foo?msg=hello%20world&bla=&bar
168 | * kong.request.get_raw_query() -- "msg=hello%20world&bla=&bar"
169 | * @returns The query component of the request's URL.
170 | */
171 | getRawQuery(): Promise;
172 |
173 | /**
174 | * -- Given a request to https://example.com:1234/v1/movies
175 | * kong.request.get_scheme() -- "https"
176 | * @returns A string like `"http"` or `"https"`.
177 | */
178 | getScheme(): Promise;
179 |
180 | /**
181 | * kong.request.get_start_time() -- 1649960273000
182 | * @returns The timestamp
183 | */
184 | getStartTime(): Promise;
185 |
186 | /**
187 | * local captures = kong.request.get_uri_captures()
188 | * for idx, value in ipairs(captures.unnamed) do
189 | * -- do what you want to captures
190 | * end
191 | * for name, value in pairs(captures.named) do
192 | * -- do what you want to captures
193 | * end
194 | * @returns tables containing unamed and named captures.
195 | */
196 | getUriCaptures(): Promise | object>;
197 |
198 | }
199 |
--------------------------------------------------------------------------------
/kong/response.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/response.lua
3 |
4 |
5 | export default interface response {
6 |
7 |
8 | /**
9 | * kong.response.add_header("Cache-Control", "no-cache")
10 | * kong.response.add_header("Cache-Control", "no-store")
11 | * @param name The header name.
12 | * @param of strings|string|number|boolean value The header value.
13 | * @returns throws an error on invalid input.
14 | */
15 | addHeader(name: string, of: array): Promise;
16 |
17 | /**
18 | * kong.response.set_header("X-Foo", "foo")
19 | * kong.response.add_header("X-Foo", "bar")
20 | * kong.response.clear_header("X-Foo")
21 | * -- from here onwards, no X-Foo headers will exist in the response
22 | * @param name The name of the header to be cleared
23 | * @returns throws an error on invalid input.
24 | */
25 | clearHeader(name: string): Promise;
26 |
27 | /**
28 | * return kong.response.error(403, "Access Forbidden", {
29 | * ["Content-Type"] = "text/plain",
30 | * ["WWW-Authenticate"] = "Basic"
31 | * })
32 | * ---
33 | * return kong.response.error(403, "Access Forbidden")
34 | * ---
35 | * return kong.response.error(403)
36 | * @param status The status to be used (>399).
37 | * @param message? The error message to be used.
38 | * @param headers? The headers to be used.
39 | * @returns throws an error on invalid input.
40 | */
41 | error(status: number, message?: string, headers?: Array | object): Promise;
42 |
43 | /**
44 | * return kong.response.exit(403, "Access Forbidden", {
45 | * ["Content-Type"] = "text/plain",
46 | * ["WWW-Authenticate"] = "Basic"
47 | * })
48 | * ---
49 | * return kong.response.exit(403, [[{"message":"Access Forbidden"}]], {
50 | * ["Content-Type"] = "application/json",
51 | * ["WWW-Authenticate"] = "Basic"
52 | * })
53 | * ---
54 | * return kong.response.exit(403, { message = "Access Forbidden" }, {
55 | * ["WWW-Authenticate"] = "Basic"
56 | * })
57 | * ---
58 | * -- In L4 proxy mode
59 | * return kong.response.exit(200, "Success")
60 | * @param status The status to be used.
61 | * @param body? The body to be used.
62 | * @param headers? The headers to be used.
63 | * @returns throws an error on invalid input.
64 | */
65 | exit(status: number, body?: Buffer, headers?: Array | object): Promise;
66 |
67 | /**
68 | * -- Given a response with the following headers:
69 | * -- X-Custom-Header: bla
70 | * -- X-Another: foo bar
71 | * -- X-Another: baz
72 | * kong.response.get_header("x-custom-header") -- "bla"
73 | * kong.response.get_header("X-Another") -- "foo bar"
74 | * kong.response.get_header("X-None") -- nil
75 | * @param name The name of the header.
76 | Header names are case-insensitive and dashes (`-`) can be written as
77 | underscores (`_`). For example, the header `X-Custom-Header` can also be
78 | retrieved as `x_custom_header`.
79 | * @returns The value of the header.
80 | */
81 | getHeader(name: string): Promise;
82 |
83 | /**
84 | * -- Given an response from the Service with the following headers:
85 | * -- X-Custom-Header: bla
86 | * -- X-Another: foo bar
87 | * -- X-Another: baz
88 | * local headers = kong.response.get_headers()
89 | * headers.x_custom_header -- "bla"
90 | * headers.x_another[1] -- "foo bar"
91 | * headers["X-Another"][2] -- "baz"
92 | * @param max_headers? Limits the number of headers parsed.
93 | * @returns headers A table representation of the headers in the
94 | response.
95 | * @returns err If more headers than `max_headers` were present,
96 | returns a string with the error `"truncated"`.
97 | */
98 | getHeaders(max_headers?: number): Promise<[ret_1: Array | object, ret_2: string]>;
99 |
100 | /**
101 | * if kong.response.get_source() == "service" then
102 | * kong.log("The response comes from the Service")
103 | * elseif kong.response.get_source() == "error" then
104 | * kong.log("There was an error while processing the request")
105 | * elseif kong.response.get_source() == "exit" then
106 | * kong.log("There was an early exit while processing the request")
107 | * end
108 | * @returns The source.
109 | */
110 | getSource(): Promise;
111 |
112 | /**
113 | * kong.response.get_status() -- 200
114 | * @returns status The HTTP status code currently set for the
115 | downstream response.
116 | */
117 | getStatus(): Promise;
118 |
119 | /**
120 | * kong.response.set_header("X-Foo", "value")
121 | * @param name The name of the header
122 | * @param of strings|string|number|boolean value The new value for the header.
123 | * @returns throws an error on invalid input.
124 | */
125 | setHeader(name: string, of: array): Promise;
126 |
127 | /**
128 | * kong.response.set_headers({
129 | * ["Bla"] = "boo",
130 | * ["X-Foo"] = "foo3",
131 | * ["Cache-Control"] = { "no-store", "no-cache" }
132 | * })
133 | * -- Will add the following headers to the response, in this order:
134 | * -- X-Bar: bar1
135 | * -- Bla: boo
136 | * -- Cache-Control: no-store
137 | * -- Cache-Control: no-cache
138 | * -- X-Foo: foo3
139 | * @param headers
140 | * @returns throws an error on invalid input.
141 | */
142 | setHeaders(headers: Array | object): Promise;
143 |
144 | /**
145 | * kong.response.set_status(404)
146 | * @param status The new status.
147 | * @returns throws an error on invalid input.
148 | */
149 | setStatus(status: number): Promise;
150 |
151 | }
152 |
--------------------------------------------------------------------------------
/kong/router.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/router.lua
3 |
4 |
5 | export default interface router {
6 |
7 |
8 | /**
9 | * local route = kong.router.get_route()
10 | * local protocols = route.protocols
11 | * @returns The `route` entity.
12 | */
13 | getRoute(): Promise | object>;
14 |
15 | /**
16 | * if kong.router.get_service() then
17 | * -- routed by route & service entities
18 | * else
19 | * -- routed by a route without a service
20 | * end
21 | * @returns The `service` entity.
22 | */
23 | getService(): Promise | object>;
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/kong/service/index.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/service.lua
3 |
4 | import type request from "./request"
5 | import type response from "./response"
6 |
7 | export default interface service {
8 |
9 | request: request;
10 | response: response;
11 |
12 | /**
13 | * kong.service.set_retries(233)
14 | * @param retries
15 | */
16 | setRetries(retries: number): Promise;
17 |
18 | /**
19 | * kong.service.set_target("service.local", 443)
20 | * kong.service.set_target("192.168.130.1", 80)
21 | * @param host
22 | * @param port
23 | */
24 | setTarget(host: string, port: number): Promise;
25 |
26 | /**
27 | * kong.service.set_target_retry_callback(function() return "service.local", 443 end)
28 | * @param retry_callback
29 | */
30 | setTargetRetryCallback(retry_callback: function): Promise;
31 |
32 | /**
33 | * kong.service.set_timeouts(233, 233, 233)
34 | * @param connect_timeout
35 | * @param write_timeout
36 | * @param read_timeout
37 | */
38 | setTimeouts(connect_timeout: number, write_timeout: number, read_timeout: number): Promise;
39 |
40 | /**
41 | * local ok, err = kong.service.set_tls_verify(true)
42 | * if not ok then
43 | * -- do something with error
44 | * end
45 | * @param on Whether to enable TLS certificate verification for the current request
46 | * @returns `true` if the operation succeeded, `nil` if an error occurred
47 | * @returns An error message describing the error if there was one
48 | */
49 | setTlsVerify(on: boolean): Promise<[ret_1: boolean, ret_2: string]>;
50 |
51 | /**
52 | * local ok, err = kong.service.set_tls_verify_depth(3)
53 | * if not ok then
54 | * -- do something with error
55 | * end
56 | * @param depth Depth to use when validating. Must be non-negative
57 | * @returns `true` if the operation succeeded, `nil` if an error occurred
58 | * @returns An error message describing the error if there was one
59 | */
60 | setTlsVerifyDepth(depth: number): Promise<[ret_1: boolean, ret_2: string]>;
61 |
62 | /**
63 | * local ok, err = kong.service.set_upstream("service.prod")
64 | * if not ok then
65 | * kong.log.err(err)
66 | * return
67 | * end
68 | * @param host
69 | * @returns `true` on success, or `nil` if no upstream entities
70 | where found
71 | * @returns An error message describing the error if there was
72 | one.
73 | */
74 | setUpstream(host: string): Promise<[ret_1: boolean, ret_2: string]>;
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/kong/service/request.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/service/request.lua
3 |
4 |
5 | export default interface request {
6 |
7 |
8 | /**
9 | * kong.service.request.add_header("Cache-Control", "no-cache")
10 | * kong.service.request.add_header("Cache-Control", "no-store")
11 | * @param header The header name. Example: "Cache-Control".
12 | * @param of strings|string|number|boolean value The header value. Example: "no-cache".
13 | * @returns throws an error on invalid inputs.
14 | */
15 | addHeader(header: string, of: array): Promise;
16 |
17 | /**
18 | * kong.service.request.set_header("X-Foo", "foo")
19 | * kong.service.request.add_header("X-Foo", "bar")
20 | * kong.service.request.clear_header("X-Foo")
21 | * -- from here onwards, no X-Foo headers will exist in the request
22 | * @param header The header name. Example: "X-Foo".
23 | * @returns throws an error on invalid inputs.
24 | The function does not throw an error if no header was removed.
25 | */
26 | clearHeader(header: string): Promise;
27 |
28 | /**
29 | * local ok, err = kong.service.request.disable_tls()
30 | * if not ok then
31 | * -- do something with error
32 | * end
33 | * @returns `true` if the operation succeeded, `nil` if an error occurred.
34 | * @returns An error message describing the error if there was one.
35 | */
36 | disableTls(): Promise<[ret_1: boolean, ret_2: string]>;
37 |
38 | /**
39 | * kong.service.request.enable_buffering()
40 | * @returns
41 | */
42 | enableBuffering(): Promise;
43 |
44 | /**
45 | * kong.service.set_header("application/json")
46 | * local ok, err = kong.service.request.set_body({
47 | * name = "John Doe",
48 | * age = 42,
49 | * numbers = {1, 2, 3}
50 | * })
51 | * -- Produces the following JSON body:
52 | * -- { "name": "John Doe", "age": 42, "numbers":[1, 2, 3] }
53 | * local ok, err = kong.service.request.set_body({
54 | * foo = "hello world",
55 | * bar = {"baz", "bla", true},
56 | * zzz = true,
57 | * blo = ""
58 | * }, "application/x-www-form-urlencoded")
59 | * -- Produces the following body:
60 | * -- bar=baz&bar=bla&bar&blo=&foo=hello%20world&zzz
61 | * @param args A table with data to be converted to the appropriate format
62 | and stored in the body.
63 | * @param mimetype? can be one of:
64 | * @returns `true` on success, `nil` otherwise.
65 | * @returns `nil` on success, an error message in case of error.
66 | Throws an error on invalid inputs.
67 | */
68 | setBody(args: Array | object, mimetype?: string): Promise<[ret_1: boolean, ret_2: string]>;
69 |
70 | /**
71 | * kong.service.request.set_header("X-Foo", "value")
72 | * @param header The header name. Example: "X-Foo".
73 | * @param of strings|string|boolean|number value The header value. Example: "hello world".
74 | * @returns throws an error on invalid inputs.
75 | */
76 | setHeader(header: string, of: array): Promise;
77 |
78 | /**
79 | * kong.service.request.set_header("X-Foo", "foo1")
80 | * kong.service.request.add_header("X-Foo", "foo2")
81 | * kong.service.request.set_header("X-Bar", "bar1")
82 | * kong.service.request.set_headers({
83 | * ["X-Foo"] = "foo3",
84 | * ["Cache-Control"] = { "no-store", "no-cache" },
85 | * ["Bla"] = "boo"
86 | * })
87 | * -- Will add the following headers to the request, in this order:
88 | * -- X-Bar: bar1
89 | * -- Bla: boo
90 | * -- Cache-Control: no-store
91 | * -- Cache-Control: no-cache
92 | * -- X-Foo: foo3
93 | * @param headers A table where each key is a string containing a header name
94 | and each value is either a string or an array of strings.
95 | * @returns throws an error on invalid inputs.
96 | */
97 | setHeaders(headers: Array | object): Promise;
98 |
99 | /**
100 | * kong.service.request.set_method("DELETE")
101 | * @param method The method string, which must be in all
102 | uppercase. Supported values are: `"GET"`, `"HEAD"`, `"PUT"`, `"POST"`,
103 | `"DELETE"`, `"OPTIONS"`, `"MKCOL"`, `"COPY"`, `"MOVE"`, `"PROPFIND"`,
104 | `"PROPPATCH"`, `"LOCK"`, `"UNLOCK"`, `"PATCH"`, or `"TRACE"`.
105 | * @returns throws an error on invalid inputs.
106 | */
107 | setMethod(method: string): Promise;
108 |
109 | /**
110 | * kong.service.request.set_path("/v2/movies")
111 | * @param path The path string. Special characters and UTF-8
112 | characters are allowed, for example: `"/v2/movies"` or `"/foo/😀"`.
113 | * @returns throws an error on invalid inputs.
114 | */
115 | setPath(path: string): Promise;
116 |
117 | /**
118 | * kong.service.request.set_query({
119 | * foo = "hello world",
120 | * bar = {"baz", "bla", true},
121 | * zzz = true,
122 | * blo = ""
123 | * })
124 | * -- Produces the following query string:
125 | * -- bar=baz&bar=bla&bar&blo=&foo=hello%20world&zzz
126 | * @param args A table where each key is a string (corresponding to an
127 | argument name), and each value is either a boolean, a string, or an array of
128 | strings or booleans. Any string values given are URL-encoded.
129 | * @returns throws an error on invalid inputs.
130 | */
131 | setQuery(args: Array | object): Promise;
132 |
133 | /**
134 | * kong.service.request.set_raw_body("Hello, world!")
135 | * @param body The raw body.
136 | * @returns throws an error on invalid inputs.
137 | */
138 | setRawBody(body: string): Promise;
139 |
140 | /**
141 | * kong.service.request.set_raw_query("zzz&bar=baz&bar=bla&bar&blo=&foo=hello%20world")
142 | * @param query The raw querystring. Example:
143 | `"foo=bar&bla&baz=hello%20world"`.
144 | * @returns throws an error on invalid inputs.
145 | */
146 | setRawQuery(query: string): Promise;
147 |
148 | /**
149 | * kong.service.request.set_scheme("https")
150 | * @param scheme The scheme to be used. Supported values are `"http"` or `"https"`.
151 | * @returns throws an error on invalid inputs.
152 | */
153 | setScheme(scheme: string): Promise;
154 |
155 | }
156 |
--------------------------------------------------------------------------------
/kong/service/response.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/service/response.lua
3 |
4 |
5 | export default interface response {
6 |
7 |
8 | /**
9 | * -- Plugin needs to call kong.service.request.enable_buffering() on `rewrite`
10 | * -- or `access` phase prior calling this function.
11 | * local body = kong.service.response.get_body()
12 | * @param mimetype? The MIME type of the response (if known).
13 | * @param max_args? Sets a limit on the maximum number of (what?)
14 | that can be parsed.
15 | * @returns The decoded buffered body
16 | */
17 | getBody(mimetype?: string, max_args?: number): Promise;
18 |
19 | /**
20 | * -- Given a response with the following headers:
21 | * -- X-Custom-Header: bla
22 | * -- X-Another: foo bar
23 | * -- X-Another: baz
24 | * kong.log.inspect(kong.service.response.get_header("x-custom-header")) -- "bla"
25 | * kong.log.inspect(kong.service.response.get_header("X-Another")) -- "foo bar"
26 | * @param name The name of the header.
27 | Header names in are case-insensitive and are normalized to lowercase, and
28 | dashes (`-`) can be written as underscores (`_`); that is, the header
29 | `X-Custom-Header` can also be retrieved as `x_custom_header`.
30 | * @returns The value of the header, or `nil` if a header with
31 | `name` is not found in the response. If a header with the same name is present
32 | multiple times in the response, this function returns the value of the
33 | first occurrence of this header.
34 | */
35 | getHeader(name: string): Promise;
36 |
37 | /**
38 | * -- Given a response with the following headers:
39 | * -- X-Custom-Header: bla
40 | * -- X-Another: foo bar
41 | * -- X-Another: baz
42 | * local headers = kong.service.response.get_headers()
43 | * if headers then
44 | * kong.log.inspect(headers.x_custom_header) -- "bla"
45 | * kong.log.inspect(headers.x_another[1]) -- "foo bar"
46 | * kong.log.inspect(headers["X-Another"][2]) -- "baz"
47 | * end
48 | * Note that this function returns a proxy table
49 | * which cannot be iterated with `pairs` or used as operand of `#`.
50 | * @param max_headers? Sets a limit on the maximum number of
51 | headers that can be parsed.
52 | * @returns The response headers in table form.
53 | * @returns If more headers than `max_headers` are present, returns
54 | a string with the error `"truncated"`.
55 | */
56 | getHeaders(max_headers?: number): Promise<[ret_1: Array | object, ret_2: string]>;
57 |
58 | /**
59 | * -- Plugin needs to call kong.service.request.enable_buffering() on `rewrite`
60 | * -- or `access` phase prior calling this function.
61 | * local body = kong.service.response.get_raw_body()
62 | * @returns The raw buffered body.
63 | */
64 | getRawBody(): Promise;
65 |
66 | /**
67 | * kong.log.inspect(kong.service.response.get_status()) -- 418
68 | * @returns The status code from the response from the Service, or `nil`
69 | if the request was not proxied (that is, if `kong.response.get_source()` returned
70 | anything other than `"service"`).
71 | */
72 | getStatus(): Promise;
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/kong/telemetry.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/telemetry.lua
3 |
4 |
5 | export default interface telemetry {
6 |
7 |
8 | /**
9 | * local attributes = {
10 | * http_method = kong.request.get_method()
11 | * ["node.id"] = kong.node.get_id(),
12 | * hostname = kong.node.get_hostname(),
13 | * }
14 | * local ok, err = kong.telemetry.log("my_plugin", conf, "result", "successful operation", attributes)
15 | * @param plugin_name the name of the plugin
16 | * @param plugin_config the plugin configuration
17 | * @param message_type the type of the log message, useful to categorize
18 | the log entry
19 | * @param message the log message
20 | * @param attributes structured information to be included in the
21 | `attributes` field of the log entry
22 | */
23 | log(plugin_name: string, plugin_config: Array | object, message_type: string, message: string, attributes: Array | object): Promise;
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/kong/vault.d.ts:
--------------------------------------------------------------------------------
1 | // AUTO GENERATED BASED ON Kong 3.8.x, DO NOT EDIT
2 | // Original source path: kong/pdk/vault.lua
3 |
4 |
5 | export default interface vault {
6 |
7 |
8 | /**
9 | * kong.vault.flush()
10 | */
11 | flush(): Promise;
12 |
13 | /**
14 | * local value, err = kong.vault.get("{vault://env/cert/key}")
15 | * @param reference reference to resolve
16 | * @returns resolved value of the reference
17 | * @returns error message on failure, otherwise `nil`
18 | */
19 | get(reference: string): Promise<[ret_1: string, ret_2: string]>;
20 |
21 | /**
22 | *
23 | */
24 | initWorker(): Promise;
25 |
26 | /**
27 | * kong.vault.is_reference("{vault://env/key}") -- true
28 | * kong.vault.is_reference("not a reference") -- false
29 | * @param reference reference to check
30 | * @returns `true` is the passed in reference looks like a reference, otherwise `false`
31 | */
32 | isReference(reference: string): Promise;
33 |
34 | /**
35 | * local ref, err = kong.vault.parse_reference("{vault://env/cert/key?prefix=SSL_#1}") -- table
36 | * @param reference reference to parse
37 | * @returns a table containing each component of the reference, or `nil` on error
38 | * @returns error message on failure, otherwise `nil`
39 | */
40 | parseReference(reference: string): Promise<[ret_1: Array | object, ret_2: string]>;
41 |
42 | /**
43 | * local options = kong.vault.update({
44 | * cert = "-----BEGIN CERTIFICATE-----...",
45 | * key = "-----BEGIN RSA PRIVATE KEY-----...",
46 | * cert_alt = "-----BEGIN CERTIFICATE-----...",
47 | * key_alt = "-----BEGIN EC PRIVATE KEY-----...",
48 | * ["$refs"] = {
49 | * cert = "{vault://aws/cert}",
50 | * key = "{vault://aws/key}",
51 | * cert_alt = "{vault://aws/cert-alt}",
52 | * key_alt = "{vault://aws/key-alt}",
53 | * }
54 | * })
55 | * -- or
56 | * local options = {
57 | * cert = "-----BEGIN CERTIFICATE-----...",
58 | * key = "-----BEGIN RSA PRIVATE KEY-----...",
59 | * cert_alt = "-----BEGIN CERTIFICATE-----...",
60 | * key_alt = "-----BEGIN EC PRIVATE KEY-----...",
61 | * ["$refs"] = {
62 | * cert = "{vault://aws/cert}",
63 | * key = "{vault://aws/key}",
64 | * cert_alt = "{vault://aws/cert-alt}",
65 | * key_alt = "{vault://aws/key-alt}",
66 | * }
67 | * }
68 | * kong.vault.update(options)
69 | * @param options options containing secrets and references (this function modifies the input options)
70 | * @returns options with updated secret values
71 | */
72 | update(options: Array | object): Promise | object>;
73 |
74 | /**
75 | *
76 | */
77 | warmup(): Promise;
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/lib/mod.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('ts-node').register();
4 | const fs = require('fs')
5 |
6 | const phases = ['certificate', 'rewrite', 'log', 'access', 'preread', 'response']
7 | const noop = () => {}
8 | class Module {
9 | constructor(name, path, literal) {
10 | this.loadTime = Date.now() / 1000
11 | this.mod = null
12 |
13 | if (path) {
14 | this.mod = require(path)
15 | this.mtime = fs.statSync(path).mtime.getTime() / 1000
16 | this.location = path
17 | } else if (literal) {
18 | this.mod = literal
19 | // use mtime of current script instead
20 | this.mtime = fs.statSync(__filename).mtime.getTime() / 1000
21 | } else {
22 | throw new Error('either path or module needs to be passed in')
23 | }
24 |
25 | const plugin_name = (this.mod.Name || name).toLowerCase()
26 | this.path = path
27 | this.cls = this.mod.Plugin
28 | this.name = plugin_name
29 | this.phases = []
30 |
31 | for (const phase of phases) {
32 | if (typeof this.cls.prototype[phase] == 'function') {
33 | this.phases.push(phase)
34 | }
35 | }
36 |
37 | this.priority = this.mod.Priority || 0
38 | this.version = this.mod.Version
39 | this.schema = this.mod.Schema || []
40 |
41 | this.lastStartInstanceTime = 0
42 | this.lastCloseInstanceTime = 0
43 | }
44 |
45 | new(config) {
46 | this.lastStartInstanceTime = Date.now() / 1000
47 | return new Instance(this.name, config, this.cls, this.setLastCloseInstanceTime.bind(this))
48 | }
49 |
50 | setLastCloseInstanceTime() {
51 | this.lastCloseInstanceTime = Date.now() / 1000
52 | }
53 |
54 | getLastCloseInstanceTime() {
55 | return this.lastCloseInstanceTime
56 | }
57 |
58 | getLastStartInstanceTime() {
59 | return this.lastStartInstanceTime
60 | }
61 |
62 | getMTime() {
63 | return this.mtime
64 | }
65 |
66 | getName() {
67 | return this.name
68 | }
69 |
70 | getLoadTime() {
71 | return this.loadTime
72 | }
73 |
74 | getPhases() {
75 | return this.phases
76 | }
77 |
78 | getPriority() {
79 | return this.priority
80 | }
81 |
82 | getVersion() {
83 | return this.version
84 | }
85 |
86 | getSchema() {
87 | return this.schema
88 | }
89 | }
90 |
91 | class Instance {
92 | constructor(name, config, cls, closeCb = noop) {
93 | this.cls = new cls(config)
94 | this.name = name
95 | this.config = config
96 | this.startTime = Date.now() / 1000
97 | this.lastUsedTime = 0
98 | this.closeCb = closeCb
99 | }
100 |
101 | isExpired(ttl = 60) {
102 | const until = Date.now() / 1000 - ttl
103 | return this.startTime < until && this.lastUsedTime < until
104 | }
105 |
106 | resetExpireTs() {
107 | this.lastUsedTime = Date.now() / 1000
108 | return this.cls
109 | }
110 |
111 | close() {
112 | this.closeCb()
113 | }
114 |
115 | executePhase(phase, ctx) {
116 | return this.cls[phase](ctx)
117 | }
118 |
119 | getName() {
120 | return this.name
121 | }
122 |
123 | getConfig() {
124 | return this.config
125 | }
126 |
127 | getStartTime() {
128 | return this.startTime
129 | }
130 |
131 | getLastUsedTime() {
132 | return this.lastUsedTime
133 | }
134 | }
135 |
136 | module.exports = {
137 | Module: Module,
138 | Instance: Instance,
139 | }
140 |
--------------------------------------------------------------------------------
/lib/pipe.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // https://stackoverflow.com/questions/47157428/how-to-implement-a-pseudo-blocking-async-queue-in-js-ts
4 | class AsyncBlockingQueue {
5 | constructor() {
6 | // invariant: at least one of the arrays is empty
7 | this.resolvers = [];
8 | this.promises = [];
9 | }
10 |
11 | _add() {
12 | this.promises.push(new Promise(resolve => {
13 | this.resolvers.push(resolve);
14 | }))
15 | }
16 |
17 | enqueue(t) {
18 | if (!this.resolvers.length) this._add();
19 | this.resolvers.shift()(t);
20 | }
21 |
22 | dequeue() {
23 | if (!this.promises.length) this._add();
24 | return this.promises.shift();
25 | }
26 |
27 | // now some utilities:
28 | isEmpty() { // there are no values available
29 | return !this.promises.length; // this.length <= 0
30 | }
31 |
32 | isBlocked() { // it's waiting for values
33 | return !!this.resolvers.length; // this.length < 0
34 | }
35 |
36 | get length() {
37 | return this.promises.length - this.resolvers.length;
38 | }
39 |
40 | [Symbol.asyncIterator]() {
41 | return {
42 | next: () => this.dequeue().then(value => ({
43 | done: false,
44 | value
45 | }))
46 | };
47 | }
48 | }
49 |
50 | class Pipe {
51 | constructor (get, put) {
52 | this.get = get
53 | this.put = put
54 | }
55 | }
56 |
57 | class PipePair {
58 | constructor() {
59 | this.qa = new AsyncBlockingQueue()
60 | this.qb = new AsyncBlockingQueue()
61 |
62 | this.a = new Pipe(
63 | () => { return this.qa.dequeue() },
64 | (v) => { return this.qb.enqueue(v) },
65 | )
66 |
67 | this.b = new Pipe(
68 | () => { return this.qb.dequeue() },
69 | (v) => { return this.qa.enqueue(v) },
70 | )
71 | }
72 |
73 | getPair() {
74 | return [this.a, this.b]
75 | }
76 | }
77 |
78 | module.exports = PipePair
79 |
--------------------------------------------------------------------------------
/listener.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const net = require('net')
4 | const fs = require('fs')
5 | const {Encoder, Decoder} = require('@msgpack/msgpack')
6 | const TYPE_EXP = /^\[object (.*)\]$/
7 | const ERR_UNKNOWN = 'Unknown plugin listener error encountered'
8 | const toString = Object.prototype.toString
9 |
10 | function typeOf(value) {
11 | if (!value) return ''
12 | const parts = TYPE_EXP.exec(toString.call(value))
13 | return parts[1].toLowerCase()
14 | }
15 |
16 | function thenable(obj) {
17 | if (!obj) return false
18 | return (typeof obj.then === 'function' && typeof obj.catch === 'function')
19 | }
20 |
21 | function write_response (client, msgid, response) {
22 | client.write(client.encoder.encode([
23 | 1, // is response
24 | msgid,
25 | undefined,
26 | response
27 | ]))
28 | }
29 |
30 | function write_error (client, msgid, error) {
31 | client.write(client.encoder.encode([
32 | 1, // is response
33 | msgid,
34 | errToString(error),
35 | undefined
36 | ]))
37 | }
38 |
39 | function errToString (err) {
40 | if (typeof err === 'string') return err
41 | if ('message' in err) return err.message
42 | if (typeof err.toString === 'function') return err.toString()
43 | return ERR_UNKNOWN
44 | }
45 |
46 | function getStreamDecoder () {
47 | const decoder = new Decoder()
48 | let buffer
49 |
50 | return function (chunk) {
51 | let decoded
52 | try {
53 | let data = chunk
54 | if (buffer !== undefined) {
55 | buffer.push(chunk)
56 | data = Buffer.concat(buffer)
57 | }
58 | decoded = decoder.decode(data)
59 | buffer = undefined
60 | } catch (ex) {
61 | // TODO: less hacky way to detect insufficient data
62 | if (ex.message === 'Insufficient data') {
63 | if (buffer === undefined) {
64 | buffer = [chunk]
65 | }
66 | return
67 | }
68 |
69 | throw ex
70 | }
71 |
72 | return decoded
73 | }
74 | }
75 |
76 | class Listener {
77 | get [Symbol.toStringTag]() {
78 | return 'PluginListener'
79 | }
80 |
81 | constructor(pluginServer, prefix) {
82 | this.ps = pluginServer
83 | this.prefix = prefix
84 | this.logger = pluginServer.getLogger()
85 | }
86 |
87 | serve() {
88 | const listen_path = this.prefix
89 | const logger = this.logger
90 |
91 | try {
92 | fs.unlinkSync(listen_path)
93 | } catch (ex) {
94 | if (ex.code !== 'ENOENT') throw ex
95 | }
96 |
97 | const server = net.createServer((client) => {
98 | client.encoder = new Encoder()
99 | const decodeStream = getStreamDecoder()
100 |
101 | client.on('data', (chunk) => {
102 | let decoded = decodeStream(chunk)
103 |
104 | // partial data received, wait for next chunk
105 | if (!decoded) return
106 |
107 | let [_, msgid, method, args] = decoded
108 | let [ns, cmd] = method.split('.')
109 | if (ns !== 'plugin') {
110 | write_error(client, msgid, `RPC for ${ns} is not supported`)
111 | return
112 | }
113 |
114 | logger.debug(`rpc: #${msgid} method: ${method} args: ${JSON.stringify(args)}`)
115 | if (!this.ps[cmd]) {
116 | const err = `method ${cmd} not implemented`
117 | logger.error(`rpc: #${msgid} ${err}`)
118 | write_error(client, msgid, err)
119 | return
120 | }
121 |
122 | let promise
123 | try {
124 | promise = this.ps[cmd](...args)
125 | } catch (ex) {
126 | logger.error(ex.stack)
127 | write_error(client, msgid, ex)
128 | return
129 | }
130 |
131 | if (!thenable(promise)) {
132 | const err = `${cmd} should return a Promise or thenable object, got ${typeOf(promise)}`
133 | logger.error(`rpc: #${msgid} ${err}`)
134 | write_error(client, msgid, err)
135 | return
136 | }
137 |
138 | promise
139 | .then((ret) => {
140 | write_response(client, msgid, ret)
141 | })
142 | .catch((err) => {
143 | logger.error(`rpc: # ${msgid} ${err}`)
144 | write_error(client, msgid, err)
145 | })
146 | })
147 | })
148 |
149 | server.listen(listen_path)
150 | logger.info('server started at', listen_path)
151 | return server
152 | }
153 | }
154 |
155 | module.exports = Listener
156 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kong-pdk",
3 | "version": "0.6.0",
4 | "description": "Kong PDK for Javascript and Plugin Server",
5 | "main": "app.js",
6 | "scripts": {
7 | "start": "node ./bin/kong-js-pluginserver -d $(pwd)/examples",
8 | "test": "jest --coverage"
9 | },
10 | "files": [
11 | "bin",
12 | "kong",
13 | "lib",
14 | "cli.js",
15 | "listener.js",
16 | "pdk.js",
17 | "server.js",
18 | "plugin_test.js"
19 | ],
20 | "author": "Kong",
21 | "license": "Apache-2.0",
22 | "devDependencies": {
23 | "@babel/core": "^7.14.6",
24 | "@babel/preset-env": "^7.14.7",
25 | "jest": "^28.1.0",
26 | "node-fetch": "^2.6.1",
27 | "uuid": "^9.0.0"
28 | },
29 | "dependencies": {
30 | "@msgpack/msgpack": "^2.7.0",
31 | "@types/node": "^18.11.9",
32 | "commander": "^10.0.0",
33 | "node-color-log": "^10.0.2",
34 | "ts-node": "^10.1.0",
35 | "typescript": "^4.2.4"
36 | },
37 | "directories": {
38 | "example": "examples",
39 | "lib": "lib",
40 | "bin": "bin"
41 | },
42 | "repository": {
43 | "type": "git",
44 | "url": "git+https://github.com/Kong/kong-js-pdk.git"
45 | },
46 | "jest": {
47 | "coveragePathIgnorePatterns": ["/node_modules/", "plugin_test"]
48 | },
49 | "bugs": {
50 | "url": "https://github.com/Kong/kong-js-pdk/issues"
51 | },
52 | "bin": {
53 | "kong-js-pluginserver": "./bin/kong-js-pluginserver"
54 | },
55 | "homepage": "https://github.com/Kong/kong-js-pdk#readme"
56 | }
57 |
--------------------------------------------------------------------------------
/pdk.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const ERROR_NAME = 'PDKError'
4 |
5 | class PDKError extends Error {
6 | get name () {
7 | return ERROR_NAME
8 | }
9 |
10 | constructor(...args) {
11 | super(...args)
12 | Error.captureStackTrace(this, this.constructor)
13 | }
14 | }
15 |
16 | const bridgeHandler = {
17 | get(target, name) {
18 | // camelCase to underscore_case
19 | const clean_name = name.replace(/[a-z][A-Z]/g, (str) => {
20 | return (str.substring(0, 1) + "_" + str.substring(1)).toLowerCase()
21 | })
22 | return newBridgeCall(`${target.prefix}.${clean_name}`, target.call)
23 | }
24 | }
25 |
26 | function newBridgeCall(prefix, call) {
27 | function bridgeCall(...args) {
28 | return call(prefix, ...args)
29 | }
30 | bridgeCall.prefix = prefix
31 | bridgeCall.call = call
32 |
33 | return new Proxy(bridgeCall, bridgeHandler);
34 | }
35 |
36 | // those methods never return, instead, they exit from current request immediately
37 | const NON_RETURN_METHODS = new Set([
38 | "kong.response.exit",
39 | "kong.response.error",
40 | ])
41 |
42 | function rpcCall(rpcPipe) {
43 | return async (method, ...args) => {
44 | rpcPipe.put({
45 | "Method": method,
46 | "Args": args,
47 | })
48 |
49 | if (NON_RETURN_METHODS.has(method))
50 | return
51 |
52 | const [ret, err] = await rpcPipe.get()
53 |
54 | if (err) {
55 | throw new PDKError(`PDK method ${method} failed: ${err}`)
56 | }
57 | return ret
58 | }
59 | }
60 |
61 | class Kong {
62 | get [Symbol.toStringTag]() {
63 | return 'KongPDK'
64 | }
65 |
66 | get Error() {
67 | return PDKError
68 | }
69 |
70 | static get Error() {
71 | return PDKError
72 | }
73 |
74 | constructor(rpcPipe) {
75 | this.kong = newBridgeCall("kong", rpcCall(rpcPipe))
76 | }
77 | }
78 |
79 | module.exports = Kong
80 |
--------------------------------------------------------------------------------
/plugin_test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path')
4 | const { v4: uuidv4 } = require('uuid')
5 | const fetch = require("node-fetch")
6 | const PDK = require(path.join(__dirname, 'pdk'))
7 | const PipePair = require(path.join(__dirname, 'lib', 'pipe'))
8 | const {
9 | Module
10 | } = require(path.join(__dirname, 'lib', 'mod'))
11 |
12 | const MSG_RET = 'ret'
13 |
14 | let noop = function() {
15 | return true
16 | }
17 |
18 | let mockFunctions = {
19 | "kong.client.get_ip": function() { return "1.2.3.4" },
20 | "kong.client.get_forwarded_ip": function() { return "1.2.3.4" },
21 | "kong.client.get_port": function() { return 443 },
22 | "kong.client.get_forwarded_port": function() { return 443 },
23 | "kong.client.get_credential": function() { return { id: uuidv4(), consumer_id: "123456" } },
24 | "kong.client.load_consumer": function() { return { id: uuidv4(), username: "Jon Doe" } },
25 | "kong.client.authenticate": noop,
26 | "kong.client.get_protocol": function() { "https" },
27 |
28 | "kong.ip.is_trusted": noop,
29 |
30 | "kong.node.get_id": function() { return "a9777ac2-57e6-482b-a3c4-ef3d6ca41a1f" },
31 | "kong.node.get_memory_stats": function() {
32 | return {
33 | lua_shared_dicts: {
34 | kong: {
35 | allocated_slabs: 12288,
36 | capacity: 24576
37 | },
38 | kong_db_cache: {
39 | allocated_slabs: 12288,
40 | capacity: 12288
41 | }
42 | },
43 | workers_lua_vms: [
44 | {
45 | http_allocated_gc: 1102,
46 | pid: 18004
47 | },
48 | {
49 | http_allocated_gc: 1102,
50 | pid: 18005
51 | }
52 | ],
53 | }
54 | },
55 |
56 | "kong.request.get_scheme": function(i) { return i.request.url.protocol.replace(":", "") },
57 | "kong.request.get_host": function(i) { return i.request.url.hostname },
58 | "kong.request.get_port": function(i) { return i.request.url.port },
59 | "kong.request.get_forwarded_scheme": function(i) { return i.request.headers.get("X-Forwarded-Proto") },
60 | "kong.request.get_forwarded_host": function(i) { return i.request.headers.get("X-Forwarded-Host") },
61 | "kong.request.get_forwarded_port": function(i) { return i.request.headers.get("X-Forwarded-Port") },
62 | "kong.request.get_http_version": function() { return "1.1" },
63 | "kong.request.get_method": function(i) { return i.request.method },
64 | "kong.request.get_path": function(i) { return i.request.url.pathname },
65 | "kong.request.get_path_with_query": function(i) { return i.request.url.pathname + i.request.url.search },
66 | "kong.request.get_raw_query": function(i) { return i.request.url.search },
67 | "kong.request.get_query_arg": function(i, k) { return new URLSearchParams(i.request.url.search).get(k) },
68 | "kong.request.get_query": function(i) { return Array.from(new URLSearchParams(i.request.url.search).entries()) },
69 | "kong.request.get_header": function(i, k) { return i.request.headers.get(k) },
70 | "kong.request.get_headers": function(i) { return Array.from(i.request.headers.entries()) },
71 | "kong.request.get_raw_body": function(i) { return i.request.body },
72 |
73 | "kong.response.get_status": function(i) { return i.response.status },
74 | "kong.response.get_header": function(i, k) { return i.response.headers.get(k) },
75 | "kong.response.get_headers": function(i) { return Array.from(i.response.headers.entries()) },
76 | "kong.response.get_source": function() { return "service" },
77 | "kong.response.set_status": function(i, status) { i.response.status = status },
78 | "kong.response.set_header": function(i, k, v) { i.response.headers.set(k, v) },
79 | "kong.response.add_header": function(i, k, v) { i.response.headers.add(k, v) },
80 | "kong.response.set_headers": function(i, headers) {
81 | for(let [k, v] in Object.entries(headers)) {
82 | i.response.headers.set(k, v)
83 | }
84 | },
85 | "kong.response.clear_header": function(i, k) { i.response.headers.delete(k) },
86 | "kong.response.exit": function(i, status, body, headers) {
87 | i.response.status = status
88 | i.response.body = body
89 | if (headers !== undefined) {
90 | for(let [k, v] in Object.entries(headers)) {
91 | i.response.headers.set(k, v)
92 | }
93 | }
94 | i.setExiting(true)
95 | },
96 | "kong.response.error": function(i, status, message, headers) {
97 | mockFunctions["kong.response.exit"](i, status, message, headers)
98 | },
99 |
100 | "kong.router.get_route": function() { return {
101 | id: uuidv4(),
102 | name: "route_66",
103 | protocols: ["http", "tcp"],
104 | paths: ["/v0/left", "/v1/this"],
105 | }},
106 | "kong.router.get_service": function() { return {
107 | id: uuidv4(),
108 | name: "self_service",
109 | protocol: "http",
110 | path: "/v0/left",
111 | }},
112 |
113 | "kong.service.set_upstream": noop,
114 | "kong.service.set_target": noop,
115 | "kong.service.request.set_scheme": function(i, protocol) { i.serviceRequest.url.protocol = protocol },
116 | "kong.service.request.set_path": function(i, path) { i.serviceRequest.url.pathname = path },
117 | "kong.service.request.set_raw_query": function(i, query) { i.serviceRequest.url.search = query },
118 | "kong.service.request.set_method": function(i, method) { i.serviceRequest.method = method },
119 | "kong.service.request.set_query": function(i, args) {
120 | let params = new URLSearchParams(i.serviceRequest.url.search)
121 | for (let [k, v] of Object.entries(args)) {
122 | params.set(k, v)
123 | }
124 | i.serviceRequest.url.search = params.toString()
125 | },
126 | "kong.service.request.set_header": function(i, k, v) { i.serviceRequest.headers.set(k, v) },
127 | "kong.service.request.add_header": function(i, k, v) { i.serviceRequest.headers.add(k, v) },
128 | "kong.service.request.set_headers": function(i, headers) {
129 | for (let [k, v] of Object.entries(headers)) {
130 | i.serviceRequest.headers.set(k, v)
131 | }
132 | },
133 | "kong.service.request.clear_header": function(i, k) { i.serviceRequest.headers.delete(k) },
134 | "kong.service.request.set_raw_body": function(i, body) { i.serviceRequest.body = body },
135 |
136 | "kong.service.response.get_status": function(i) { return i.serviceResponse.status },
137 | "kong.service.response.get_headers": function(i) { return i.serviceResponse.headers },
138 | "kong.service.response.get_header": function(i, k) { return i.serviceResponse.headers.get(k) },
139 | "kong.service.response.get_raw_body": function(i) { return i.serviceResponse.body },
140 | }
141 |
142 | let logFunctions = [
143 | "kong.log.alert", "kong.log.crit", "kong.log.err", "kong.log.warn",
144 | "kong.log.notice", "kong.log.info", "kong.log.debug"
145 | ]
146 |
147 | for (let i = 0; i < logFunctions.length; i++) {
148 | const logFunction = logFunctions[i]
149 | mockFunctions[logFunction] = function(...args) {
150 | console.log("Log " + logFunction, ...args)
151 | }
152 | }
153 |
154 | class PluginTest {
155 | constructor(request) {
156 | this.request = request
157 | this.response = new Response()
158 | this.serviceRequest = new Request(request)
159 | let [ch, childCh] = new PipePair().getPair()
160 | this.ch = ch
161 | this.childCh = childCh
162 | this.exiting = false
163 | }
164 |
165 | async Run(pluginToTest, pluginConfig) {
166 | let pluginModule = new Module("TestPlugin", undefined, pluginToTest)
167 | let pluginInstance = pluginModule.new(pluginConfig)
168 |
169 | if(this.request.isHttps || this.request.isTLS){
170 | await this.executePhase(pluginInstance, "certificate")
171 | }
172 |
173 | if(this.request.isTCP || this.request.isTLS) {
174 | await this.executePhase(pluginInstance, "preread")
175 | } else{
176 | await this.executePhase(pluginInstance, "access")
177 | await this.executePhase(pluginInstance, "rewrite")
178 | if (!this.exiting) {
179 | this.serviceResponse = this.serviceRequest.toResponse()
180 | this.response.merge(this.serviceResponse)
181 | }
182 | await this.executePhase(pluginInstance, "response")
183 | }
184 |
185 | await this.executePhase(pluginInstance, "log")
186 | return {mod: pluginModule, instance: pluginInstance}
187 | }
188 |
189 | async executePhase(ins, phase) {
190 | // skip phases to mock Kong "early exit"
191 | if (this.exiting && phase != "log") return
192 | // start the consumer to mock RPC functions
193 | this.mockKongPDK()
194 | // if the plugin doesn't implement this phase, ignore
195 | if (ins.cls[phase] === undefined) {
196 | return
197 | }
198 |
199 | await ins.executePhase(phase, new PDK(this.childCh).kong)
200 | this.childCh.put(MSG_RET)
201 | }
202 |
203 | mockKongPDK() {
204 | setImmediate(() => {
205 | new Promise(async () => {
206 | while(1) {
207 | let r = await this.ch.get()
208 |
209 | if (r == MSG_RET) return
210 |
211 | let meth = r.Method
212 | if (mockFunctions[meth] === undefined) {
213 | throw new Error("function " + meth + " is not a valid PDK function")
214 | }
215 |
216 | let ret = mockFunctions[meth](this, ...r.Args)
217 | this.ch.put([ret, undefined])
218 | }
219 | })
220 | })
221 | }
222 |
223 | setExiting(exiting) {
224 | this.exiting = exiting
225 | }
226 | }
227 |
228 | class Request {
229 | url = new URL("http://konghq.com");
230 | headers = new fetch.Headers();
231 | method = "GET";
232 | isHttps = false;
233 | isTCP = false;
234 | isTLS = false;
235 | body = "";
236 |
237 | constructor(request) {
238 | if (request !== undefined) {
239 | this.useURL(request.url.toString())
240 | this.useHeaders(Array.from(request.headers.entries()))
241 | this.useMethod(request.method)
242 | this.useBody(request.body)
243 | }
244 | }
245 |
246 | useURL(url) {
247 | this.url = new URL(url)
248 | this.isHttps = this.url.protocol == "https:"
249 | this.isTCP = this.url.protocol == "tcp:"
250 | this.isTLS = this.url.protocol == "tls:"
251 | return this
252 | }
253 |
254 | useHeaders(headers) {
255 | for(let [k, v] of Object.entries(headers)) {
256 | this.headers.set(k, v)
257 | }
258 | return this
259 | }
260 |
261 | useMethod(method) {
262 | this.method = method
263 | return this
264 | }
265 |
266 | useBody(body) {
267 | this.body = body
268 | return this
269 | }
270 |
271 | toResponse() {
272 | let response = new Response(this.url.toString())
273 | response.status = 200
274 | response.body = "OK"
275 | response.headers = new fetch.Headers(this.headers)
276 | return response
277 | }
278 | }
279 |
280 | class Response {
281 | status;
282 | body = "";
283 | headers = new fetch.Headers();
284 |
285 | constructor(response) {
286 | if (response !== undefined) {
287 | this.status = response.status
288 | this.headers = new fetch.Headers(response.headers)
289 | this.body = response.body
290 | }
291 | }
292 |
293 | merge(response) {
294 | for (let [k, v] in response.headers.entries()) {
295 | this.headers.append(k, v)
296 | }
297 | this.body = response.body
298 | this.status = response.status
299 | }
300 | }
301 |
302 | module.exports = {
303 | PluginTest: PluginTest,
304 | Request: Request,
305 | Response: Response,
306 | }
307 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs')
4 | const path = require('path')
5 | const {Module} = require('./lib/mod')
6 | const PipePair = require('./lib/pipe')
7 | const PDK = require('./pdk')
8 |
9 | const entities = ['Service', 'Consumer', 'Route', 'Plugin', 'Credential', 'MemoryStats']
10 | const MSG_RET = 'ret'
11 | const ERROR_NAME = 'PluginServerError'
12 | const VALID_EXTENSIONS = new Set([
13 | '.js',
14 | '.ts',
15 | '.node',
16 | '.cjs',
17 | ''
18 | ])
19 |
20 | class PluginServerError extends Error {
21 | get name () {
22 | return ERROR_NAME
23 | }
24 |
25 | constructor(...args) {
26 | super(...args)
27 | Error.captureStackTrace(this, this.constructor)
28 | }
29 | }
30 |
31 | class Server {
32 | get Error() {
33 | return PluginServerError
34 | }
35 |
36 | static get Error() {
37 | return PluginServerError
38 | }
39 |
40 | constructor(pluginDir, logger, expireTtl) {
41 | this.pluginDir = pluginDir
42 | this.logger = logger
43 | this.plugins = new Map()
44 | this.instances = new Map()
45 | this.instanceID = 0
46 | this.events = new Map()
47 | this.eventID = 0
48 |
49 | if (pluginDir) {
50 | this.loadPlugins()
51 | }
52 |
53 | this.clearExpiredPluginsTimer = this.clearExpiredPlugins(expireTtl || 60)
54 | }
55 |
56 | loadPlugins() {
57 | if (!this.pluginDir) {
58 | throw new PluginServerError('plugin server is not initialized, call SetPluginDir first')
59 | }
60 |
61 | const files = fs.readdirSync(this.pluginDir)
62 | for (const file of files) {
63 |
64 | if (file.startsWith('.')) continue
65 | if (/node_modules/.test(file)) continue
66 | const file_path = require.resolve(path.join(this.pluginDir, file))
67 | const {name, ext} = path.parse(file_path)
68 |
69 | if (!name) continue
70 | if (!VALID_EXTENSIONS.has(ext)) continue
71 |
72 | const plugin = this.plugins.get(name)
73 | if (plugin) {
74 | this.logger.warn(
75 | `plugin "${name}" is already loaded from ${plugin.path}, ` +
76 | `trying to load from ${file_path}`
77 | )
78 | continue
79 | }
80 |
81 | try {
82 | const mod = new Module(name, file_path)
83 | this.plugins.set(mod.name, mod)
84 | this.logger.debug(`loaded plugin "${mod.name}" from ${file_path}`)
85 | } catch (ex) {
86 | this.logger.warn(`error loading plugin "${name}" from ${file_path}: ${ex.stack}`)
87 | }
88 | };
89 | }
90 |
91 | clearExpiredPlugins(ttl) {
92 | return setInterval(() => {
93 | for (const [id, instance] of this.instances.entries()) {
94 | if (instance.isExpired()) {
95 | this.logger.debug(`cleanup instance #iid of ${instance.name}`)
96 | this.instances.delete(id)
97 | }
98 | }
99 | }, ttl)
100 | }
101 |
102 | close() {
103 | clearInterval(this.clearExpiredPluginsTimer)
104 | }
105 |
106 | async SetPluginDir(dir) {
107 | try {
108 | await fs.promises.stat(dir)
109 | } catch (err) {
110 | if (err.code !== 'ENOENT') throw err
111 | throw new PluginServerError(`${dir} does not exists`)
112 | }
113 |
114 | this.pluginDir = dir
115 | this.loadPlugins()
116 | return 'ok'
117 | }
118 |
119 | // RPC method
120 | async GetStatus() {
121 | const pluginStatus = Object.create(null)
122 | for (const [name, plugin] of this.plugins.entries()) {
123 | const instances = []
124 | for (const iid in this.instances) {
125 | instances.push(await this.InstanceStatus(iid))
126 | }
127 |
128 | pluginStatus[name] = {
129 | Name: name,
130 | Modtime: plugin.getMTime(),
131 | LoadTime: plugin.getLoadTime(),
132 | Instances: instances,
133 | LastStartInstance: plugin.getLastStartInstanceTime(),
134 | LastCloseInstance: plugin.getLastCloseInstanceTime(),
135 | }
136 | }
137 |
138 | return {
139 | Pid: process.pid,
140 | Plugins: pluginStatus
141 | }
142 | }
143 |
144 | // RPC method
145 | async GetPluginInfo(name) {
146 | const plugin = this.plugins.get(name)
147 | if (!name || !plugin) {
148 | throw new PluginServerError(`${name} not initizlied`)
149 | }
150 |
151 | return {
152 | Name: name,
153 | Version: plugin.getVersion(),
154 | Phases: plugin.getPhases(),
155 | Priority: plugin.getPriority(),
156 | Schema: {
157 | name: name,
158 | fields: [{
159 | config: {
160 | type: 'record',
161 | fields: plugin.getSchema(),
162 | }
163 | }],
164 | },
165 | }
166 | }
167 |
168 | // RPC method
169 | async StartInstance(cfg) {
170 | const name = cfg.Name
171 | const plugin = this.plugins.get(name)
172 | if (!plugin) {
173 | throw new PluginServerError(`${name} not initizlied`)
174 | }
175 |
176 | const config = JSON.parse(cfg.Config)
177 | const iid = this.instanceID++
178 | this.instances.set(iid, plugin.new(config))
179 |
180 | this.logger.info(`instance #${iid} of ${name} started`)
181 |
182 | return {
183 | Name: name,
184 | Id: iid,
185 | Config: config,
186 | StartTime: Date.now() / 1000,
187 | }
188 | }
189 |
190 | // RPC method
191 | async InstanceStatus(iid) {
192 | const ins = this.instances.get(iid)
193 | if (!ins) {
194 | // Note: Kong expect the error to start with "no plugin instance"
195 | throw new PluginServerError(`no plugin instance #${iid}`)
196 | }
197 |
198 | return {
199 | Name: ins.getName(),
200 | Id: iid,
201 | Config: ins.getConfig(),
202 | StartTime: ins.getStartTime(),
203 | }
204 | }
205 |
206 | // RPC method
207 | async CloseInstance(iid) {
208 |
209 | let ins = this.instances.get(iid)
210 | if (!ins) {
211 | // Note: Kong expect the error to start with "no plugin instance"
212 | throw new PluginServerError(`no plugin instance #${iid}`)
213 | }
214 |
215 | ins.close()
216 | this.instances.delete(iid)
217 |
218 | return {
219 | Name: ins.getName(),
220 | Id: iid,
221 | Config: ins.getConfig(),
222 | }
223 | }
224 |
225 | // RPC method
226 | async HandleEvent(event) {
227 | const iid = event.InstanceId
228 | const ins = this.instances.get(iid)
229 | if (!ins) {
230 | // Note: Kong expect the error to start with "no plugin instance"
231 | throw new PluginServerError(`no plugin instance #${iid}`)
232 | }
233 |
234 | ins.resetExpireTs()
235 |
236 | const phase = event.EventName
237 | const eid = this.eventID++
238 |
239 | const [ch, childCh] = new PipePair().getPair()
240 | this.events.set(eid, ch)
241 |
242 | // https://snyk.io/blog/nodejs-how-even-quick-async-functions-can-block-the-event-loop-starve-io/
243 | setImmediate(async () => {
244 | try {
245 | await ins.executePhase(phase, new PDK(childCh).kong)
246 | } catch(ex){
247 | this.logger.warn(
248 | `unhandled exception in ${ins.name}.${phase} on instance #${iid}: ${ex}`
249 | )
250 | }
251 | childCh.put(MSG_RET)
252 | })
253 |
254 | const r = await ch.get()
255 | ins.resetExpireTs()
256 |
257 | return {
258 | Data: r,
259 | EventId: eid,
260 | }
261 | }
262 |
263 | async step(data, isError) {
264 | const din = data.Data
265 | const eid = data.EventId
266 | const ch = this.events.get(eid)
267 |
268 | if (!ch) {
269 | throw new PluginServerError(`event id ${eid} not found`)
270 | }
271 |
272 | if (isError) {
273 | await ch.put([ undefined, din ])
274 | } else {
275 | await ch.put([ din, undefined ])
276 | }
277 |
278 | const ret = await ch.get()
279 | if (ret === MSG_RET) this.events.delete(eid)
280 | return {
281 | Data: ret,
282 | EventId: eid
283 | }
284 | }
285 |
286 | // RPC method
287 | async Step(data) {
288 | return this.step(data, false)
289 | }
290 |
291 | // RPC method
292 | async StepError(err) {
293 | return this.step(err, true)
294 | }
295 |
296 | // RPC method
297 | async StepMultiMap(data) {
298 | return this.step(data, false)
299 | }
300 |
301 | getLogger() {
302 | return this.logger
303 | }
304 |
305 | getPlugins() {
306 | return this.plugins
307 | }
308 | }
309 |
310 | // Generate other RPC methods
311 | for (const entity of entities) {
312 | Server.prototype['Step' + entity] = Server.prototype.Step
313 | }
314 |
315 |
316 | module.exports = Server
317 |
--------------------------------------------------------------------------------