[njs] Stream js: js_access, js_preread, js_filter.

Roman Arutyunyan arut at nginx.com
Wed Sep 28 16:59:23 UTC 2016


details:   http://hg.nginx.org/njs/rev/322359ec0913
branches:  
changeset: 185:322359ec0913
user:      Roman Arutyunyan <arut at nginx.com>
date:      Wed Sep 28 19:52:05 2016 +0300
description:
Stream js: js_access, js_preread, js_filter.

diffstat:

 README                       |  100 +++++++-
 nginx/ngx_stream_js_module.c |  550 ++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 642 insertions(+), 8 deletions(-)

diffs (792 lines):

diff -r cfa17c3e25da -r 322359ec0913 README
--- a/README	Mon Sep 26 18:41:57 2016 +0300
+++ b/README	Wed Sep 28 19:52:05 2016 +0300
@@ -144,8 +144,16 @@ Each Stream JavaScript handler receives 
 The following properties are available in the session object:
 
      - remoteAddress
+     - eof
+     - fromUpstream
+     - buffer
      - variables{}
      - log()
+     - OK
+     - DECLINED
+     - AGAIN
+     - ERROR
+     - ABORT
 
 
 Example nginx.conf:
@@ -167,22 +175,60 @@ Example nginx.conf:
         server {
             listen 8000;
 
+            # preread data with qux()
+            js_preread qux;
+
             # create $foo and $bar variables and set JavaScript
             # functions foo() and bar() from the included JavaScript
             # file as their handlers
             js_set $foo foo;
             js_set $bar bar;
 
-            return $foo-$bar;
+            # echo-server: return preread data
+            return foo;
+        }
+
+        server {
+            listen 8001;
+
+            # check access in xyz()
+            js_access xyz;
+
+            proxy_pass 127.0.0.1:9000;
+
+            # add JavaScript filter baz() from the included
+            # JavaScript file
+            js_filter baz;
+        }
+    }
+
+    http {
+        server {
+            listen 9000;
+            location / {
+                return 200 $http_foo\n;
+            }
         }
     }
 
 
 stream.js:
 
+    var req = '';
+    var matched = 0;
+    var line = '';
+
+    function qux(s) {
+        n = s.buffer.indexOf('\n');
+        if (n == -1) {
+            return s.AGAIN;
+        }
+
+        line = s.buffer.substr(0, n);
+    }
+
     function foo(s) {
-        s.log("hello from foo() handler!");
-        return s.remoteAddress;
+        return line;
     }
 
     function bar(s) {
@@ -191,6 +237,54 @@ stream.js:
         return "foo-var" + v.remote_port + "; pid=" + v.pid;
     }
 
+    // The filter processes one buffer per call.
+    // The buffer is available in s.buffer both for
+    // reading and writing.  Called for both directions.
+
+    function baz(s) {
+        if (s.fromUpstream || matched) {
+            return;
+        }
+
+        // Disable certain addresses.
+
+        if (s.remoteAddress.match('^192.*')) {
+            return s.ERROR;
+        }
+
+        // Read HTTP request line.
+        // Collect bytes in 'req' until request
+        // line is read.  Clear current buffer to
+        // disable output.
+
+        req = req + s.buffer;
+        s.buffer = '';
+
+        n = req.search('\n');
+
+        if (n != -1) {
+            // Inject a new HTTP header.
+            var rest = req.substr(n + 1);
+            req = req.substr(0, n + 1);
+
+            addr = s.remoteAddress;
+
+            s.log('req:' + req);
+            s.log('rest:' + rest);
+
+            // Output the result and skip further
+            // processing.
+
+            s.buffer = req + 'Foo: addr_' + addr + '\r\n' + rest;
+            matched = 1;
+        }
+    }
+
+    function xyz(s) {
+        if (s.remoteAddress.match('^192.*')) {
+            return s.ABORT;
+        }
+    }
 
 --
 NGINX, Inc., http://nginx.com
