Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: APISIX JavaScript Plugin Runner #5106

Closed
zenozeng opened this issue Sep 20, 2021 · 2 comments
Closed

Proposal: APISIX JavaScript Plugin Runner #5106

zenozeng opened this issue Sep 20, 2021 · 2 comments

Comments

@zenozeng
Copy link
Contributor

zenozeng commented Sep 20, 2021

Issue description

Abstract

Apache APISIX has been supporting writing customized plugins using Lua, Java, Go and Python.
I propose to add JavaScript Plugin Runner for Apache APISIX.

Background

JavaScript is widely used

JavaScript is a widely used dynamic language. According to SlashData, JavaScript, which includes CoffeeScript and TypeScript in the survey, is by far the most popular language, with 12.4 million developers using it worldwide.

Apache APISIX has been supporting writing customized plugins using Lua, Java, Go and Python.
LuaJIT is really fast, and Lua is very easy to learn. But out-of-the-box JavaScript plugin runner will unlock more developers with JavaScript background for Apache APISIX.

Node.js and Deno

Node.js is the most popular JavaScript runtime built on Chrome's V8 JavaScript engine.
Deno, yet another JavaScript runtime built on V8, with 77.9K stars on GitHub, was created by ry (creator of Node.js) to address his 10 Things I Regret About Node.js.
It would be nice if APISIX provides first class support for both Node.js and Deno.

WebAssembly support

Supporting WebAssembly in Apache APISIX has been discussed at #157.
Both Node.js and Deno can execute WebAssembly modules.

  1. https://nodejs.org/api/wasi.html
  2. https://deno.land/[email protected]/getting_started/webassembly

There are many WebAssembly runtime, including but not limited to

  1. Wasmtime, a Bytecode Alliance project (the Wasmtime team is moving to Fastly)
  2. WasmEdge (formerly SSVM), open sourced by Second State, a CNCF project
  3. Wasmer, used by Dart
  4. Lucet, open sourced by Fastly, a Bytecode Alliance project (in the medium-to-long term, the plan is to merge Lucet and Wasmtime)

It's possible to support WebAssembly in JavaScript Plugin Runner, but maybe a separated Apache APISIX WebAssembly Runner is better?

Proposal

To address the problems described above, I propose to add JavaScript Plugin Runner for Apache APISIX. It will provide first class support for both Node.js and Deno.

Demo Implementation

I have created a demo implementation: https://github.com/zenozeng/apisix-javascript-plugin-runner.

Plugin Example

# examples/config.yaml
ext-plugin:
  cmd: 
    - "/usr/local/apisix/javascript-plugin-runner/bin/runner"
    - "/usr/local/apisix/javascript-plugin-runner/examples/say.js"

apisix:
  port_admin: 9180
  admin_key:
    -
      name: "admin"
      key: "YOUR_ADMIN_KEY"
      role: admin

etcd:
  host:
    - "http://etcd:2379"
class SayPlugin {

    getName() {
        return "say"
    }

    parseConf(conf) {
        return JSON.parse(conf)
    }

    // Filter will be executed on every request with the say plugin configured.
    async filter(conf, request, response) {
        const headers = new Map()
        headers.set('X-Resp-A6-JavaScript-Plugin', 'Say')
        response.body = conf.body
        response.headers = headers
    }

}

module.exports = SayPlugin
# default config.apisix.allow_admin is 127.0.0.0/24, using docker exec
docker exec -it apisix curl -H "Content-Type: application/json" \
    -H 'X-API-KEY: YOUR_ADMIN_KEY' \
    -X PUT \
    --data '{"uri":"/say","methods":["PUT","GET"],"plugins":{"ext-plugin-pre-req":{"conf":[{"name":"say","value":"{\"body\":\"123\"}"}]}}}' \
    http://127.0.0.1:9180/apisix/admin/routes/1
curl -v http://127.0.0.1:9080/say

< HTTP/1.1 200 OK
< Date: Fri, 09 Jul 2021 18:20:24 GMT
< Content-Type: text/plain; charset=utf-8
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Resp-A6-JavaScript-Plugin: Say
< Server: APISIX/2.7
< 
* Connection #0 to host 127.0.0.1 left intact
123

Interface

interface Plugin {
    getName(): string
    parseConf(conf: string): string
    filter(conf: Object, request: Request, response: Response): Promise<void>
}

interface Request {
    // The id is for debug. It will be recycled when the number is exhausted
    id: number;

    // the path of the request.
    path: string;

    // The associated Headers object of the request.
    // See·https://developer.mozilla.org/en-US/docs/Web/API/Headers
    headers: Headers;

    srcIp: number[];

    // Request's method (GET, POST, etc.)
    method: string;

    // The associated Args object of the request.
    args: Args;
}


interface Args {
    keys(): Iterable<string>
    get(k: string): string
    set(k: string, v: string): Args
}

interface Headers {
    keys(): Iterable<string>
    get(k: string): string
    set(k: string, v: string): Headers
}

interface Response {
    // The status code of the response (200, 404, etc.)
    status?: number;
    // The associated Headers object of the response.
    // See https://developer.mozilla.org/en-US/docs/Web/API/Headers
    headers?: Headers;
    // The body of the response
    body?: Uint8Array | string;
}

References

  1. https://www.zdnet.com/article/programming-language-popularity-javascript-leads-5-million-new-developers-since-2017/
  2. https://medium.com/@ApacheAPISIX/why-apache-apisix-chose-nginx-and-lua-to-build-api-gateway-3d4f81e3e1f1
  3. https://medium.com/@ApacheAPISIX/how-to-write-an-apache-apisix-plugin-in-java-3fb7b75444fa
  4. https://deno.com/blog/v1
  5. https://www.youtube.com/watch?v=M3BM9TB-8yA
  6. https://github.com/second-state/wasm32-wasi-benchmark
  7. https://www.fastly.com/blog/how-lucet-wasmtime-make-stronger-compiler-together

What do you think about this? Is the direction I am doing right?

@zenozeng
Copy link
Contributor Author

@spacewander Thanks! I just created a pull request: #5110

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants