1 
2 /*
3  * Copyright (C) Roman Arutyunyan
4  * Copyright (C) Dmitry Volyntsev
5  * Copyright (C) NGINX, Inc.
6  */
7 
8 
9 #include <ngx_config.h>
10 #include <ngx_core.h>
11 #include <ngx_stream.h>
12 #include "ngx_js.h"
13 
14 
15 typedef struct {
16     njs_vm_t              *vm;
17     ngx_array_t           *imports;
18     ngx_array_t           *paths;
19 } ngx_stream_js_main_conf_t;
20 
21 
22 typedef struct {
23     ngx_str_t              name;
24     ngx_str_t              path;
25     u_char                *file;
26     ngx_uint_t             line;
27 } ngx_stream_js_import_t;
28 
29 
30 typedef struct {
31     ngx_str_t              access;
32     ngx_str_t              preread;
33     ngx_str_t              filter;
34 #if (NGX_STREAM_SSL)
35     ngx_ssl_t             *ssl;
36     ngx_str_t              ssl_ciphers;
37     ngx_uint_t             ssl_protocols;
38     ngx_int_t              ssl_verify_depth;
39     ngx_str_t              ssl_trusted_certificate;
40 #endif
41 } ngx_stream_js_srv_conf_t;
42 
43 
44 typedef struct {
45     njs_vm_event_t          ev;
46     ngx_uint_t              data_type;
47 } ngx_stream_js_ev_t;
48 
49 
50 typedef struct {
51     njs_vm_t               *vm;
52     njs_opaque_value_t      retval;
53     njs_opaque_value_t      args[3];
54     ngx_buf_t              *buf;
55     ngx_chain_t           **last_out;
56     ngx_chain_t            *free;
57     ngx_chain_t            *upstream_busy;
58     ngx_chain_t            *downstream_busy;
59     ngx_int_t               status;
60 #define NGX_JS_EVENT_UPLOAD   0
61 #define NGX_JS_EVENT_DOWNLOAD 1
62 #define NGX_JS_EVENT_MAX      2
63     ngx_stream_js_ev_t      events[2];
64     unsigned                from_upstream:1;
65     unsigned                filter:1;
66     unsigned                in_progress:1;
67 } ngx_stream_js_ctx_t;
68 
69 
70 typedef struct {
71     ngx_stream_session_t  *session;
72     njs_vm_event_t         vm_event;
73     void                  *unused;
74     ngx_int_t              ident;
75 } ngx_stream_js_event_t;
76 
77 
78 static ngx_int_t ngx_stream_js_access_handler(ngx_stream_session_t *s);
79 static ngx_int_t ngx_stream_js_preread_handler(ngx_stream_session_t *s);
80 static ngx_int_t ngx_stream_js_phase_handler(ngx_stream_session_t *s,
81     ngx_str_t *name);
82 static ngx_int_t ngx_stream_js_body_filter(ngx_stream_session_t *s,
83     ngx_chain_t *in, ngx_uint_t from_upstream);
84 static ngx_int_t ngx_stream_js_variable_set(ngx_stream_session_t *s,
85     ngx_stream_variable_value_t *v, uintptr_t data);
86 static ngx_int_t ngx_stream_js_variable_var(ngx_stream_session_t *s,
87     ngx_stream_variable_value_t *v, uintptr_t data);
88 static ngx_int_t ngx_stream_js_init_vm(ngx_stream_session_t *s);
89 static void ngx_stream_js_drop_events(ngx_stream_js_ctx_t *ctx);
90 static void ngx_stream_js_cleanup(void *data);
91 static void ngx_stream_js_cleanup_vm(void *data);
92 static njs_int_t ngx_stream_js_run_event(ngx_stream_session_t *s,
93     ngx_stream_js_ctx_t *ctx, ngx_stream_js_ev_t *event);
94 static njs_vm_event_t *ngx_stream_js_event(ngx_stream_session_t *s,
95     njs_str_t *event);
96 
97 static njs_int_t ngx_stream_js_ext_get_remote_address(njs_vm_t *vm,
98     njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
99     njs_value_t *retval);
100 
101 static njs_int_t ngx_stream_js_ext_done(njs_vm_t *vm, njs_value_t *args,
102      njs_uint_t nargs, njs_index_t unused);
103 
104 static njs_int_t ngx_stream_js_ext_on(njs_vm_t *vm, njs_value_t *args,
105      njs_uint_t nargs, njs_index_t unused);
106 static njs_int_t ngx_stream_js_ext_off(njs_vm_t *vm, njs_value_t *args,
107      njs_uint_t nargs, njs_index_t unused);
108 static njs_int_t ngx_stream_js_ext_send(njs_vm_t *vm, njs_value_t *args,
109      njs_uint_t nargs, njs_index_t unused);
110 static njs_int_t ngx_stream_js_ext_set_return_value(njs_vm_t *vm,
111     njs_value_t *args, njs_uint_t nargs, njs_index_t unused);
112 
113 static njs_int_t ngx_stream_js_ext_variables(njs_vm_t *vm,
114     njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
115     njs_value_t *retval);
116 
117 static njs_host_event_t ngx_stream_js_set_timer(njs_external_ptr_t external,
118     uint64_t delay, njs_vm_event_t vm_event);
119 static void ngx_stream_js_clear_timer(njs_external_ptr_t external,
120     njs_host_event_t event);
121 static void ngx_stream_js_timer_handler(ngx_event_t *ev);
122 static ngx_pool_t *ngx_stream_js_pool(njs_vm_t *vm, ngx_stream_session_t *s);
123 static ngx_resolver_t *ngx_stream_js_resolver(njs_vm_t *vm,
124     ngx_stream_session_t *s);
125 static ngx_msec_t ngx_stream_js_resolver_timeout(njs_vm_t *vm,
126     ngx_stream_session_t *s);
127 static void ngx_stream_js_handle_event(ngx_stream_session_t *s,
128     njs_vm_event_t vm_event, njs_value_t *args, njs_uint_t nargs);
129 
130 static char *ngx_stream_js_import(ngx_conf_t *cf, ngx_command_t *cmd,
131     void *conf);
132 static char *ngx_stream_js_set(ngx_conf_t *cf, ngx_command_t *cmd,
133     void *conf);
134 static char *ngx_stream_js_var(ngx_conf_t *cf, ngx_command_t *cmd,
135     void *conf);
136 static void *ngx_stream_js_create_main_conf(ngx_conf_t *cf);
137 static char *ngx_stream_js_init_main_conf(ngx_conf_t *cf, void *conf);
138 static void *ngx_stream_js_create_srv_conf(ngx_conf_t *cf);
139 static char *ngx_stream_js_merge_srv_conf(ngx_conf_t *cf, void *parent,
140     void *child);
141 static ngx_int_t ngx_stream_js_init(ngx_conf_t *cf);
142 
143 #if (NGX_STREAM_SSL)
144 static char * ngx_stream_js_set_ssl(ngx_conf_t *cf,
145     ngx_stream_js_srv_conf_t *jscf);
146 #endif
147 static ngx_ssl_t *ngx_stream_js_ssl(njs_vm_t *vm, ngx_stream_session_t *s);
148 
149 #if (NGX_STREAM_SSL)
150 
151 static ngx_conf_bitmask_t  ngx_stream_js_ssl_protocols[] = {
152     { ngx_string("TLSv1"), NGX_SSL_TLSv1 },
153     { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
154     { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
155     { ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 },
156     { ngx_null_string, 0 }
157 };
158 
159 #endif
160 
161 static ngx_command_t  ngx_stream_js_commands[] = {
162 
163     { ngx_string("js_import"),
164       NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE13,
165       ngx_stream_js_import,
166       NGX_STREAM_MAIN_CONF_OFFSET,
167       0,
168       NULL },
169 
170     { ngx_string("js_path"),
171       NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
172       ngx_conf_set_str_array_slot,
173       NGX_STREAM_MAIN_CONF_OFFSET,
174       offsetof(ngx_stream_js_main_conf_t, paths),
175       NULL },
176 
177     { ngx_string("js_set"),
178       NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE2,
179       ngx_stream_js_set,
180       0,
181       0,
182       NULL },
183 
184     { ngx_string("js_var"),
185       NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12,
186       ngx_stream_js_var,
187       0,
188       0,
189       NULL },
190 
191     { ngx_string("js_access"),
192       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
193       ngx_conf_set_str_slot,
194       NGX_STREAM_SRV_CONF_OFFSET,
195       offsetof(ngx_stream_js_srv_conf_t, access),
196       NULL },
197 
198     { ngx_string("js_preread"),
199       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
200       ngx_conf_set_str_slot,
201       NGX_STREAM_SRV_CONF_OFFSET,
202       offsetof(ngx_stream_js_srv_conf_t, preread),
203       NULL },
204 
205     { ngx_string("js_filter"),
206       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
207       ngx_conf_set_str_slot,
208       NGX_STREAM_SRV_CONF_OFFSET,
209       offsetof(ngx_stream_js_srv_conf_t, filter),
210       NULL },
211 
212 #if (NGX_STREAM_SSL)
213 
214     { ngx_string("js_fetch_ciphers"),
215       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
216       ngx_conf_set_str_slot,
217       NGX_STREAM_SRV_CONF_OFFSET,
218       offsetof(ngx_stream_js_srv_conf_t, ssl_ciphers),
219       NULL },
220 
221     { ngx_string("js_fetch_protocols"),
222       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
223       ngx_conf_set_bitmask_slot,
224       NGX_STREAM_SRV_CONF_OFFSET,
225       offsetof(ngx_stream_js_srv_conf_t, ssl_protocols),
226       &ngx_stream_js_ssl_protocols },
227 
228     { ngx_string("js_fetch_verify_depth"),
229       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
230       ngx_conf_set_num_slot,
231       NGX_STREAM_SRV_CONF_OFFSET,
232       offsetof(ngx_stream_js_srv_conf_t, ssl_verify_depth),
233       NULL },
234 
235     { ngx_string("js_fetch_trusted_certificate"),
236       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
237       ngx_conf_set_str_slot,
238       NGX_STREAM_SRV_CONF_OFFSET,
239       offsetof(ngx_stream_js_srv_conf_t, ssl_trusted_certificate),
240       NULL },
241 
242 #endif
243 
244       ngx_null_command
245 };
246 
247 
248 static ngx_stream_module_t  ngx_stream_js_module_ctx = {
249     NULL,                           /* preconfiguration */
250     ngx_stream_js_init,             /* postconfiguration */
251 
252     ngx_stream_js_create_main_conf, /* create main configuration */
253     ngx_stream_js_init_main_conf,   /* init main configuration */
254 
255     ngx_stream_js_create_srv_conf,  /* create server configuration */
256     ngx_stream_js_merge_srv_conf,   /* merge server configuration */
257 };
258 
259 
260 ngx_module_t  ngx_stream_js_module = {
261     NGX_MODULE_V1,
262     &ngx_stream_js_module_ctx,      /* module context */
263     ngx_stream_js_commands,         /* module directives */
264     NGX_STREAM_MODULE,              /* module type */
265     NULL,                           /* init master */
266     NULL,                           /* init module */
267     NULL,                           /* init process */
268     NULL,                           /* init thread */
269     NULL,                           /* exit thread */
270     NULL,                           /* exit process */
271     NULL,                           /* exit master */
272     NGX_MODULE_V1_PADDING
273 };
274 
275 
276 static njs_external_t  ngx_stream_js_ext_session[] = {
277 
278     {
279         .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
280         .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
281         .u.property = {
282             .value = "Stream Session",
283         }
284     },
285 
286     {
287         .flags = NJS_EXTERN_PROPERTY,
288         .name.string = njs_str("status"),
289         .enumerable = 1,
290         .u.property = {
291             .handler = ngx_js_ext_uint,
292             .magic32 = offsetof(ngx_stream_session_t, status),
293         }
294     },
295 
296     {
297         .flags = NJS_EXTERN_PROPERTY,
298         .name.string = njs_str("remoteAddress"),
299         .enumerable = 1,
300         .u.property = {
301             .handler = ngx_stream_js_ext_get_remote_address,
302         }
303     },
304 
305     {
306         .flags = NJS_EXTERN_OBJECT,
307         .name.string = njs_str("variables"),
308         .u.object = {
309             .writable = 1,
310             .prop_handler = ngx_stream_js_ext_variables,
311             .magic32 = NGX_JS_STRING,
312         }
313     },
314 
315     {
316         .flags = NJS_EXTERN_OBJECT,
317         .name.string = njs_str("rawVariables"),
318         .u.object = {
319             .writable = 1,
320             .prop_handler = ngx_stream_js_ext_variables,
321             .magic32 = NGX_JS_BUFFER,
322         }
323     },
324 
325     {
326         .flags = NJS_EXTERN_METHOD,
327         .name.string = njs_str("allow"),
328         .writable = 1,
329         .configurable = 1,
330         .enumerable = 1,
331         .u.method = {
332             .native = ngx_stream_js_ext_done,
333             .magic8 = NGX_OK,
334         }
335     },
336 
337     {
338         .flags = NJS_EXTERN_METHOD,
339         .name.string = njs_str("deny"),
340         .writable = 1,
341         .configurable = 1,
342         .enumerable = 1,
343         .u.method = {
344             .native = ngx_stream_js_ext_done,
345             .magic8 = -NGX_DONE,
346         }
347     },
348 
349     {
350         .flags = NJS_EXTERN_METHOD,
351         .name.string = njs_str("decline"),
352         .writable = 1,
353         .configurable = 1,
354         .enumerable = 1,
355         .u.method = {
356             .native = ngx_stream_js_ext_done,
357             .magic8 = -NGX_DECLINED,
358         }
359     },
360 
361     {
362         .flags = NJS_EXTERN_METHOD,
363         .name.string = njs_str("done"),
364         .writable = 1,
365         .configurable = 1,
366         .enumerable = 1,
367         .u.method = {
368             .native = ngx_stream_js_ext_done,
369             .magic8 = NGX_OK,
370 
371         }
372     },
373 
374     {
375         .flags = NJS_EXTERN_METHOD,
376         .name.string = njs_str("log"),
377         .writable = 1,
378         .configurable = 1,
379         .enumerable = 1,
380         .u.method = {
381             .native = ngx_js_ext_log,
382             .magic8 = NGX_LOG_INFO,
383         }
384     },
385 
386     {
387         .flags = NJS_EXTERN_METHOD,
388         .name.string = njs_str("warn"),
389         .writable = 1,
390         .configurable = 1,
391         .enumerable = 1,
392         .u.method = {
393             .native = ngx_js_ext_log,
394             .magic8 = NGX_LOG_WARN,
395         }
396     },
397 
398     {
399         .flags = NJS_EXTERN_METHOD,
400         .name.string = njs_str("error"),
401         .writable = 1,
402         .configurable = 1,
403         .enumerable = 1,
404         .u.method = {
405             .native = ngx_js_ext_log,
406             .magic8 = NGX_LOG_ERR,
407         }
408     },
409 
410     {
411         .flags = NJS_EXTERN_METHOD,
412         .name.string = njs_str("on"),
413         .writable = 1,
414         .configurable = 1,
415         .enumerable = 1,
416         .u.method = {
417             .native = ngx_stream_js_ext_on,
418         }
419     },
420 
421     {
422         .flags = NJS_EXTERN_METHOD,
423         .name.string = njs_str("off"),
424         .writable = 1,
425         .configurable = 1,
426         .enumerable = 1,
427         .u.method = {
428             .native = ngx_stream_js_ext_off,
429         }
430     },
431 
432     {
433         .flags = NJS_EXTERN_METHOD,
434         .name.string = njs_str("send"),
435         .writable = 1,
436         .configurable = 1,
437         .enumerable = 1,
438         .u.method = {
439             .native = ngx_stream_js_ext_send,
440         }
441     },
442 
443     {
444         .flags = NJS_EXTERN_METHOD,
445         .name.string = njs_str("setReturnValue"),
446         .writable = 1,
447         .configurable = 1,
448         .enumerable = 1,
449         .u.method = {
450             .native = ngx_stream_js_ext_set_return_value,
451         }
452     },
453 
454 };
455 
456 
457 static njs_vm_ops_t ngx_stream_js_ops = {
458     ngx_stream_js_set_timer,
459     ngx_stream_js_clear_timer
460 };
461 
462 
463 static uintptr_t ngx_stream_js_uptr[] = {
464     offsetof(ngx_stream_session_t, connection),
465     (uintptr_t) ngx_stream_js_pool,
466     (uintptr_t) ngx_stream_js_resolver,
467     (uintptr_t) ngx_stream_js_resolver_timeout,
468     (uintptr_t) ngx_stream_js_handle_event,
469     (uintptr_t) ngx_stream_js_ssl,
470 };
471 
472 
473 static njs_vm_meta_t ngx_stream_js_metas = {
474     .size = 6,
475     .values = ngx_stream_js_uptr
476 };
477 
478 
479 static ngx_stream_filter_pt  ngx_stream_next_filter;
480 
481 
482 static njs_int_t    ngx_stream_js_session_proto_id;
483 
484 
485 static ngx_int_t
ngx_stream_js_access_handler(ngx_stream_session_t * s)486 ngx_stream_js_access_handler(ngx_stream_session_t *s)
487 {
488     ngx_stream_js_srv_conf_t  *jscf;
489 
490     ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
491                    "js access handler");
492 
493     jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module);
494 
495     return ngx_stream_js_phase_handler(s, &jscf->access);
496 }
497 
498 
499 static ngx_int_t
ngx_stream_js_preread_handler(ngx_stream_session_t * s)500 ngx_stream_js_preread_handler(ngx_stream_session_t *s)
501 {
502     ngx_stream_js_srv_conf_t  *jscf;
503 
504     ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
505                    "js preread handler");
506 
507     jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module);
508 
509     return ngx_stream_js_phase_handler(s, &jscf->preread);
510 }
511 
512 
513 static ngx_int_t
ngx_stream_js_phase_handler(ngx_stream_session_t * s,ngx_str_t * name)514 ngx_stream_js_phase_handler(ngx_stream_session_t *s, ngx_str_t *name)
515 {
516     ngx_str_t             exception;
517     njs_int_t             ret;
518     ngx_int_t             rc;
519     ngx_connection_t     *c;
520     ngx_stream_js_ctx_t  *ctx;
521 
522     if (name->len == 0) {
523         return NGX_DECLINED;
524     }
525 
526     rc = ngx_stream_js_init_vm(s);
527     if (rc != NGX_OK) {
528         return rc;
529     }
530 
531     c = s->connection;
532 
533     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
534                    "stream js phase call \"%V\"", name);
535 
536     ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
537 
538     if (!ctx->in_progress) {
539         /*
540          * status is expected to be overriden by allow(), deny(), decline() or
541          * done() methods.
542          */
543 
544         ctx->status = NGX_ERROR;
545 
546         rc = ngx_js_call(ctx->vm, name, c->log, &ctx->args[0], 1);
547 
548         if (rc == NGX_ERROR) {
549             return rc;
550         }
551     }
552 
553     ret = ngx_stream_js_run_event(s, ctx, &ctx->events[NGX_JS_EVENT_UPLOAD]);
554     if (ret != NJS_OK) {
555         ngx_js_retval(ctx->vm, NULL, &exception);
556 
557         ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %V",
558                       &exception);
559 
560         return NGX_ERROR;
561     }
562 
563     if (njs_vm_pending(ctx->vm)) {
564         ctx->in_progress = 1;
565         rc = ctx->events[NGX_JS_EVENT_UPLOAD].ev ? NGX_AGAIN : NGX_DONE;
566 
567     } else {
568         ctx->in_progress = 0;
569         rc = ctx->status;
570     }
571 
572     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "stream js phase rc: %i",
573                    rc);
574 
575     return rc;
576 }
577 
578 
579 #define ngx_stream_event(from_upstream)                                 \
580     (from_upstream ? &ctx->events[NGX_JS_EVENT_DOWNLOAD]                \
581                    : &ctx->events[NGX_JS_EVENT_UPLOAD])
582 
583 
584 static ngx_int_t
ngx_stream_js_body_filter(ngx_stream_session_t * s,ngx_chain_t * in,ngx_uint_t from_upstream)585 ngx_stream_js_body_filter(ngx_stream_session_t *s, ngx_chain_t *in,
586     ngx_uint_t from_upstream)
587 {
588     ngx_str_t                  exception;
589     njs_int_t                  ret;
590     ngx_int_t                  rc;
591     ngx_chain_t               *out, *cl, **busy;
592     ngx_connection_t          *c, *dst;
593     ngx_stream_js_ev_t        *event;
594     ngx_stream_js_ctx_t       *ctx;
595     ngx_stream_js_srv_conf_t  *jscf;
596 
597     jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module);
598     if (jscf->filter.len == 0) {
599         return ngx_stream_next_filter(s, in, from_upstream);
600     }
601 
602     c = s->connection;
603 
604     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "stream js filter u:%ui",
605                    from_upstream);
606 
607     rc = ngx_stream_js_init_vm(s);
608 
609     if (rc == NGX_ERROR) {
610         return NGX_ERROR;
611     }
612 
613     if (rc == NGX_DECLINED) {
614         return ngx_stream_next_filter(s, in, from_upstream);
615     }
616 
617     ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
618 
619     if (!ctx->filter) {
620         rc = ngx_js_call(ctx->vm, &jscf->filter, c->log, &ctx->args[0], 1);
621 
622         if (rc == NGX_ERROR) {
623             return rc;
624         }
625     }
626 
627     ctx->filter = 1;
628     ctx->from_upstream = from_upstream;
629 
630     ctx->last_out = &out;
631 
632     while (in) {
633         ctx->buf = in->buf;
634 
635         event = ngx_stream_event(from_upstream);
636 
637         if (event->ev != NULL) {
638             ret = ngx_stream_js_run_event(s, ctx, event);
639             if (ret != NJS_OK) {
640                 ngx_js_retval(ctx->vm, NULL, &exception);
641 
642                 ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %V",
643                               &exception);
644 
645                 return NGX_ERROR;
646             }
647 
648             ctx->buf->pos = ctx->buf->last;
649 
650         } else {
651             cl = ngx_alloc_chain_link(c->pool);
652             if (cl == NULL) {
653                 return NGX_ERROR;
654             }
655 
656             cl->buf = ctx->buf;
657 
658             *ctx->last_out = cl;
659             ctx->last_out = &cl->next;
660         }
661 
662         in = in->next;
663     }
664 
665     *ctx->last_out = NULL;
666 
667     if (from_upstream) {
668         dst = c;
669         busy = &ctx->downstream_busy;
670 
671     } else {
672         dst = s->upstream ? s->upstream->peer.connection : NULL;
673         busy = &ctx->upstream_busy;
674     }
675 
676     if (out != NULL || dst == NULL || dst->buffered) {
677         rc = ngx_stream_next_filter(s, out, from_upstream);
678 
679         ngx_chain_update_chains(c->pool, &ctx->free, busy, &out,
680                                 (ngx_buf_tag_t) &ngx_stream_js_module);
681 
682     } else {
683         rc = NGX_OK;
684     }
685 
686     return rc;
687 }
688 
689 
690 static ngx_int_t
ngx_stream_js_variable_set(ngx_stream_session_t * s,ngx_stream_variable_value_t * v,uintptr_t data)691 ngx_stream_js_variable_set(ngx_stream_session_t *s,
692     ngx_stream_variable_value_t *v, uintptr_t data)
693 {
694     ngx_str_t *fname = (ngx_str_t *) data;
695 
696     ngx_int_t             rc;
697     njs_int_t             pending;
698     ngx_str_t             value;
699     ngx_stream_js_ctx_t  *ctx;
700 
701     rc = ngx_stream_js_init_vm(s);
702 
703     if (rc == NGX_ERROR) {
704         return NGX_ERROR;
705     }
706 
707     if (rc == NGX_DECLINED) {
708         v->not_found = 1;
709         return NGX_OK;
710     }
711 
712     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
713                    "stream js variable call \"%V\"", fname);
714 
715     ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
716 
717     pending = njs_vm_pending(ctx->vm);
718 
719     rc = ngx_js_call(ctx->vm, fname, s->connection->log, &ctx->args[0], 1);
720 
721     if (rc == NGX_ERROR) {
722         v->not_found = 1;
723         return NGX_OK;
724     }
725 
726     if (!pending && rc == NGX_AGAIN) {
727         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
728                       "async operation inside \"%V\" variable handler", fname);
729         return NGX_ERROR;
730     }
731 
732     if (ngx_js_retval(ctx->vm, &ctx->retval, &value) != NGX_OK) {
733         return NGX_ERROR;
734     }
735 
736     v->len = value.len;
737     v->valid = 1;
738     v->no_cacheable = 0;
739     v->not_found = 0;
740     v->data = value.data;
741 
742     return NGX_OK;
743 }
744 
745 
746 static ngx_int_t
ngx_stream_js_variable_var(ngx_stream_session_t * s,ngx_stream_variable_value_t * v,uintptr_t data)747 ngx_stream_js_variable_var(ngx_stream_session_t *s,
748     ngx_stream_variable_value_t *v, uintptr_t data)
749 {
750     ngx_stream_complex_value_t *cv = (ngx_stream_complex_value_t *) data;
751 
752     ngx_str_t  value;
753 
754     if (cv != NULL) {
755         if (ngx_stream_complex_value(s, cv, &value) != NGX_OK) {
756             return NGX_ERROR;
757         }
758 
759     } else {
760         ngx_str_null(&value);
761     }
762 
763     v->len = value.len;
764     v->valid = 1;
765     v->no_cacheable = 0;
766     v->not_found = 0;
767     v->data = value.data;
768 
769     return NGX_OK;
770 }
771 
772 
773 static ngx_int_t
ngx_stream_js_init_vm(ngx_stream_session_t * s)774 ngx_stream_js_init_vm(ngx_stream_session_t *s)
775 {
776     njs_int_t                   rc;
777     ngx_str_t                   exception;
778     ngx_pool_cleanup_t         *cln;
779     ngx_stream_js_ctx_t        *ctx;
780     ngx_stream_js_main_conf_t  *jmcf;
781 
782     jmcf = ngx_stream_get_module_main_conf(s, ngx_stream_js_module);
783     if (jmcf->vm == NULL) {
784         return NGX_DECLINED;
785     }
786 
787     ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
788 
789     if (ctx == NULL) {
790         ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_stream_js_ctx_t));
791         if (ctx == NULL) {
792             return NGX_ERROR;
793         }
794 
795         njs_value_invalid_set(njs_value_arg(&ctx->retval));
796 
797         ngx_stream_set_ctx(s, ctx, ngx_stream_js_module);
798     }
799 
800     if (ctx->vm) {
801         return NGX_OK;
802     }
803 
804     ctx->vm = njs_vm_clone(jmcf->vm, s);
805     if (ctx->vm == NULL) {
806         return NGX_ERROR;
807     }
808 
809     cln = ngx_pool_cleanup_add(s->connection->pool, 0);
810     if (cln == NULL) {
811         return NGX_ERROR;
812     }
813 
814     cln->handler = ngx_stream_js_cleanup;
815     cln->data = s;
816 
817     if (njs_vm_start(ctx->vm) == NJS_ERROR) {
818         ngx_js_retval(ctx->vm, NULL, &exception);
819 
820         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
821                       "js exception: %V", &exception);
822 
823         return NGX_ERROR;
824     }
825 
826     rc = njs_vm_external_create(ctx->vm, njs_value_arg(&ctx->args[0]),
827                                 ngx_stream_js_session_proto_id, s, 0);
828     if (rc != NJS_OK) {
829         return NGX_ERROR;
830     }
831 
832     return NGX_OK;
833 }
834 
835 
836 static void
ngx_stream_js_drop_events(ngx_stream_js_ctx_t * ctx)837 ngx_stream_js_drop_events(ngx_stream_js_ctx_t *ctx)
838 {
839     ngx_uint_t  i;
840 
841     for (i = 0; i < NGX_JS_EVENT_MAX; i++) {
842         if (ctx->events[i].ev != NULL) {
843             njs_vm_del_event(ctx->vm, ctx->events[i].ev);
844             ctx->events[i].ev = NULL;
845         }
846     }
847 }
848 
849 
850 static void
ngx_stream_js_cleanup(void * data)851 ngx_stream_js_cleanup(void *data)
852 {
853     ngx_stream_js_ctx_t  *ctx;
854 
855     ngx_stream_session_t *s = data;
856 
857     ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
858 
859     ngx_stream_js_drop_events(ctx);
860 
861     if (njs_vm_pending(ctx->vm)) {
862         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "pending events");
863     }
864 
865     njs_vm_destroy(ctx->vm);
866 }
867 
868 
869 static void
ngx_stream_js_cleanup_vm(void * data)870 ngx_stream_js_cleanup_vm(void *data)
871 {
872     njs_vm_t *vm = data;
873 
874     njs_vm_destroy(vm);
875 }
876 
877 
878 static njs_int_t
ngx_stream_js_run_event(ngx_stream_session_t * s,ngx_stream_js_ctx_t * ctx,ngx_stream_js_ev_t * event)879 ngx_stream_js_run_event(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx,
880     ngx_stream_js_ev_t *event)
881 {
882     size_t               len;
883     u_char              *p;
884     njs_int_t            ret;
885     ngx_buf_t           *b;
886     ngx_connection_t    *c;
887     njs_opaque_value_t   last_key, last;
888 
889     static const njs_str_t last_str = njs_str("last");
890 
891     if (event->ev == NULL) {
892         return NJS_OK;
893     }
894 
895     c = s->connection;
896     b = ctx->filter ? ctx->buf : c->buffer;
897 
898     len = b ? b->last - b->pos : 0;
899 
900     p = ngx_pnalloc(c->pool, len);
901     if (p == NULL) {
902         njs_vm_memory_error(ctx->vm);
903         return NJS_ERROR;
904     }
905 
906     if (len) {
907         ngx_memcpy(p, b->pos, len);
908     }
909 
910     ret = ngx_js_prop(ctx->vm, event->data_type, njs_value_arg(&ctx->args[1]),
911                       p, len);
912     if (ret != NJS_OK) {
913         return ret;
914     }
915 
916     njs_vm_value_string_set(ctx->vm, njs_value_arg(&last_key), last_str.start,
917                             last_str.length);
918 
919     njs_value_boolean_set(njs_value_arg(&last), b && b->last_buf);
920 
921     ret = njs_vm_object_alloc(ctx->vm, njs_value_arg(&ctx->args[2]),
922                                njs_value_arg(&last_key),
923                                njs_value_arg(&last), NULL);
924     if (ret != NJS_OK) {
925         return ret;
926     }
927 
928     njs_vm_post_event(ctx->vm, event->ev, njs_value_arg(&ctx->args[1]), 2);
929 
930     ret = njs_vm_run(ctx->vm);
931     if (ret == NJS_ERROR) {
932         return ret;
933     }
934 
935     return NJS_OK;
936 }
937 
938 
939 static njs_vm_event_t *
ngx_stream_js_event(ngx_stream_session_t * s,njs_str_t * event)940 ngx_stream_js_event(ngx_stream_session_t *s, njs_str_t *event)
941 {
942     ngx_uint_t            i, n, type;
943     ngx_stream_js_ctx_t  *ctx;
944 
945     static const struct {
946         ngx_str_t   name;
947         ngx_uint_t  data_type;
948         ngx_uint_t  id;
949     } events[] = {
950         {
951             ngx_string("upload"),
952             NGX_JS_STRING,
953             NGX_JS_EVENT_UPLOAD,
954         },
955 
956         {
957             ngx_string("download"),
958             NGX_JS_STRING,
959             NGX_JS_EVENT_DOWNLOAD,
960         },
961 
962         {
963             ngx_string("upstream"),
964             NGX_JS_BUFFER,
965             NGX_JS_EVENT_UPLOAD,
966         },
967 
968         {
969             ngx_string("downstream"),
970             NGX_JS_BUFFER,
971             NGX_JS_EVENT_DOWNLOAD,
972         },
973     };
974 
975     ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
976 
977     i = 0;
978     n = sizeof(events) / sizeof(events[0]);
979 
980     while (i < n) {
981         if (event->length == events[i].name.len
982             && ngx_memcmp(event->start, events[i].name.data, event->length)
983                == 0)
984         {
985             break;
986         }
987 
988         i++;
989     }
990 
991     if (i == n) {
992         njs_vm_error(ctx->vm, "unknown event \"%V\"", event);
993         return NULL;
994     }
995 
996     ctx->events[events[i].id].data_type = events[i].data_type;
997 
998     for (n = 0; n < NGX_JS_EVENT_MAX; n++) {
999         type = ctx->events[n].data_type;
1000         if (type != NGX_JS_UNSET && type != events[i].data_type) {
1001             njs_vm_error(ctx->vm, "mixing string and buffer events"
1002                          " is not allowed");
1003             return NULL;
1004         }
1005     }
1006 
1007     return &ctx->events[events[i].id].ev;
1008 }
1009 
1010 
1011 static njs_int_t
ngx_stream_js_ext_get_remote_address(njs_vm_t * vm,njs_object_prop_t * prop,njs_value_t * value,njs_value_t * setval,njs_value_t * retval)1012 ngx_stream_js_ext_get_remote_address(njs_vm_t *vm,
1013     njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
1014     njs_value_t *retval)
1015 {
1016     ngx_connection_t      *c;
1017     ngx_stream_session_t  *s;
1018 
1019     s = njs_vm_external(vm, ngx_stream_js_session_proto_id, value);
1020     if (s == NULL) {
1021         njs_value_undefined_set(retval);
1022         return NJS_DECLINED;
1023     }
1024 
1025     c = s->connection;
1026 
1027     return njs_vm_value_string_set(vm, retval, c->addr_text.data,
1028                                    c->addr_text.len);
1029 }
1030 
1031 
1032 static njs_int_t
ngx_stream_js_ext_done(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t magic)1033 ngx_stream_js_ext_done(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1034     njs_index_t magic)
1035 {
1036     ngx_int_t              status;
1037     njs_value_t           *code;
1038     ngx_stream_js_ctx_t   *ctx;
1039     ngx_stream_session_t  *s;
1040 
1041     s = njs_vm_external(vm, ngx_stream_js_session_proto_id,
1042                         njs_argument(args, 0));
1043     if (s == NULL) {
1044         njs_vm_error(vm, "\"this\" is not an external");
1045         return NJS_ERROR;
1046     }
1047 
1048     status = (ngx_int_t) magic;
1049     status = -status;
1050 
1051     if (status == NGX_DONE) {
1052         status = NGX_STREAM_FORBIDDEN;
1053     }
1054 
1055     code = njs_arg(args, nargs, 1);
1056 
1057     if (!njs_value_is_undefined(code)) {
1058         if (ngx_js_integer(vm, code, &status) != NGX_OK) {
1059             return NJS_ERROR;
1060         }
1061 
1062         if (status < NGX_ABORT || status > NGX_STREAM_SERVICE_UNAVAILABLE) {
1063             njs_vm_error(vm, "code is out of range");
1064             return NJS_ERROR;
1065         }
1066     }
1067 
1068 
1069     ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
1070 
1071     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
1072                    "stream js set status: %i", status);
1073 
1074     ctx->status = status;
1075 
1076     ngx_stream_js_drop_events(ctx);
1077 
1078     njs_value_undefined_set(njs_vm_retval(vm));
1079 
1080     return NJS_OK;
1081 }
1082 
1083 
1084 static njs_int_t
ngx_stream_js_ext_on(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1085 ngx_stream_js_ext_on(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1086     njs_index_t unused)
1087 {
1088     njs_str_t              name;
1089     njs_value_t           *callback;
1090     njs_vm_event_t        *event;
1091     ngx_stream_session_t  *s;
1092 
1093     s = njs_vm_external(vm, ngx_stream_js_session_proto_id,
1094                         njs_argument(args, 0));
1095     if (s == NULL) {
1096         njs_vm_error(vm, "\"this\" is not an external");
1097         return NJS_ERROR;
1098     }
1099 
1100     if (ngx_js_string(vm, njs_arg(args, nargs, 1), &name) == NJS_ERROR) {
1101         njs_vm_error(vm, "failed to convert event arg");
1102         return NJS_ERROR;
1103     }
1104 
1105     callback = njs_arg(args, nargs, 2);
1106     if (!njs_value_is_function(callback)) {
1107         njs_vm_error(vm, "callback is not a function");
1108         return NJS_ERROR;
1109     }
1110 
1111     event = ngx_stream_js_event(s, &name);
1112     if (event == NULL) {
1113         return NJS_ERROR;
1114     }
1115 
1116     if (*event != NULL) {
1117         njs_vm_error(vm, "event handler \"%V\" is already set", &name);
1118         return NJS_ERROR;
1119     }
1120 
1121     *event = njs_vm_add_event(vm, njs_value_function(callback), 0, NULL, NULL);
1122     if (*event == NULL) {
1123         njs_vm_error(vm, "internal error");
1124         return NJS_ERROR;
1125     }
1126 
1127     njs_value_undefined_set(njs_vm_retval(vm));
1128 
1129     return NJS_OK;
1130 }
1131 
1132 
1133 static njs_int_t
ngx_stream_js_ext_off(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1134 ngx_stream_js_ext_off(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1135     njs_index_t unused)
1136 {
1137     njs_str_t              name;
1138     njs_vm_event_t        *event;
1139     ngx_stream_session_t  *s;
1140 
1141     s = njs_vm_external(vm, ngx_stream_js_session_proto_id,
1142                         njs_argument(args, 0));
1143     if (s == NULL) {
1144         njs_vm_error(vm, "\"this\" is not an external");
1145         return NJS_ERROR;
1146     }
1147 
1148     if (ngx_js_string(vm, njs_arg(args, nargs, 1), &name) == NJS_ERROR) {
1149         njs_vm_error(vm, "failed to convert event arg");
1150         return NJS_ERROR;
1151     }
1152 
1153     event = ngx_stream_js_event(s, &name);
1154     if (event == NULL) {
1155         return NJS_ERROR;
1156     }
1157 
1158     njs_vm_del_event(vm, *event);
1159 
1160     *event = NULL;
1161 
1162     njs_value_undefined_set(njs_vm_retval(vm));
1163 
1164     return NJS_OK;
1165 }
1166 
1167 
1168 static njs_int_t
ngx_stream_js_ext_send(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1169 ngx_stream_js_ext_send(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1170     njs_index_t unused)
1171 {
1172     unsigned               last_buf, flush;
1173     njs_str_t              buffer;
1174     ngx_buf_t             *b;
1175     njs_value_t           *flags, *value;
1176     ngx_chain_t           *cl;
1177     ngx_connection_t      *c;
1178     njs_opaque_value_t     lvalue;
1179     ngx_stream_js_ctx_t   *ctx;
1180     ngx_stream_session_t  *s;
1181 
1182     static const njs_str_t last_key = njs_str("last");
1183     static const njs_str_t flush_key = njs_str("flush");
1184 
1185     s = njs_vm_external(vm, ngx_stream_js_session_proto_id,
1186                         njs_argument(args, 0));
1187     if (s == NULL) {
1188         njs_vm_error(vm, "\"this\" is not an external");
1189         return NJS_ERROR;
1190     }
1191 
1192     c = s->connection;
1193 
1194     ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
1195 
1196     if (!ctx->filter) {
1197         njs_vm_error(vm, "cannot send buffer in this handler");
1198         return NJS_ERROR;
1199     }
1200 
1201     if (ngx_js_string(vm, njs_arg(args, nargs, 1), &buffer) != NGX_OK) {
1202         njs_vm_error(vm, "failed to get buffer arg");
1203         return NJS_ERROR;
1204     }
1205 
1206     flush = ctx->buf->flush;
1207     last_buf = ctx->buf->last_buf;
1208 
1209     flags = njs_arg(args, nargs, 2);
1210 
1211     if (njs_value_is_object(flags)) {
1212         value = njs_vm_object_prop(vm, flags, &flush_key, &lvalue);
1213         if (value != NULL) {
1214             flush = njs_value_bool(value);
1215         }
1216 
1217         value = njs_vm_object_prop(vm, flags, &last_key, &lvalue);
1218         if (value != NULL) {
1219             last_buf = njs_value_bool(value);
1220         }
1221     }
1222 
1223     cl = ngx_chain_get_free_buf(c->pool, &ctx->free);
1224     if (cl == NULL) {
1225         njs_vm_error(vm, "memory error");
1226         return NJS_ERROR;
1227     }
1228 
1229     b = cl->buf;
1230 
1231     b->flush = flush;
1232     b->last_buf = last_buf;
1233 
1234     b->memory = (buffer.length ? 1 : 0);
1235     b->sync = (buffer.length ? 0 : 1);
1236     b->tag = (ngx_buf_tag_t) &ngx_stream_js_module;
1237 
1238     b->start = buffer.start;
1239     b->end = buffer.start + buffer.length;
1240     b->pos = b->start;
1241     b->last = b->end;
1242 
1243     *ctx->last_out = cl;
1244     ctx->last_out = &cl->next;
1245 
1246     njs_value_undefined_set(njs_vm_retval(vm));
1247 
1248     return NJS_OK;
1249 }
1250 
1251 
1252 static njs_int_t
ngx_stream_js_ext_set_return_value(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1253 ngx_stream_js_ext_set_return_value(njs_vm_t *vm, njs_value_t *args,
1254     njs_uint_t nargs, njs_index_t unused)
1255 {
1256     ngx_stream_js_ctx_t   *ctx;
1257     ngx_stream_session_t  *s;
1258 
1259     s = njs_vm_external(vm, ngx_stream_js_session_proto_id,
1260                         njs_argument(args, 0));
1261     if (s == NULL) {
1262         njs_vm_error(vm, "\"this\" is not an external");
1263         return NJS_ERROR;
1264     }
1265 
1266     ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
1267 
1268     njs_value_assign(&ctx->retval, njs_arg(args, nargs, 1));
1269     njs_value_undefined_set(njs_vm_retval(vm));
1270 
1271     return NJS_OK;
1272 }
1273 
1274 
1275 static njs_int_t
ngx_stream_js_ext_variables(njs_vm_t * vm,njs_object_prop_t * prop,njs_value_t * value,njs_value_t * setval,njs_value_t * retval)1276 ngx_stream_js_ext_variables(njs_vm_t *vm, njs_object_prop_t *prop,
1277     njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
1278 {
1279     njs_int_t                     rc;
1280     njs_str_t                     val;
1281     ngx_str_t                     name;
1282     ngx_uint_t                    key;
1283     ngx_stream_variable_t        *v;
1284     ngx_stream_session_t         *s;
1285     ngx_stream_core_main_conf_t  *cmcf;
1286     ngx_stream_variable_value_t  *vv;
1287 
1288     s = njs_vm_external(vm, ngx_stream_js_session_proto_id, value);
1289     if (s == NULL) {
1290         njs_value_undefined_set(retval);
1291         return NJS_DECLINED;
1292     }
1293 
1294     rc = njs_vm_prop_name(vm, prop, &val);
1295     if (rc != NJS_OK) {
1296         njs_value_undefined_set(retval);
1297         return NJS_DECLINED;
1298     }
1299 
1300     name.data = val.start;
1301     name.len = val.length;
1302 
1303     if (setval == NULL) {
1304         key = ngx_hash_strlow(name.data, name.data, name.len);
1305 
1306         vv = ngx_stream_get_variable(s, &name, key);
1307         if (vv == NULL || vv->not_found) {
1308             njs_value_undefined_set(retval);
1309             return NJS_DECLINED;
1310         }
1311 
1312         return ngx_js_prop(vm, njs_vm_prop_magic32(prop), retval, vv->data,
1313                            vv->len);
1314     }
1315 
1316     cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
1317 
1318     key = ngx_hash_strlow(name.data, name.data, name.len);
1319 
1320     v = ngx_hash_find(&cmcf->variables_hash, key, name.data, name.len);
1321 
1322     if (v == NULL) {
1323         njs_vm_error(vm, "variable not found");
1324         return NJS_ERROR;
1325     }
1326 
1327     if (ngx_js_string(vm, setval, &val) != NGX_OK) {
1328         return NJS_ERROR;
1329     }
1330 
1331     if (v->set_handler != NULL) {
1332         vv = ngx_pcalloc(s->connection->pool,
1333                          sizeof(ngx_stream_variable_value_t));
1334         if (vv == NULL) {
1335             return NJS_ERROR;
1336         }
1337 
1338         vv->valid = 1;
1339         vv->not_found = 0;
1340         vv->data = val.start;
1341         vv->len = val.length;
1342 
1343         v->set_handler(s, vv, v->data);
1344 
1345         return NJS_OK;
1346     }
1347 
1348     if (!(v->flags & NGX_STREAM_VAR_INDEXED)) {
1349         njs_vm_error(vm, "variable is not writable");
1350         return NJS_ERROR;
1351     }
1352 
1353     vv = &s->variables[v->index];
1354 
1355     vv->valid = 1;
1356     vv->not_found = 0;
1357 
1358     vv->data = ngx_pnalloc(s->connection->pool, val.length);
1359     if (vv->data == NULL) {
1360         return NJS_ERROR;
1361     }
1362 
1363     vv->len = val.length;
1364     ngx_memcpy(vv->data, val.start, vv->len);
1365 
1366     return NJS_OK;
1367 }
1368 
1369 
1370 static njs_host_event_t
ngx_stream_js_set_timer(njs_external_ptr_t external,uint64_t delay,njs_vm_event_t vm_event)1371 ngx_stream_js_set_timer(njs_external_ptr_t external, uint64_t delay,
1372     njs_vm_event_t vm_event)
1373 {
1374     ngx_event_t            *ev;
1375     ngx_stream_session_t   *s;
1376     ngx_stream_js_event_t  *js_event;
1377 
1378     s = (ngx_stream_session_t *) external;
1379 
1380     ev = ngx_pcalloc(s->connection->pool, sizeof(ngx_event_t));
1381     if (ev == NULL) {
1382         return NULL;
1383     }
1384 
1385     js_event = ngx_palloc(s->connection->pool, sizeof(ngx_stream_js_event_t));
1386     if (js_event == NULL) {
1387         return NULL;
1388     }
1389 
1390     js_event->session = s;
1391     js_event->vm_event = vm_event;
1392     js_event->ident = s->connection->fd;
1393 
1394     ev->data = js_event;
1395     ev->log = s->connection->log;
1396     ev->handler = ngx_stream_js_timer_handler;
1397 
1398     ngx_add_timer(ev, delay);
1399 
1400     return ev;
1401 }
1402 
1403 
1404 static void
ngx_stream_js_clear_timer(njs_external_ptr_t external,njs_host_event_t event)1405 ngx_stream_js_clear_timer(njs_external_ptr_t external, njs_host_event_t event)
1406 {
1407     ngx_event_t  *ev = event;
1408 
1409     if (ev->timer_set) {
1410         ngx_del_timer(ev);
1411     }
1412 }
1413 
1414 
1415 static void
ngx_stream_js_timer_handler(ngx_event_t * ev)1416 ngx_stream_js_timer_handler(ngx_event_t *ev)
1417 {
1418     ngx_stream_session_t   *s;
1419     ngx_stream_js_event_t  *js_event;
1420 
1421     js_event = (ngx_stream_js_event_t *) ev->data;
1422 
1423     s = js_event->session;
1424 
1425     ngx_stream_js_handle_event(s, js_event->vm_event, NULL, 0);
1426 }
1427 
1428 
1429 static ngx_pool_t *
ngx_stream_js_pool(njs_vm_t * vm,ngx_stream_session_t * s)1430 ngx_stream_js_pool(njs_vm_t *vm, ngx_stream_session_t *s)
1431 {
1432     return s->connection->pool;
1433 }
1434 
1435 
1436 static ngx_resolver_t *
ngx_stream_js_resolver(njs_vm_t * vm,ngx_stream_session_t * s)1437 ngx_stream_js_resolver(njs_vm_t *vm, ngx_stream_session_t *s)
1438 {
1439     ngx_stream_core_srv_conf_t  *cscf;
1440 
1441     cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);
1442 
1443     return cscf->resolver;
1444 }
1445 
1446 
1447 static ngx_msec_t
ngx_stream_js_resolver_timeout(njs_vm_t * vm,ngx_stream_session_t * s)1448 ngx_stream_js_resolver_timeout(njs_vm_t *vm, ngx_stream_session_t *s)
1449 {
1450     ngx_stream_core_srv_conf_t  *cscf;
1451 
1452     cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);
1453 
1454     return cscf->resolver_timeout;
1455 }
1456 
1457 
1458 static void
ngx_stream_js_handle_event(ngx_stream_session_t * s,njs_vm_event_t vm_event,njs_value_t * args,njs_uint_t nargs)1459 ngx_stream_js_handle_event(ngx_stream_session_t *s, njs_vm_event_t vm_event,
1460     njs_value_t *args, njs_uint_t nargs)
1461 {
1462     njs_int_t            rc;
1463     ngx_str_t            exception;
1464     ngx_stream_js_ctx_t  *ctx;
1465 
1466     ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
1467 
1468     njs_vm_post_event(ctx->vm, vm_event, args, nargs);
1469 
1470     rc = njs_vm_run(ctx->vm);
1471 
1472     if (rc == NJS_ERROR) {
1473         ngx_js_retval(ctx->vm, NULL, &exception);
1474 
1475         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
1476                       "js exception: %V", &exception);
1477 
1478         ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
1479     }
1480 
1481     if (rc == NJS_OK) {
1482         ngx_post_event(s->connection->read, &ngx_posted_events);
1483     }
1484 }
1485 
1486 
1487 static char *
ngx_stream_js_init_main_conf(ngx_conf_t * cf,void * conf)1488 ngx_stream_js_init_main_conf(ngx_conf_t *cf, void *conf)
1489 {
1490     ngx_stream_js_main_conf_t *jmcf = conf;
1491 
1492     size_t                   size;
1493     u_char                  *start, *end, *p;
1494     ngx_str_t               *m, file;
1495     njs_int_t                rc;
1496     njs_str_t                text, path;
1497     ngx_uint_t               i;
1498     njs_value_t             *value;
1499     njs_vm_opt_t             options;
1500     ngx_pool_cleanup_t      *cln;
1501     njs_opaque_value_t       lvalue, exception;
1502     ngx_stream_js_import_t  *import;
1503 
1504     static const njs_str_t line_number_key = njs_str("lineNumber");
1505     static const njs_str_t file_name_key = njs_str("fileName");
1506 
1507     if (jmcf->imports == NGX_CONF_UNSET_PTR) {
1508         return NGX_CONF_OK;
1509     }
1510 
1511     size = 0;
1512 
1513     import = jmcf->imports->elts;
1514     for (i = 0; i < jmcf->imports->nelts; i++) {
1515         size += sizeof("import  from '';\n") - 1 + import[i].name.len
1516                 + import[i].path.len;
1517     }
1518 
1519     start = ngx_pnalloc(cf->pool, size);
1520     if (start == NULL) {
1521         return NGX_CONF_ERROR;
1522     }
1523 
1524     p = start;
1525     import = jmcf->imports->elts;
1526     for (i = 0; i < jmcf->imports->nelts; i++) {
1527         p = ngx_cpymem(p, "import ", sizeof("import ") - 1);
1528         p = ngx_cpymem(p, import[i].name.data, import[i].name.len);
1529         p = ngx_cpymem(p, " from '", sizeof(" from '") - 1);
1530         p = ngx_cpymem(p, import[i].path.data, import[i].path.len);
1531         p = ngx_cpymem(p, "';\n", sizeof("';\n") - 1);
1532     }
1533 
1534     njs_vm_opt_init(&options);
1535 
1536     options.backtrace = 1;
1537     options.unhandled_rejection = NJS_VM_OPT_UNHANDLED_REJECTION_THROW;
1538     options.ops = &ngx_stream_js_ops;
1539     options.metas = &ngx_stream_js_metas;
1540     options.addons = njs_js_addon_modules;
1541     options.argv = ngx_argv;
1542     options.argc = ngx_argc;
1543 
1544     file = ngx_cycle->conf_prefix;
1545 
1546     options.file.start = file.data;
1547     options.file.length = file.len;
1548 
1549     jmcf->vm = njs_vm_create(&options);
1550     if (jmcf->vm == NULL) {
1551         ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "failed to create js VM");
1552         return NGX_CONF_ERROR;
1553     }
1554 
1555     cln = ngx_pool_cleanup_add(cf->pool, 0);
1556     if (cln == NULL) {
1557         return NGX_CONF_ERROR;
1558     }
1559 
1560     cln->handler = ngx_stream_js_cleanup_vm;
1561     cln->data = jmcf->vm;
1562 
1563     path.start = ngx_cycle->conf_prefix.data;
1564     path.length = ngx_cycle->conf_prefix.len;
1565 
1566     rc = njs_vm_add_path(jmcf->vm, &path);
1567     if (rc != NJS_OK) {
1568         ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "failed to add \"js_path\"");
1569         return NGX_CONF_ERROR;
1570     }
1571 
1572     if (jmcf->paths != NGX_CONF_UNSET_PTR) {
1573         m = jmcf->paths->elts;
1574 
1575         for (i = 0; i < jmcf->paths->nelts; i++) {
1576             if (ngx_conf_full_name(cf->cycle, &m[i], 1) != NGX_OK) {
1577                 return NGX_CONF_ERROR;
1578             }
1579 
1580             path.start = m[i].data;
1581             path.length = m[i].len;
1582 
1583             rc = njs_vm_add_path(jmcf->vm, &path);
1584             if (rc != NJS_OK) {
1585                 ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
1586                               "failed to add \"js_path\"");
1587                 return NGX_CONF_ERROR;
1588             }
1589         }
1590     }
1591 
1592     ngx_stream_js_session_proto_id = njs_vm_external_prototype(jmcf->vm,
1593                                          ngx_stream_js_ext_session,
1594                                          njs_nitems(ngx_stream_js_ext_session));
1595     if (ngx_stream_js_session_proto_id < 0) {
1596         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
1597                       "failed to add js request proto");
1598         return NGX_CONF_ERROR;
1599     }
1600 
1601     rc = ngx_js_core_init(jmcf->vm, cf->log);
1602     if (njs_slow_path(rc != NJS_OK)) {
1603         return NGX_CONF_ERROR;
1604     }
1605 
1606     end = start + size;
1607 
1608     rc = njs_vm_compile(jmcf->vm, &start, end);
1609 
1610     if (rc != NJS_OK) {
1611         njs_value_assign(&exception, njs_vm_retval(jmcf->vm));
1612         njs_vm_retval_string(jmcf->vm, &text);
1613 
1614         value = njs_vm_object_prop(jmcf->vm, njs_value_arg(&exception),
1615                                    &file_name_key, &lvalue);
1616         if (value == NULL) {
1617             value = njs_vm_object_prop(jmcf->vm, njs_value_arg(&exception),
1618                                        &line_number_key, &lvalue);
1619 
1620             if (value != NULL) {
1621                 i = njs_value_number(value) - 1;
1622 
1623                 if (i < jmcf->imports->nelts) {
1624                     import = jmcf->imports->elts;
1625                     ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
1626                                   "%*s, included in %s:%ui", text.length,
1627                                   text.start, import[i].file, import[i].line);
1628                     return NGX_CONF_ERROR;
1629                 }
1630             }
1631         }
1632 
1633         ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "%*s", text.length,
1634                       text.start);
1635         return NGX_CONF_ERROR;
1636     }
1637 
1638     if (start != end) {
1639         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
1640                       "extra characters in js script: \"%*s\"",
1641                       end - start, start);
1642         return NGX_CONF_ERROR;
1643     }
1644 
1645     return NGX_CONF_OK;
1646 }
1647 
1648 
1649 static char *
ngx_stream_js_import(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)1650 ngx_stream_js_import(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1651 {
1652     ngx_stream_js_main_conf_t *jmcf = conf;
1653 
1654     u_char                  *p, *end, c;
1655     ngx_int_t               from;
1656     ngx_str_t               *value, name, path;
1657     ngx_stream_js_import_t  *import;
1658 
1659     value = cf->args->elts;
1660     from = (cf->args->nelts == 4);
1661 
1662     if (from) {
1663         if (ngx_strcmp(value[2].data, "from") != 0) {
1664             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1665                                "invalid parameter \"%V\"", &value[2]);
1666             return NGX_CONF_ERROR;
1667         }
1668     }
1669 
1670     name = value[1];
1671     path = (from ? value[3] : value[1]);
1672 
1673     if (!from) {
1674         end = name.data + name.len;
1675 
1676         for (p = end - 1; p >= name.data; p--) {
1677             if (*p == '/') {
1678                 break;
1679             }
1680         }
1681 
1682         name.data = p + 1;
1683         name.len = end - p - 1;
1684 
1685         if (name.len < 3
1686             || ngx_memcmp(&name.data[name.len - 3], ".js", 3) != 0)
1687         {
1688             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1689                                "cannot extract export name from file path "
1690                                "\"%V\", use extended \"from\" syntax", &path);
1691             return NGX_CONF_ERROR;
1692         }
1693 
1694         name.len -= 3;
1695     }
1696 
1697     if (name.len == 0) {
1698         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty \"name\" parameter");
1699         return NGX_CONF_ERROR;
1700     }
1701 
1702     p = name.data;
1703     end = name.data + name.len;
1704 
1705     while (p < end) {
1706         c = ngx_tolower(*p);
1707 
1708         if (*p != '_' && (c < 'a' || c > 'z')) {
1709             if (p == name.data) {
1710                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot start "
1711                                    "with \"%c\" in export name \"%V\"", *p,
1712                                    &name);
1713                 return NGX_CONF_ERROR;
1714             }
1715 
1716             if (*p < '0' || *p > '9') {
1717                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character "
1718                                    "\"%c\" in export name \"%V\"", *p, &name);
1719                 return NGX_CONF_ERROR;
1720             }
1721         }
1722 
1723         p++;
1724     }
1725 
1726     if (ngx_strchr(path.data, '\'') != NULL) {
1727         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character \"'\" "
1728                            "in file path \"%V\"", &path);
1729         return NGX_CONF_ERROR;
1730     }
1731 
1732     if (jmcf->imports == NGX_CONF_UNSET_PTR) {
1733         jmcf->imports = ngx_array_create(cf->pool, 4,
1734                                          sizeof(ngx_stream_js_import_t));
1735         if (jmcf->imports == NULL) {
1736             return NGX_CONF_ERROR;
1737         }
1738     }
1739 
1740     import = ngx_array_push(jmcf->imports);
1741     if (import == NULL) {
1742         return NGX_CONF_ERROR;
1743     }
1744 
1745     import->name = name;
1746     import->path = path;
1747     import->file = cf->conf_file->file.name.data;
1748     import->line = cf->conf_file->line;
1749 
1750     return NGX_CONF_OK;
1751 }
1752 
1753 
1754 static char *
ngx_stream_js_set(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)1755 ngx_stream_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1756 {
1757     ngx_str_t              *value, *fname;
1758     ngx_stream_variable_t  *v;
1759 
1760     value = cf->args->elts;
1761 
1762     if (value[1].data[0] != '$') {
1763         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1764                            "invalid variable name \"%V\"", &value[1]);
1765         return NGX_CONF_ERROR;
1766     }
1767 
1768     value[1].len--;
1769     value[1].data++;
1770 
1771     v = ngx_stream_add_variable(cf, &value[1], NGX_STREAM_VAR_CHANGEABLE);
1772     if (v == NULL) {
1773         return NGX_CONF_ERROR;
1774     }
1775 
1776     fname = ngx_palloc(cf->pool, sizeof(ngx_str_t));
1777     if (fname == NULL) {
1778         return NGX_CONF_ERROR;
1779     }
1780 
1781     *fname = value[2];
1782 
1783     v->get_handler = ngx_stream_js_variable_set;
1784     v->data = (uintptr_t) fname;
1785 
1786     return NGX_CONF_OK;
1787 }
1788 
1789 
1790 static char *
ngx_stream_js_var(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)1791 ngx_stream_js_var(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1792 {
1793     ngx_str_t                           *value;
1794     ngx_int_t                            index;
1795     ngx_stream_variable_t               *v;
1796     ngx_stream_complex_value_t          *cv;
1797     ngx_stream_compile_complex_value_t   ccv;
1798 
1799     value = cf->args->elts;
1800 
1801     if (value[1].data[0] != '$') {
1802         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1803                            "invalid variable name \"%V\"", &value[1]);
1804         return NGX_CONF_ERROR;
1805     }
1806 
1807     value[1].len--;
1808     value[1].data++;
1809 
1810     v = ngx_stream_add_variable(cf, &value[1], NGX_STREAM_VAR_CHANGEABLE);
1811     if (v == NULL) {
1812         return NGX_CONF_ERROR;
1813     }
1814 
1815     index = ngx_stream_get_variable_index(cf, &value[1]);
1816     if (index == NGX_ERROR) {
1817         return NGX_CONF_ERROR;
1818     }
1819 
1820     cv = NULL;
1821 
1822     if (cf->args->nelts == 3) {
1823         cv = ngx_palloc(cf->pool, sizeof(ngx_stream_complex_value_t));
1824         if (cv == NULL) {
1825             return NGX_CONF_ERROR;
1826         }
1827 
1828         ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
1829 
1830         ccv.cf = cf;
1831         ccv.value = &value[2];
1832         ccv.complex_value = cv;
1833 
1834         if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
1835             return NGX_CONF_ERROR;
1836         }
1837     }
1838 
1839     v->get_handler = ngx_stream_js_variable_var;
1840     v->data = (uintptr_t) cv;
1841 
1842     return NGX_CONF_OK;
1843 }
1844 
1845 
1846 static void *
ngx_stream_js_create_main_conf(ngx_conf_t * cf)1847 ngx_stream_js_create_main_conf(ngx_conf_t *cf)
1848 {
1849     ngx_stream_js_main_conf_t  *conf;
1850 
1851     conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_js_main_conf_t));
1852     if (conf == NULL) {
1853         return NULL;
1854     }
1855 
1856     /*
1857      * set by ngx_pcalloc():
1858      *
1859      *     conf->vm = NULL;
1860      */
1861 
1862     conf->paths = NGX_CONF_UNSET_PTR;
1863     conf->imports = NGX_CONF_UNSET_PTR;
1864 
1865     return conf;
1866 }
1867 
1868 
1869 static void *
ngx_stream_js_create_srv_conf(ngx_conf_t * cf)1870 ngx_stream_js_create_srv_conf(ngx_conf_t *cf)
1871 {
1872     ngx_stream_js_srv_conf_t  *conf;
1873 
1874     conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_js_srv_conf_t));
1875     if (conf == NULL) {
1876         return NULL;
1877     }
1878 
1879     /*
1880      * set by ngx_pcalloc():
1881      *
1882      *     conf->access = { 0, NULL };
1883      *     conf->preread = { 0, NULL };
1884      *     conf->filter = { 0, NULL };
1885      *     conf->ssl_ciphers = { 0, NULL };
1886      *     conf->ssl_protocols = 0;
1887      *     conf->ssl_trusted_certificate = { 0, NULL };
1888      */
1889 
1890 #if (NGX_STREAM_SSL)
1891     conf->ssl_verify_depth = NGX_CONF_UNSET;
1892 #endif
1893     return conf;
1894 }
1895 
1896 
1897 static char *
ngx_stream_js_merge_srv_conf(ngx_conf_t * cf,void * parent,void * child)1898 ngx_stream_js_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
1899 {
1900     ngx_stream_js_srv_conf_t *prev = parent;
1901     ngx_stream_js_srv_conf_t *conf = child;
1902 
1903     ngx_conf_merge_str_value(conf->access, prev->access, "");
1904     ngx_conf_merge_str_value(conf->preread, prev->preread, "");
1905     ngx_conf_merge_str_value(conf->filter, prev->filter, "");
1906 
1907 #if (NGX_STREAM_SSL)
1908     ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT");
1909 
1910     ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols,
1911                                  (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1
1912                                   |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
1913 
1914     ngx_conf_merge_value(conf->ssl_verify_depth, prev->ssl_verify_depth, 100);
1915 
1916     ngx_conf_merge_str_value(conf->ssl_trusted_certificate,
1917                              prev->ssl_trusted_certificate, "");
1918 
1919     return ngx_stream_js_set_ssl(cf, conf);
1920 #else
1921     return NGX_CONF_OK;
1922 #endif
1923 }
1924 
1925 
1926 static ngx_int_t
ngx_stream_js_init(ngx_conf_t * cf)1927 ngx_stream_js_init(ngx_conf_t *cf)
1928 {
1929     ngx_stream_handler_pt        *h;
1930     ngx_stream_core_main_conf_t  *cmcf;
1931 
1932     ngx_stream_next_filter = ngx_stream_top_filter;
1933     ngx_stream_top_filter = ngx_stream_js_body_filter;
1934 
1935     cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
1936 
1937     h = ngx_array_push(&cmcf->phases[NGX_STREAM_ACCESS_PHASE].handlers);
1938     if (h == NULL) {
1939         return NGX_ERROR;
1940     }
1941 
1942     *h = ngx_stream_js_access_handler;
1943 
1944     h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREREAD_PHASE].handlers);
1945     if (h == NULL) {
1946         return NGX_ERROR;
1947     }
1948 
1949     *h = ngx_stream_js_preread_handler;
1950 
1951     return NGX_OK;
1952 }
1953 
1954 
1955 #if (NGX_STREAM_SSL)
1956 
1957 static char *
ngx_stream_js_set_ssl(ngx_conf_t * cf,ngx_stream_js_srv_conf_t * jscf)1958 ngx_stream_js_set_ssl(ngx_conf_t *cf, ngx_stream_js_srv_conf_t *jscf)
1959 {
1960     ngx_ssl_t           *ssl;
1961     ngx_pool_cleanup_t  *cln;
1962 
1963     ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));
1964     if (ssl == NULL) {
1965         return NGX_CONF_ERROR;
1966     }
1967 
1968     jscf->ssl = ssl;
1969     ssl->log = cf->log;
1970 
1971     if (ngx_ssl_create(ssl, jscf->ssl_protocols, NULL) != NGX_OK) {
1972         return NGX_CONF_ERROR;
1973     }
1974 
1975     cln = ngx_pool_cleanup_add(cf->pool, 0);
1976     if (cln == NULL) {
1977         ngx_ssl_cleanup_ctx(ssl);
1978         return NGX_CONF_ERROR;
1979     }
1980 
1981     cln->handler = ngx_ssl_cleanup_ctx;
1982     cln->data = ssl;
1983 
1984     if (ngx_ssl_ciphers(NULL, ssl, &jscf->ssl_ciphers, 0) != NGX_OK) {
1985         return NGX_CONF_ERROR;
1986     }
1987 
1988     if (ngx_ssl_trusted_certificate(cf, ssl, &jscf->ssl_trusted_certificate,
1989                                     jscf->ssl_verify_depth)
1990         != NGX_OK)
1991     {
1992         return NGX_CONF_ERROR;
1993     }
1994 
1995     return NGX_CONF_OK;
1996 }
1997 
1998 #endif
1999 
2000 
2001 static ngx_ssl_t *
ngx_stream_js_ssl(njs_vm_t * vm,ngx_stream_session_t * s)2002 ngx_stream_js_ssl(njs_vm_t *vm, ngx_stream_session_t *s)
2003 {
2004 #if (NGX_STREAM_SSL)
2005     ngx_stream_js_srv_conf_t  *jscf;
2006 
2007     jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module);
2008 
2009     return jscf->ssl;
2010 #else
2011     return NULL;
2012 #endif
2013 }
2014