diff -r cfa17c3e25da -r 322359ec0913 nginx/ngx_stream_js_module.c
--- a/nginx/ngx_stream_js_module.c	Mon Sep 26 18:41:57 2016 +0300
+++ b/nginx/ngx_stream_js_module.c	Wed Sep 28 19:52:05 2016 +0300
@@ -40,15 +40,30 @@
 typedef struct {
     njs_vm_t              *vm;
     njs_opaque_value_t     arg;
+    ngx_str_t              access;
+    ngx_str_t              preread;
+    ngx_str_t              filter;
 } ngx_stream_js_srv_conf_t;
 
 
 typedef struct {
     njs_vm_t              *vm;
     njs_opaque_value_t    *arg;
+    ngx_buf_t             *buf;
+    ngx_chain_t           *free;
+    ngx_chain_t           *busy;
+    ngx_stream_session_t  *session;
+    unsigned               from_upstream:1;
+    unsigned               filter:1;
 } ngx_stream_js_ctx_t;
 
 
+static ngx_int_t ngx_stream_js_access_handler(ngx_stream_session_t *s);
+static ngx_int_t ngx_stream_js_preread_handler(ngx_stream_session_t *s);
+static ngx_int_t ngx_stream_js_phase_handler(ngx_stream_session_t *s,
+    ngx_str_t *name);
+static ngx_int_t ngx_stream_js_body_filter(ngx_stream_session_t *s,
+    ngx_chain_t *in, ngx_uint_t from_upstream);
 static ngx_int_t ngx_stream_js_variable(ngx_stream_session_t *s,
     ngx_stream_variable_value_t *v, uintptr_t data);
 static void ngx_stream_js_cleanup_mem_cache_pool(void *data);
