view xml/en/docs/njs/examples.xml @ 2846:fdf1464e1977

Moved banner to the external file to make partial rollout possible. An idea is to have several banners and show them with different probability specified by split directive in the nginx.conf
author Sergey Budnevitch <sb@waeme.net>
date Tue, 10 May 2022 18:07:27 +0400
parents 24b379907b0f
children 9eadb98ec770
line wrap: on
line source

<?xml version="1.0"?>

<!--
  Copyright (C) Nginx, Inc.
  -->

<!DOCTYPE article SYSTEM "../../../../dtd/article.dtd">

<article name="Examples"
        link="/en/docs/njs/examples.html"
        lang="en"
        rev="21">

<section id="summary">

<para>
The examples work since
<link doc="../njs/changes.xml" id="njs0.4.0">0.4.0</link>.
</para>

</section>


<section id="helloword" name="Hello World">

<para>
<path>nginx.conf</path>:
<example>
events {}

http {
    js_import http.js;

    server {
        listen 8000;

        location / {
            js_content http.hello;
        }
    }
}
</example>
</para>

<para>
<literal>http.js</literal>:
<example>
function hello(r) {
    r.return(200, "Hello world!");
}

export default {hello};
</example>
</para>

</section>


<section id="http_auth" name="HTTP Аuthorization">


<section id="jwt" name="Creating HS JWT">

<para>
<path>nginx.conf</path>:
<example>
js_import http.js;

js_set $jwt http.jwt;
</example>
</para>

<para>
<path>http.js</path>:
<example>
function generate_hs256_jwt(claims, key, valid) {
    var header = { typ: "JWT",  alg: "HS256" };
    var claims = Object.assign(claims, {exp: Math.floor(Date.now()/1000) + valid});

    var s = [header, claims].map(JSON.stringify)
                            .map(v=>v.toString('base64url'))
                            .join('.');

    var h = require('crypto').createHmac('sha256', key);

    return s + '.' + h.update(s).digest('base64url');
}

function jwt(r) {
    var claims = {
        iss: "nginx",
        sub: "alice",
        foo: 123,
        bar: "qq",
        zyx: false
    };

    return generate_hs256_jwt(claims, 'foo', 600);
}

export default {jwt};
</example>
</para>

</section>


<section id="secure_link" name="Creating secure_link Hash">

<para>
<path>nginx.conf</path>:
<example>
js_import http.js;

js_set $new_foo http.create_secure_link;
#...

location / {
    secure_link $cookie_foo;
    secure_link_md5 "$uri mykey";
    #
}

location @login {
    add_header Set-Cookie "foo=$new_foo; Max-Age=60";
    return 302 /;
}
</example>
</para>

<para>
<path>http.js</path>:
<example>
function create_secure_link(r) {
    return require('crypto').createHash('md5')
                            .update(r.uri).update(" mykey")
                            .digest('base64url');
}

export default {create_secure_link};
</example>
</para>

</section>


<section id="jwt_field" name="Getting Arbitrary Field from JWT
                              as nginx Variable">

<para>
In the following example, the <literal>sub</literal> field
is extracted from JWT payload.
The JWT token is taken from the <header>Authorization</header> header.
</para>

<para>
<path>nginx.conf</path>:
<example>
js_import http.js;

js_set $jwt_payload_sub http.jwt_payload_sub;

server {
    #...

    location /jwt {
        return 200 $jwt_payload_sub;
    }
}
</example>
</para>

<para>
<path>http.js</path>:
<example>
function jwt(data) {
    var parts = data.split('.').slice(0,2)
        .map(v=>Buffer.from(v, 'base64url').toString())
        .map(JSON.parse);
    return { headers:parts[0], payload: parts[1] };
}

function jwt_payload_sub(r) {
    return jwt(r.headersIn.Authorization.slice(7)).payload.sub;
    // when the token is provided as the "myjwt" argument
    // return jwt(r.args.myjwt).payload.sub;
}

export default {jwt_payload_sub};
</example>
</para>

</section>

</section>


<section id="http_proxying" name="HTTP Proxying">


<section id="subrequest" name="Accessing API from a Subrequest">

<para>
<path>nginx.conf</path>:
<example>
js_import http.js;

