Launching nginScript and Looking Ahead - NGINX

Original: https://www.nginx.com/blog/launching-nginscript-and-looking-ahead/

Editor – nginScript is now known as the NGINX JavaScript module.

I’ve been wanting to add more scripting capabilities to NGINX for a long time. Scripting lets people do more in NGINX without having to write C modules, for example. Lua is a good tool in this area, but it’s not as widely known as some other languages.

JavaScript was the most obvious language to add next. It’s the most popular language – #1 on GitHub for the past three years. JavaScript is also a good fit for the way we configure NGINX.

I recently announced a working prototype of a JavaScript virtual machine (VM) that would be embedded within NGINX. Today we announced the launch of the first preview of this software, nginScript.

This is another milestone in the development of NGINX open source software and NGINX Plus. I want to take the opportunity to explain what nginScript is, describe why it’s needed, share some examples, and talk about the future.

What nginScript Is

nginScript is a robust implementation of much of JavaScript, including the parts most useful for configuring NGINX. Some less‑used parts of JavaScript and some less relevant built‑in objects are not supported.

nginScript can be used for quick fixes, making NGINX configuration more convenient and operations more efficient. But, because it runs at the application delivery level, it will also enable you to refactor your applications for greater long‑term stability, security, and scale.

nginScript has two parts:

  1. A custom virtual machine (VM) and byte‑code compiler with a very fast start‑up and tear‑down time. Blocking operations, such as an HTTP subrequest, can be suspended and resumed, in the same way as other blocking operations in JavaScript.
  2. A configuration syntax for embedding snippets of JavaScript in your NGINX configuration. These snippets are evaluated at runtime, as HTTP transactions proceed. You can create powerful conditional configurations, modify requests and responses on the fly, and adjust the internal operations of NGINX specifically for each request.

There are currently several popular JavaScript VMs out there, but they are optimized for use with browsers. The nginScript VM is focused on server requirements. We run a separate VM for each request, so there’s no need for garbage collection. And an nginScript VM can be pre‑empted so NGINX code can run.

What nginScript Makes Possible

nginScript has a wide range of potential uses. The following capabilities are available today, and they will be extended over time:

In the future, we’ll add more capabilities to nginScript and broaden its scope, so you can use it for tasks such as fixing application bugs, changing business rules, personalizing the user experience, and sharding requests across servers and aggregating the replies.

nginScript in Action

There are two new directives that can be used to insert JavaScript code into NGINX, js_set and js_run.

[Editor – The following configuration examples use an early form of nginScript syntax that is deprecated since nginScript became generally available in NGINX Plus R12. For details on the standardized syntax, see Introduction to nginScript and the reference documentation for the HTTP and Stream nginScript modules.]

The js_set Directive

The js_set directive sets the value of a variable to the result of the execution of JavaScript code:

http {
    js_set $hello_world "
            var str = 'Hello World!';
            // JavaScript
            str;
    ";
 
    server {
        # ...
        location /{
            return 200 $hello_world;
        }
    }
}

In the above example, the value of $hello_world is set to the value of the last statement in the JavaScript code. The client is then returned the value of this variable: “Hello World!”.

A variable set by js_set can be used in any NGINX directive that takes a variable parameter, such as limit_req_zone, proxy_pass, and sub_filter.

The js_run Directive

The js_run directive is evaluated at the content‑generation stage. It’s used to execute JavaScript natively and generate an HTTP response. It goes inside a location block and triggers execution of JavaScript code when the location matches a request URL.

location / {
    js_run "
        var res;
        res = $r.response;
        res.status = 200;
        res.send('Hello World!');
        res.finish();
    ";
}

Request Parameters

With nginScript you have full access to the request parameters, with the $r variable. The following example returns the request parameters back to the requester.

http {
    js_set $summary "
    var a, s, h;
 
    s = 'JS summary\n\n';
 
    s += 'Method: ' + $r.method + '\n';
    s += 'HTTP version: ' + $r.httpVersion + '\n';
    s += 'Host: ' + $r.headers.host + '\n';
    s += 'Remote Address: ' + $r.remoteAddress + '\n';
    s += 'URI: ' + $r.uri + '\n';
 
    s += 'Headers:\n';
    for (h in $r.headers) {
        s += '  header \"' + h + '\" is \"' + $r.headers[h] + '\"\n';
    }
 
    s += 'Args:\n';
    for (a in $r.args) {
        s += '  arg \"' + a + '\" is \"' + $r.args[a] + '\"\n';
    }
 
    s;
    ";
 
    server {
    listen 8000;
 
    location /summary {
        return 200 $summary;
    }
}

Custom Request Routing

With the location block in NGINX you can route traffic based on URI. With nginScript you can route traffic based on any data in the request, including cookies, headers, arguments, or any keywords in the request body. The following example routes traffic based on the presence of an argument named upstream:

upstream my_upstream0 {
    server server1.example.com;
    server server2.example.com;
}
upstream my_upstream1 {
    server server3.example.com;
    server server4.example.com;
}
 
js_set $my_upstream "
    var s, upstream, upstream_num;
 
    upstream = $r.args.upstream;
 
    // convert upstream number to integer
    upstream_num = +upstream | 0;
 
    if (upstream_num < 0 || upstream_num > 1) {
        upstream_num = 0;
    }
 
    s = 'my_upstream' + upstream_num;
 
    s;
";
 
server {
    listen 80;
 
    location / {
        proxy_set_header Host $host;
        proxy_pass http://$my_upstream;
    }
}

The Future of nginScript

Our goal is to make NGINX the connective tissue and traffic management layer for all modern web applications. Embedding JavaScript in NGINX is a major step in this direction. nginScript joins Lua, Perl, and modules as robust ways to extend NGINX.

nginScript is not intended to replace Lua or other embedded languages in NGINX. JavaScript has now been added to the existing community of extensions to NGINX, and we expect the number of alternatives continues to grow in the future.

nginScript enables testing and prototyping, functionality and security fixes, and strategic refactoring of application development and deployment. We look forward to your feedback as you try out nginScript and consider its long‑term place in your application development and delivery strategy.

For more information, check out these resources:

To provide feedback, please use our mailing list.

[Editor – To learn more about how and why we tailored nginScript to work seamlessly with NGINX, see nginScript – Why Are We Creating Our Own JavaScript Implementation?]

Retrieved by Nick Shadrin from nginx.com website.