@@ -61,10 +76,20 @@ static void ngx_stream_js_free(void *mem
 
 static njs_ret_t ngx_stream_js_ext_get_remote_address(njs_vm_t *vm,
     njs_value_t *value, void *obj, uintptr_t data);
-static njs_ret_t ngx_stream_js_ext_log(njs_vm_t *vm, njs_value_t *args,
-    nxt_uint_t nargs, njs_index_t unused);
+static njs_ret_t ngx_stream_js_ext_get_eof(njs_vm_t *vm, njs_value_t *value,
+    void *obj, uintptr_t data);
+static njs_ret_t ngx_stream_js_ext_get_from_upstream(njs_vm_t *vm,
+    njs_value_t *value, void *obj, uintptr_t data);
+static njs_ret_t ngx_stream_js_ext_get_buffer(njs_vm_t *vm, njs_value_t *value,
+    void *obj, uintptr_t data);
+static njs_ret_t ngx_stream_js_ext_set_buffer(njs_vm_t *vm, void *obj,
+    uintptr_t data, nxt_str_t *value);
+ static njs_ret_t ngx_stream_js_ext_log(njs_vm_t *vm, njs_value_t *args,
+     nxt_uint_t nargs, njs_index_t unused);
 static njs_ret_t ngx_stream_js_ext_get_variable(njs_vm_t *vm,
     njs_value_t *value, void *obj, uintptr_t data);
+static njs_ret_t ngx_stream_js_ext_get_code(njs_vm_t *vm,
+    njs_value_t *value, void *obj, uintptr_t data);
 
 static char *ngx_stream_js_include(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
@@ -73,6 +98,7 @@ static char *ngx_stream_js_set(ngx_conf_
 static void *ngx_stream_js_create_srv_conf(ngx_conf_t *cf);
 static char *ngx_stream_js_merge_srv_conf(ngx_conf_t *cf, void *parent,
     void *child);
+static ngx_int_t ngx_stream_js_init(ngx_conf_t *cf);
 
 
 static ngx_command_t  ngx_stream_js_commands[] = {
@@ -91,13 +117,34 @@ static ngx_command_t  ngx_stream_js_comm
       0,
       NULL },
 
+    { ngx_string("js_access"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_js_srv_conf_t, access),
+      NULL },
+
+    { ngx_string("js_preread"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_js_srv_conf_t, preread),
+      NULL },
+
+    { ngx_string("js_filter"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_js_srv_conf_t, filter),
+      NULL },
+
     ngx_null_command
 };
 
 
 static ngx_stream_module_t  ngx_stream_js_module_ctx = {
     NULL,                          /* preconfiguration */
-    NULL,                          /* postconfiguration */
+    ngx_stream_js_init,            /* postconfiguration */
 
     NULL,                          /* create main configuration */
     NULL,                          /* init main configuration */
@@ -148,6 +195,42 @@ static njs_external_t  ngx_stream_js_ext
       NULL,
       0 },
 
+    { nxt_string("eof"),
+      NJS_EXTERN_PROPERTY,
+      NULL,
+      0,
+      ngx_stream_js_ext_get_eof,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      0 },
+
+    { nxt_string("fromUpstream"),
+      NJS_EXTERN_PROPERTY,
+      NULL,
+      0,
+      ngx_stream_js_ext_get_from_upstream,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      0 },
+
+    { nxt_string("buffer"),
+      NJS_EXTERN_PROPERTY,
+      NULL,
+      0,
+      ngx_stream_js_ext_get_buffer,
+      ngx_stream_js_ext_set_buffer,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      0 },
+
     { nxt_string("log"),
       NJS_EXTERN_METHOD,
       NULL,
@@ -171,6 +254,66 @@ static njs_external_t  ngx_stream_js_ext
       NULL,
       NULL,
       0 },
+
+    { nxt_string("OK"),
+      NJS_EXTERN_PROPERTY,
+      NULL,
+      0,
+      ngx_stream_js_ext_get_code,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      -NGX_OK },
+
+    { nxt_string("DECLINED"),
+      NJS_EXTERN_PROPERTY,
+      NULL,
+      0,
+      ngx_stream_js_ext_get_code,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      -NGX_DECLINED },
+
+    { nxt_string("AGAIN"),
+      NJS_EXTERN_PROPERTY,
+      NULL,
+      0,
+      ngx_stream_js_ext_get_code,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      -NGX_AGAIN },
+
+    { nxt_string("ERROR"),
+      NJS_EXTERN_PROPERTY,
+      NULL,
+      0,
+      ngx_stream_js_ext_get_code,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      -NGX_ERROR },
+
+    { nxt_string("ABORT"),
+      NJS_EXTERN_PROPERTY,
+      NULL,
+      0,
+      ngx_stream_js_ext_get_code,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      -NGX_ABORT },
 };
 
 
@@ -190,6 +333,224 @@ static njs_external_t  ngx_stream_js_ext
 };
 
 