keyval_zone zone=foo:10m;
#...

location /keyval {
    js_content http.set_keyval;
}

location /version {
    js_content http.version;
}

location /api {
    api write=on;
}
</example>
</para>

<para>
<path>http.js</path>:
<example>
async function set_keyval(r) {
    let res = await r.subrequest('/api/7/http/keyvals/foo',
        { method: 'POST',
          body: JSON.stringify({ foo: 789, bar: "ss dd 00" })});

    if (res.status >= 300) {
        r.return(res.status, res.responseBody);
        return;
    }

    r.return(200);
}

async function version(r) {
    let res = await r.subrequest('/api/7/nginx', { method: 'GET' });

    if (res.status != 200) {
        r.return(res.status);
        return;
    }

    var json = JSON.parse(res.responseBody);
    r.return(200, json.version);
}

export default {set_keyval, version};
</example>
<note>
The <link doc="../http/ngx_http_keyval_module.xml" id="keyval"/>,
<link doc="../http/ngx_http_keyval_module.xml" id="keyval_zone"/>,
and <link doc="../http/ngx_http_api_module.xml" id="api"/> directives
are available as part of our
<commercial_version>commercial subscription</commercial_version>.
</note>
</para>

</section>


<section id="fast_response" name="Returning Fastest Response from Proxy">

<para>
<path>nginx.conf</path>:
<example>
js_import http.js;

location /start {
    js_content http.content;
}

location /foo {
    proxy_pass http://backend1;
}

location /bar {
    proxy_pass http://backend2;
}
</example>
</para>

<para>
<path>http.js</path>:
<example>
function content(r) {
    var n = 0;

    function done(res) {
        if (n++ == 0) {
            r.return(res.status, res.responseBody);
        }
    }

    r.subrequest('/foo', r.variables.args, done);
    r.subrequest('/bar', r.variables.args, done);
}

export default {content};
</example>
</para>

</section>


<section id="subrequests_chaining" name="Subrequests Chaining">

<para>
<path>nginx.conf</path>:
<example>
js_import http.js;

location /start {
    js_content http.content;
}

location /auth {
    proxy_pass http://auth_backend;
}

location /backend {
    proxy_pass http://backend;
}
</example>
</para>

<para>
<path>http.js</path>:
<example>
async function content(r) {
    try {
        let reply = await r.subrequest('/auth');
        let response = JSON.parse(reply.responseBody);
        let token = response['token'];

        if (!token) {
            throw new Error("token is not available");
        }

        let backend_reply = await r.subrequest('/backend', `token=${token}`);
        r.return(backend_reply.status, backend_reply.responseBody);

    } catch (e) {
        r.error(e);
        r.return(500);
    }
}

export default {content};
</example>
</para>

</section>

</section>


<section id="misc" name="Miscellaneous">


<section id="redirect" name="Internal Redirect">

<para>
<path>nginx.conf</path>:
<example>
js_import http.js;

location /redirect {
    js_content http.redirect;
}

location @named {
    return 200 named;
}
</example>
</para>

<para>
<path>http.js</path>:
<example>
function redirect(r) {
    r.internalRedirect('@named');
}

export default {redirect};
</example>
</para>

</section>


<section id="requests" name="Logging the Number of Requests Per Client">

<para>
<path>nginx.conf</path>:
<example>
js_import http.js;

js_set $num_requests http.num_requests;

keyval_zone zone=foo:10m;

keyval $remote_addr $foo zone=foo;

log_format bar '$remote_addr [$time_local] $num_requests';
access_log logs/access.log bar;

server {
    listen 8000;

    location / {
        root html;
    }
}
</example>
</para>

<para>
<path>http.js</path>:
<example>
function num_requests(r)
{
    var n = r.variables.foo;
    n = n ? Number(n) + 1 : 1;
    r.variables.foo = n;
    return n;
}

export default {num_requests};
</example>
<note>
The <link doc="../http/ngx_http_keyval_module.xml" id="keyval"/> and
<link doc="../http/ngx_http_keyval_module.xml" id="keyval_zone"/> directives
are available as part of our
<commercial_version>commercial subscription</commercial_version>.
</note>
</para>

</section>

</section>

</article>