+static ngx_stream_filter_pt  ngx_stream_next_filter;
+
+
+static ngx_int_t
+ngx_stream_js_access_handler(ngx_stream_session_t *s)
+{
+    ngx_int_t                  rc;
+    ngx_stream_js_srv_conf_t  *jscf;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
+                   "js access handler");
+
+    jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module);
+
+    rc = ngx_stream_js_phase_handler(s, &jscf->access);
+
+    if (rc == NGX_ABORT) {
+        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+                      "access forbidden by js");
+        rc = NGX_STREAM_FORBIDDEN;
+    }
+
+    return rc;
+}
+
+
+static ngx_int_t
+ngx_stream_js_preread_handler(ngx_stream_session_t *s)
+{
+    ngx_stream_js_srv_conf_t  *jscf;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
+                   "js preread handler");
+
+    jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module);
+
+    return ngx_stream_js_phase_handler(s, &jscf->preread);
+}
+
+
+static ngx_int_t
+ngx_stream_js_phase_handler(ngx_stream_session_t *s, ngx_str_t *name)
+{
+    nxt_str_t                  fname, value, exception;
+    ngx_int_t                  rc;
+    njs_function_t            *func;
+    ngx_connection_t          *c;
+    ngx_stream_js_ctx_t       *ctx;
+
+    if (name->len == 0) {
+        return NGX_DECLINED;
+    }
+
+    c = s->connection;
+
+    rc = ngx_stream_js_init_vm(s);
+    if (rc != NGX_OK) {
+        return rc;
+    }
+
+    ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
+
+    fname.start = name->data;
+    fname.length = name->len;
+
+    func = njs_vm_function(ctx->vm, &fname);
+
+    if (func == NULL) {
+        ngx_log_error(NGX_LOG_ERR, c->log, 0, "js function \"%V\" not found",
+                      name);
+        return NGX_ERROR;
+    }
+
+    if (njs_vm_call(ctx->vm, func, ctx->arg, 1) != NJS_OK) {
+        njs_vm_exception(ctx->vm, &exception);
+
+        ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %*s",
+                      exception.length, exception.start);
+
+        return NGX_ERROR;
+    }
+
+    if (ctx->vm->retval.type == NJS_VOID) {
+        return NGX_OK;
+    }
+
+    if (njs_vm_retval(ctx->vm, &value) != NJS_OK) {
+        return NGX_ERROR;
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_STREAM, c->log, 0, "js return value: \"%*s\"",
+                   value.length, value.start);
+
+    rc = ngx_atoi(value.start, value.length);
+
+    if (rc == NGX_ERROR) {
+        ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                      "unexpected js return code: \"%*s\"",
+                      value.length, value.start);
+        return NGX_ERROR;
+    }
+
+    return -rc;
+}
+
+
+static ngx_int_t
+ngx_stream_js_body_filter(ngx_stream_session_t *s, ngx_chain_t *in,
+    ngx_uint_t from_upstream)
+{
+    nxt_str_t                  name, value, exception;
+    ngx_int_t                  rc;
+    ngx_chain_t               *out, *cl, **ll;
+    njs_function_t            *func;
+    ngx_connection_t          *c;
+    ngx_stream_js_ctx_t       *ctx;
+    ngx_stream_js_srv_conf_t  *jscf;
+
+    jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module);
+    if (jscf->filter.len == 0) {
+        return ngx_stream_next_filter(s, in, from_upstream);
+    }
+
+    c = s->connection;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "stream js filter u:%ui",
+                   from_upstream);
+
+    rc = ngx_stream_js_init_vm(s);
+
+    if (rc == NGX_ERROR) {
+        return NGX_ERROR;
+    }
+
+    if (rc == NGX_DECLINED) {
+        return ngx_stream_next_filter(s, in, from_upstream);
+    }
+
+    ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
+
+    ctx->filter = 1;
+
+    name.start = jscf->filter.data;
+    name.length = jscf->filter.len;
+
+    func = njs_vm_function(ctx->vm, &name);
+
+    if (func == NULL) {
+        ngx_log_error(NGX_LOG_ERR, c->log, 0, "js function \"%V\" not found",
+                      &jscf->filter);
+        return NGX_ERROR;
+    }
+
+    ctx->from_upstream = from_upstream;
+
+    ll = &out;
+
+    while (in) {
+        ctx->buf = in->buf;
+
+        if (njs_vm_call(ctx->vm, func, ctx->arg, 1) != NJS_OK) {
+            njs_vm_exception(ctx->vm, &exception);
+
+            ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %*s",
+                          exception.length, exception.start);
+
+            return NGX_ERROR;
+        }
+
+        if (njs_vm_retval(ctx->vm, &value) != NJS_OK) {
+            return NGX_ERROR;
+        }
+
+        ngx_log_debug2(NGX_LOG_DEBUG_STREAM, c->log, 0,
+                       "js return value: \"%*s\"",
+                       value.length, value.start);
+
+        if (value.length) {
+            rc = ngx_atoi(value.start, value.length);
+
+            if (rc != NGX_OK && rc != -NGX_ERROR) {
+                ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                              "unexpected js return code: \"%*s\"",
+                              value.length, value.start);
+                return NGX_ERROR;
+            }
+
+            rc = -rc;
+
+            if (rc == NGX_ERROR) {
+                return NGX_ERROR;
+            }
+        }
+
+        cl = ngx_alloc_chain_link(c->pool);
+        if (cl == NULL) {
+            return NGX_ERROR;
+        }
+
+        cl->buf = ctx->buf;
+
+        *ll = cl;
+        ll = &cl->next;
+
+        in = in->next;
+    }
+
+    *ll = NULL;
+
+    rc = ngx_stream_next_filter(s, out, from_upstream);
+
+    ngx_chain_update_chains(c->pool, &ctx->free, &ctx->busy, &out,
+                            (ngx_buf_tag_t) &ngx_stream_js_module);
+
+    return rc;
+}
+
+
 static ngx_int_t
 ngx_stream_js_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v,
     uintptr_t data)
@@ -364,6 +725,137 @@ ngx_stream_js_ext_get_remote_address(njs
 
 
 static njs_ret_t
+ngx_stream_js_ext_get_eof(njs_vm_t *vm, njs_value_t *value, void *obj,
+    uintptr_t data)
+{
+    ngx_buf_t             *b;
+    ngx_connection_t      *c;
+    ngx_stream_js_ctx_t   *ctx;
+    ngx_stream_session_t  *s;
+
+    s = (ngx_stream_session_t *) obj;
+    c = s->connection;
+    ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
+
+    b = ctx->filter ? ctx->buf : c->buffer;
+
+    *value = (b && b->last_buf ? njs_value_true : njs_value_false);
+
+    return NJS_OK;
+}
+
+
+static njs_ret_t
+ngx_stream_js_ext_get_from_upstream(njs_vm_t *vm, njs_value_t *value, void *obj,
+    uintptr_t data)
+{
+    ngx_stream_js_ctx_t   *ctx;
+    ngx_stream_session_t  *s;
+
+    s = (ngx_stream_session_t *) obj;
+    ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
+
+    *value = (ctx->from_upstream ? njs_value_true : njs_value_false);
+
+    return NJS_OK;
+}
+
+
+static njs_ret_t
+ngx_stream_js_ext_get_buffer(njs_vm_t *vm, njs_value_t *value, void *obj,
+    uintptr_t data)
+{
+    size_t                 len;
+    u_char                *p;
+    ngx_buf_t             *b;
+    ngx_connection_t      *c;
+    ngx_stream_js_ctx_t   *ctx;
+    ngx_stream_session_t  *s;
+
+    s = (ngx_stream_session_t *) obj;
+    c = s->connection;
+    ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
+
+    b = ctx->filter ? ctx->buf : c->buffer;
+
+    len = b ? b->last - b->pos : 0;
+
+    p = njs_string_alloc(vm, value, len, 0);
+    if (p == NULL) {
+        return NJS_ERROR;
+    }
+
+    if (len) {
+        ngx_memcpy(p, b->pos, len);
+    }
+
+    return NJS_OK;
+}
+
+
+static njs_ret_t
+ngx_stream_js_ext_set_buffer(njs_vm_t *vm, void *obj, uintptr_t data,
+    nxt_str_t *value)
+{
+    ngx_buf_t             *b;
+    ngx_chain_t           *cl;
+    ngx_connection_t      *c;
+    ngx_stream_js_ctx_t   *ctx;
+    ngx_stream_session_t  *s;
+
+    s = (ngx_stream_session_t *) obj;
+    c = s->connection;
+    ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
+
+    ngx_log_debug2(NGX_LOG_DEBUG_STREAM, c->log, 0,
+                   "stream js set buffer \"%*s\"", value->length, value->start);
+
+    if (!ctx->filter) {
+        ngx_log_error(NGX_LOG_WARN, c->log, 0,
+                      "cannot set buffer in this handler");
+        return NJS_OK;
+    }
+
+    cl = ngx_chain_get_free_buf(c->pool, &ctx->free);
+    if (cl == NULL) {
+        return NJS_ERROR;
+    }
+
+    b = cl->buf;
+
+    ngx_free_chain(c->pool, cl);
+
+    b->last_buf = ctx->buf->last_buf;
+    b->memory = (value->length ? 1 : 0);
+    b->sync = (value->length ? 0 : 1);
+    b->tag = (ngx_buf_tag_t) &ngx_stream_js_module;
+
+    b->start = value->start;
+    b->end = value->start + value->length;
+    b->pos = b->start;
+    b->last = b->end;
+
+    if (ctx->buf->tag != (ngx_buf_tag_t) &ngx_stream_js_module) {
+        ctx->buf->pos = ctx->buf->last;
+
+    } else {
+        cl = ngx_alloc_chain_link(c->pool);
+        if (cl == NULL) {
+            return NJS_ERROR;
+        }
+
+        cl->buf = ctx->buf;
+        cl->next = ctx->free;
+        ctx->free = cl;
+    }
+
+    ctx->buf = b;
+
+    return NJS_OK;
+}
+
+
+static njs_ret_t
 ngx_stream_js_ext_log(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
 {
@@ -375,8 +867,7 @@ ngx_stream_js_ext_log(njs_vm_t *vm, njs_
     s = njs_value_data(njs_argument(args, 0));
     c = s->connection;
 
-    if (njs_value_to_ext_string(vm, &msg, njs_argument(args, 1)) == NJS_ERROR)
-    {
+    if (njs_value_to_ext_string(vm, &msg, njs_argument(args, 1)) == NJS_ERROR) {
         return NJS_ERROR;
     }
 
@@ -418,6 +909,19 @@ ngx_stream_js_ext_get_variable(njs_vm_t 
 }
 
 
+static njs_ret_t
+ngx_stream_js_ext_get_code(njs_vm_t *vm, njs_value_t *value, void *obj,
+    uintptr_t data)
+{
+    ngx_memzero(value, sizeof(njs_value_t));
+
+    value->data.type = NJS_NUMBER;
+    value->data.u.number = data;
+
+    return NJS_OK;
+}
+
+
 static char *
 ngx_stream_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
@@ -606,6 +1110,9 @@ ngx_stream_js_create_srv_conf(ngx_conf_t
      *
      *     conf->vm = NULL;
      *     conf->arg = NULL;
+     *     conf->access = { 0, NULL };
+     *     conf->preread = { 0, NULL };
+     *     conf->filter = { 0, NULL };
      */
 
     return conf;
@@ -623,5 +1130,38 @@ ngx_stream_js_merge_srv_conf(ngx_conf_t 
         conf->arg = prev->arg;
     }
 
+    ngx_conf_merge_str_value(conf->access, prev->access, "");
+    ngx_conf_merge_str_value(conf->preread, prev->preread, "");
+    ngx_conf_merge_str_value(conf->filter, prev->filter, "");
+
     return NGX_CONF_OK;
 }
+
+
+static ngx_int_t
+ngx_stream_js_init(ngx_conf_t *cf)
+{
+    ngx_stream_handler_pt        *h;
+    ngx_stream_core_main_conf_t  *cmcf;
+
+    ngx_stream_next_filter = ngx_stream_top_filter;
+    ngx_stream_top_filter = ngx_stream_js_body_filter;
+
+    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
+
+    h = ngx_array_push(&cmcf->phases[NGX_STREAM_ACCESS_PHASE].handlers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
+
+    *h = ngx_stream_js_access_handler;
+
+    h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREREAD_PHASE].handlers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
+
+    *h = ngx_stream_js_preread_handler;
+
+    return NGX_OK;
+}



More information about the nginx-devel mailing list