[njs] Added support for HTTPS URLs to the Fetch API.

Dmitry Volyntsev xeioex at nginx.com
Tue Oct 5 15:08:49 UTC 2021


details:   https://hg.nginx.org/njs/rev/05a313868939
branches:  
changeset: 1711:05a313868939
user:      Antoine Bonavita <antoine.bonavita at gmail.com>
date:      Wed Sep 01 20:43:56 2021 +0200
description:
Added support for HTTPS URLs to the Fetch API.

The fetch API now accepts an extra parameters:
    - verify: boolean (default true) to control server certificate
    verification.

Verification process can be controlled with the following directives
from the http js and stream js modules:
    - js_fetch_ciphers
    - js_fetch_protocols
    - js_fetch_verify_depth
    - js_fetch_trusted_certificate

In collaboration with Dmitry Volyntsev.

diffstat:

 nginx/ngx_http_js_module.c   |  141 +++++++++++++++++++++++++-
 nginx/ngx_js.h               |    3 +
 nginx/ngx_js_fetch.c         |  236 ++++++++++++++++++++++++++++++++++++++++++-
 nginx/ngx_stream_js_module.c |  141 +++++++++++++++++++++++++-
 4 files changed, 514 insertions(+), 7 deletions(-)

diffs (758 lines):

diff -r 64d8d8eeebda -r 05a313868939 nginx/ngx_http_js_module.c
--- a/nginx/ngx_http_js_module.c	Tue Oct 05 13:01:11 2021 +0000
+++ b/nginx/ngx_http_js_module.c	Wed Sep 01 20:43:56 2021 +0200
@@ -27,6 +27,13 @@ typedef struct {
     ngx_str_t              header_filter;
     ngx_str_t              body_filter;
     ngx_uint_t             buffer_type;
+#if (NGX_HTTP_SSL)
+    ngx_ssl_t             *ssl;
+    ngx_str_t              ssl_ciphers;
+    ngx_uint_t             ssl_protocols;
+    ngx_int_t              ssl_verify_depth;
+    ngx_str_t              ssl_trusted_certificate;
+#endif
 } ngx_http_js_loc_conf_t;
 
 
@@ -222,6 +229,22 @@ static void *ngx_http_js_create_loc_conf
 static char *ngx_http_js_merge_loc_conf(ngx_conf_t *cf, void *parent,
     void *child);
 
+#if (NGX_HTTP_SSL)
+static char * ngx_http_js_set_ssl(ngx_conf_t *cf, ngx_http_js_loc_conf_t *plcf);
+#endif
+static ngx_ssl_t *ngx_http_js_ssl(njs_vm_t *vm, ngx_http_request_t *r);
+
+#if (NGX_HTTP_SSL)
+
+static ngx_conf_bitmask_t  ngx_http_js_ssl_protocols[] = {
+    { ngx_string("TLSv1"), NGX_SSL_TLSv1 },
+    { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
+    { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
+    { ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 },
+    { ngx_null_string, 0 }
+};
+
+#endif
 
 static ngx_command_t  ngx_http_js_commands[] = {
 
@@ -281,6 +304,38 @@ static ngx_command_t  ngx_http_js_comman
       0,
       NULL },
 
+#if (NGX_HTTP_SSL)
+
+    { ngx_string("js_fetch_ciphers"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_js_loc_conf_t, ssl_ciphers),
+      NULL },
+
+    { ngx_string("js_fetch_protocols"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+      ngx_conf_set_bitmask_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_js_loc_conf_t, ssl_protocols),
+      &ngx_http_js_ssl_protocols },
+
+    { ngx_string("js_fetch_verify_depth"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_num_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_js_loc_conf_t, ssl_verify_depth),
+      NULL },
+
+    { ngx_string("js_fetch_trusted_certificate"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_js_loc_conf_t, ssl_trusted_certificate),
+      NULL },
+
+#endif
+
       ngx_null_command
 };
 
@@ -657,11 +712,12 @@ static uintptr_t ngx_http_js_uptr[] = {
     (uintptr_t) ngx_http_js_resolver,
     (uintptr_t) ngx_http_js_resolver_timeout,
     (uintptr_t) ngx_http_js_handle_event,
+    (uintptr_t) ngx_http_js_ssl,
 };
 
 
 static njs_vm_meta_t ngx_http_js_metas = {
-    .size = 5,
+    .size = 6,
     .values = ngx_http_js_uptr
 };
 
@@ -3951,8 +4007,15 @@ ngx_http_js_create_loc_conf(ngx_conf_t *
      *     conf->header_filter = { 0, NULL };
      *     conf->body_filter = { 0, NULL };
      *     conf->buffer_type = NGX_JS_UNSET;
+     *     conf->ssl_ciphers = { 0, NULL };
+     *     conf->ssl_protocols = 0;
+     *     conf->ssl_trusted_certificate = { 0, NULL };
      */
 
+#if (NGX_HTTP_SSL)
+    conf->ssl_verify_depth = NGX_CONF_UNSET;
+#endif
+
     return conf;
 }
 
@@ -3969,5 +4032,81 @@ ngx_http_js_merge_loc_conf(ngx_conf_t *c
     ngx_conf_merge_uint_value(conf->buffer_type, prev->buffer_type,
                               NGX_JS_STRING);
 
+#if (NGX_HTTP_SSL)
+    ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT");
+
+    ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols,
+                                 (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1
+                                  |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
+
+    ngx_conf_merge_value(conf->ssl_verify_depth, prev->ssl_verify_depth, 100);
+
+    ngx_conf_merge_str_value(conf->ssl_trusted_certificate,
+                             prev->ssl_trusted_certificate, "");
+
+    return ngx_http_js_set_ssl(cf, conf);
+#else
+    return NGX_CONF_OK;
+#endif
+}
+
+
+#if (NGX_HTTP_SSL)
+
+static char *
+ngx_http_js_set_ssl(ngx_conf_t *cf, ngx_http_js_loc_conf_t *plcf)
+{
+    ngx_ssl_t           *ssl;
+    ngx_pool_cleanup_t  *cln;
+
+    ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));
+    if (ssl == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    plcf->ssl = ssl;
+    ssl->log = cf->log;
+
+    if (ngx_ssl_create(ssl, plcf->ssl_protocols, NULL) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    cln = ngx_pool_cleanup_add(cf->pool, 0);
+    if (cln == NULL) {
+        ngx_ssl_cleanup_ctx(ssl);
+        return NGX_CONF_ERROR;
+    }
+
+    cln->handler = ngx_ssl_cleanup_ctx;
+    cln->data = ssl;
+
+    if (ngx_ssl_ciphers(NULL, ssl, &plcf->ssl_ciphers, 0) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (ngx_ssl_trusted_certificate(cf, ssl, &plcf->ssl_trusted_certificate,
+                                    plcf->ssl_verify_depth)
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
     return NGX_CONF_OK;
 }
+
+#endif
+
+
+static ngx_ssl_t *
+ngx_http_js_ssl(njs_vm_t *vm, ngx_http_request_t *r)
+{
+#if (NGX_HTTP_SSL)
+    ngx_http_js_loc_conf_t   *plcf;
+
+    plcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module);
+
+    return plcf->ssl;
+#else
+    return NULL;
+#endif
+}
diff -r 64d8d8eeebda -r 05a313868939 nginx/ngx_js.h
--- a/nginx/ngx_js.h	Tue Oct 05 13:01:11 2021 +0000
+++ b/nginx/ngx_js.h	Wed Sep 01 20:43:56 2021 +0200
@@ -27,6 +27,7 @@ typedef ngx_resolver_t *(*ngx_external_r
     njs_external_ptr_t e);
 typedef ngx_msec_t (*ngx_external_resolver_timeout_pt)(njs_vm_t *vm,
     njs_external_ptr_t e);
+typedef ngx_ssl_t *(*ngx_external_ssl_pt)(njs_vm_t *vm, njs_external_ptr_t e);
 
 
 #define ngx_external_connection(vm, e)                                        \
@@ -39,6 +40,8 @@ typedef ngx_msec_t (*ngx_external_resolv
 	((ngx_external_resolver_timeout_pt) njs_vm_meta(vm, 3))(vm, e)
 #define ngx_external_event_handler(vm, e)                                     \
     ((ngx_js_event_handler_pt) njs_vm_meta(vm, 4))
+#define ngx_external_ssl(vm, e)                                               \
+    ((ngx_external_ssl_pt) njs_vm_meta(vm, 5))(vm, e)
 
 
 #define ngx_js_prop(vm, type, value, start, len)                              \
diff -r 64d8d8eeebda -r 05a313868939 nginx/ngx_js_fetch.c
--- a/nginx/ngx_js_fetch.c	Tue Oct 05 13:01:11 2021 +0000
+++ b/nginx/ngx_js_fetch.c	Wed Sep 01 20:43:56 2021 +0200
@@ -2,6 +2,7 @@
 /*
  * Copyright (C) Dmitry Volyntsev
  * Copyright (C) hongzhidao
+ * Copyright (C) Antoine Bonavita
  * Copyright (C) NGINX, Inc.
  */
 
@@ -65,6 +66,12 @@ struct ngx_js_http_s {
     njs_str_t                      url;
     ngx_array_t                    headers;
 
+#if (NGX_SSL)
+    ngx_str_t                      tls_name;
+    ngx_ssl_t                     *ssl;
+    njs_bool_t                     ssl_verify;
+#endif
+
     ngx_buf_t                     *buffer;
     ngx_buf_t                     *chunk;
     njs_chb_t                      chain;
@@ -142,6 +149,12 @@ static njs_int_t ngx_response_js_ext_typ
 static njs_int_t ngx_response_js_ext_body(njs_vm_t *vm, njs_value_t *args,
      njs_uint_t nargs, njs_index_t unused);
 
+#if (NGX_SSL)
+static void ngx_js_http_ssl_init_connection(ngx_js_http_t *http);
+static void ngx_js_http_ssl_handshake_handler(ngx_connection_t *c);
+static void ngx_js_http_ssl_handshake(ngx_js_http_t *http);
+static njs_int_t ngx_js_http_ssl_name(ngx_js_http_t *http);
+#endif
 
 static njs_external_t  ngx_js_ext_http_response_headers[] = {
 
@@ -344,6 +357,9 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value
     static const njs_str_t buffer_size_key = njs_str("buffer_size");
     static const njs_str_t body_size_key = njs_str("max_response_body_size");
     static const njs_str_t method_key = njs_str("method");
+#if (NGX_SSL)
+    static const njs_str_t verify_key = njs_str("verify");
+#endif
 
     external = njs_vm_external(vm, NJS_PROTO_ID_ANY, njs_argument(args, 0));
     if (external == NULL) {
@@ -384,6 +400,17 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value
         u.url.len -= 7;
         u.url.data += 7;
 
+#if (NGX_SSL)
+    } else if (u.url.len > 8
+        && ngx_strncasecmp(u.url.data, (u_char *) "https://", 8) == 0)
+    {
+        u.url.len -= 8;
+        u.url.data += 8;
+        u.default_port = 443;
+        http->ssl = ngx_external_ssl(vm, external);
+        http->ssl_verify = 1;
+#endif
+
     } else {
         njs_vm_error(vm, "unsupported URL prefix");
         goto fail;
@@ -432,6 +459,13 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value
         {
             goto fail;
         }
+
+#if (NGX_SSL)
+        value = njs_vm_object_prop(vm, init, &verify_key, &lvalue);
+        if (value != NULL) {
+            http->ssl_verify = njs_value_bool(value);
+        }
+#endif
     }
 
     njs_chb_init(&http->chain, njs_vm_memory_pool(vm));
@@ -501,6 +535,11 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value
         njs_chb_append_literal(&http->chain, CRLF);
     }
 
+#if (NGX_SSL)
+    http->tls_name.data = u.host.data;
+    http->tls_name.len = u.host.len;
+#endif
+
     if (body.length != 0) {
         njs_chb_sprintf(&http->chain, 32, "Content-Length: %uz" CRLF CRLF,
                         body.length);
@@ -697,6 +736,29 @@ failed:
 
 
 static void
+ngx_js_http_close_connection(ngx_connection_t *c)
+{
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "close js http connection: %d", c->fd);
+
+#if (NGX_SSL)
+    if (c->ssl) {
+        c->ssl->no_wait_shutdown = 1;
+
+        if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
+            c->ssl->handler = ngx_js_http_close_connection;
+            return;
+        }
+    }
+#endif
+
+    c->destroyed = 1;
+
+    ngx_close_connection(c);
+}
+
+
+static void
 njs_js_http_destructor(njs_external_ptr_t external, njs_host_event_t host)
 {
     ngx_js_http_t  *http;
@@ -712,7 +774,7 @@ njs_js_http_destructor(njs_external_ptr_
     }
 
     if (http->peer.connection != NULL) {
-        ngx_close_connection(http->peer.connection);
+        ngx_js_http_close_connection(http->peer.connection);
         http->peer.connection = NULL;
     }
 }
@@ -773,7 +835,7 @@ ngx_js_http_fetch_done(ngx_js_http_t *ht
                    "js fetch done http:%p rc:%i", http, (ngx_int_t) rc);
 
     if (http->peer.connection != NULL) {
-        ngx_close_connection(http->peer.connection);
+        ngx_js_http_close_connection(http->peer.connection);
         http->peer.connection = NULL;
     }
 
@@ -846,12 +908,169 @@ ngx_js_http_connect(ngx_js_http_t *http)
         ngx_add_timer(http->peer.connection->write, http->timeout);
     }
 
+#if (NGX_SSL)
+    if (http->ssl != NULL && http->peer.connection->ssl == NULL) {
+        ngx_js_http_ssl_init_connection(http);
+        return;
+    }
+#endif
+
     if (rc == NGX_OK) {
         ngx_js_http_write_handler(http->peer.connection->write);
     }
 }
 
 
+#if (NGX_SSL)
+
+static void
+ngx_js_http_ssl_init_connection(ngx_js_http_t *http)
+{
+    ngx_int_t          rc;
+    ngx_connection_t  *c;
+
+    c = http->peer.connection;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, http->log, 0,
+                   "js http secure connect %ui/%ui", http->naddr, http->naddrs);
+
+    if (ngx_ssl_create_connection(http->ssl, c, NGX_SSL_BUFFER|NGX_SSL_CLIENT)
+        != NGX_OK)
+    {
+        ngx_js_http_error(http, 0, "failed to create ssl connection");
+        return;
+    }
+
+    c->sendfile = 0;
+
+    if (ngx_js_http_ssl_name(http) != NGX_OK) {
+        ngx_js_http_error(http, 0, "failed to create ssl connection");
+        return;
+    }
+
+    c->log->action = "SSL handshaking to fetch target";
+
+    rc = ngx_ssl_handshake(c);
+
+    if (rc == NGX_AGAIN) {
+        c->data = http;
+        c->ssl->handler = ngx_js_http_ssl_handshake_handler;
+        return;
+    }
+
+    ngx_js_http_ssl_handshake(http);
+}
+
+
+static void
+ngx_js_http_ssl_handshake_handler(ngx_connection_t *c)
+{
+    ngx_js_http_t  *http;
+
+    http = c->data;
+
+    http->peer.connection->write->handler = ngx_js_http_write_handler;
+    http->peer.connection->read->handler = ngx_js_http_read_handler;
+
+    ngx_js_http_ssl_handshake(http);
+}
+
+
+static void
+ngx_js_http_ssl_handshake(ngx_js_http_t *http)
+{
+    long               rc;
+    ngx_connection_t  *c;
+
+    c = http->peer.connection;
+
+    if (c->ssl->handshaked) {
+        if (http->ssl_verify) {
+            rc = SSL_get_verify_result(c->ssl->connection);
+
+            if (rc != X509_V_OK) {
+                ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                              "js http fetch SSL certificate verify "
+                              "error: (%l:%s)", rc,
+                              X509_verify_cert_error_string(rc));
+                goto failed;
+            }
+
+            if (ngx_ssl_check_host(c, &http->tls_name) != NGX_OK) {
+                ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                              "js http SSL certificate does not match \"%V\"",
+                              &http->tls_name);
+                goto failed;
+            }
+        }
+
+        c->write->handler = ngx_js_http_write_handler;
+        c->read->handler = ngx_js_http_read_handler;
+
+        http->process = ngx_js_http_process_status_line;
+        ngx_js_http_write_handler(c->write);
+
+        return;
+    }
+
+failed:
+
+    ngx_js_http_next(http);
+ }
+
+
+static njs_int_t
+ngx_js_http_ssl_name(ngx_js_http_t *http)
+{
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+    u_char  *p;
+
+    /* as per RFC 6066, literal IPv4 and IPv6 addresses are not permitted */
+    ngx_str_t  *name = &http->tls_name;
+
+    if (name->len == 0 || *name->data == '[') {
+        goto done;
+    }
+
+    if (ngx_inet_addr(name->data, name->len) != INADDR_NONE) {
+        goto done;
+    }
+
+    /*
+     * SSL_set_tlsext_host_name() needs a null-terminated string,
+     * hence we explicitly null-terminate name here
+     */
+
+    p = ngx_pnalloc(http->pool, name->len + 1);
+    if (p == NULL) {
+        return NGX_ERROR;
+    }
+
+    (void) ngx_cpystrn(p, name->data, name->len + 1);
+
+    name->data = p;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, http->log, 0,
+                   "js http SSL server name: \"%s\"", name->data);
+
+    if (SSL_set_tlsext_host_name(http->peer.connection->ssl->connection,
+                                 (char *) name->data)
+        == 0)
+    {
+        ngx_ssl_error(NGX_LOG_ERR, http->log, 0,
+                      "SSL_set_tlsext_host_name(\"%s\") failed", name->data);
+        return NGX_ERROR;
+    }
+
+#endif
+done:
+
+    return NJS_OK;
+}
+
+#endif
+
+
 static void
 ngx_js_http_next(ngx_js_http_t *http)
 {
@@ -863,7 +1082,7 @@ ngx_js_http_next(ngx_js_http_t *http)
     }
 
     if (http->peer.connection != NULL) {
-        ngx_close_connection(http->peer.connection);
+        ngx_js_http_close_connection(http->peer.connection);
         http->peer.connection = NULL;
     }
 
@@ -891,6 +1110,13 @@ ngx_js_http_write_handler(ngx_event_t *w
         return;
     }
 
+#if (NGX_SSL)
+    if (http->ssl != NULL && http->peer.connection->ssl == NULL) {
+        ngx_js_http_ssl_init_connection(http);
+        return;
+    }
+#endif
+
     b = http->buffer;
 
     if (b == NULL) {
@@ -914,7 +1140,7 @@ ngx_js_http_write_handler(ngx_event_t *w
 
     size = b->last - b->pos;
 
-    n = ngx_send(c, b->pos, size);
+    n = c->send(c, b->pos, size);
 
     if (n == NGX_ERROR) {
         ngx_js_http_next(http);
@@ -980,7 +1206,7 @@ ngx_js_http_read_handler(ngx_event_t *re
         b = http->buffer;
         size = b->end - b->last;
 
-        n = ngx_recv(c, b->last, size);
+        n = c->recv(c, b->last, size);
 
         if (n > 0) {
             b->last += n;
diff -r 64d8d8eeebda -r 05a313868939 nginx/ngx_stream_js_module.c
--- a/nginx/ngx_stream_js_module.c	Tue Oct 05 13:01:11 2021 +0000
+++ b/nginx/ngx_stream_js_module.c	Wed Sep 01 20:43:56 2021 +0200
@@ -34,6 +34,13 @@ typedef struct {
     ngx_str_t              access;
     ngx_str_t              preread;
     ngx_str_t              filter;
+#if (NGX_SSL)
+    ngx_ssl_t             *ssl;
+    ngx_str_t              ssl_ciphers;
+    ngx_uint_t             ssl_protocols;
+    ngx_int_t              ssl_verify_depth;
+    ngx_str_t              ssl_trusted_certificate;
+#endif
 } ngx_stream_js_srv_conf_t;
 
 
@@ -135,6 +142,23 @@ static char *ngx_stream_js_merge_srv_con
     void *child);
 static ngx_int_t ngx_stream_js_init(ngx_conf_t *cf);
 
+#if (NGX_SSL)
+static char * ngx_stream_js_set_ssl(ngx_conf_t *cf,
+    ngx_stream_js_srv_conf_t *pscf);
+#endif
+static ngx_ssl_t *ngx_stream_js_ssl(njs_vm_t *vm, ngx_stream_session_t *s);
+
+#if (NGX_HTTP_SSL)
+
+static ngx_conf_bitmask_t  ngx_stream_js_ssl_protocols[] = {
+    { ngx_string("TLSv1"), NGX_SSL_TLSv1 },
+    { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
+    { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
+    { ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 },
+    { ngx_null_string, 0 }
+};
+
+#endif
 
 static ngx_command_t  ngx_stream_js_commands[] = {
 
@@ -194,6 +218,38 @@ static ngx_command_t  ngx_stream_js_comm
       offsetof(ngx_stream_js_srv_conf_t, filter),
       NULL },
 
+#if (NGX_SSL)
+
+    { ngx_string("js_fetch_ciphers"),
+      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, ssl_ciphers),
+      NULL },
+
+    { ngx_string("js_fetch_protocols"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
+      ngx_conf_set_bitmask_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_js_srv_conf_t, ssl_protocols),
+      &ngx_stream_js_ssl_protocols },
+
+    { ngx_string("js_fetch_verify_depth"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_num_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_js_srv_conf_t, ssl_verify_depth),
+      NULL },
+
+    { ngx_string("js_fetch_trusted_certificate"),
+      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, ssl_trusted_certificate),
+      NULL },
+
+#endif
+
       ngx_null_command
 };
 
@@ -408,11 +464,12 @@ static uintptr_t ngx_stream_js_uptr[] = 
     (uintptr_t) ngx_stream_js_resolver,
     (uintptr_t) ngx_stream_js_resolver_timeout,
     (uintptr_t) ngx_stream_js_handle_event,
+    (uintptr_t) ngx_stream_js_ssl,
 };
 
 
 static njs_vm_meta_t ngx_stream_js_metas = {
-    .size = 5,
+    .size = 6,
     .values = ngx_stream_js_uptr
 };
 
@@ -1891,8 +1948,14 @@ ngx_stream_js_create_srv_conf(ngx_conf_t
      *     conf->access = { 0, NULL };
      *     conf->preread = { 0, NULL };
      *     conf->filter = { 0, NULL };
+     *     conf->ssl_ciphers = { 0, NULL };
+     *     conf->ssl_protocols = 0;
+     *     conf->ssl_trusted_certificate = { 0, NULL };
      */
 
+#if (NGX_SSL)
+    conf->ssl_verify_depth = NGX_CONF_UNSET;
+#endif
     return conf;
 }
 
@@ -1907,7 +1970,22 @@ ngx_stream_js_merge_srv_conf(ngx_conf_t 
     ngx_conf_merge_str_value(conf->preread, prev->preread, "");
     ngx_conf_merge_str_value(conf->filter, prev->filter, "");
 
+#if (NGX_HTTP_SSL)
+    ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT");
+
+    ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols,
+                                 (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1
+                                  |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
+
+    ngx_conf_merge_value(conf->ssl_verify_depth, prev->ssl_verify_depth, 100);
+
+    ngx_conf_merge_str_value(conf->ssl_trusted_certificate,
+                             prev->ssl_trusted_certificate, "");
+
+    return ngx_stream_js_set_ssl(cf, conf);
+#else
     return NGX_CONF_OK;
+#endif
 }
 
 
@@ -1938,3 +2016,64 @@ ngx_stream_js_init(ngx_conf_t *cf)
 
     return NGX_OK;
 }
+
+
+#if (NGX_SSL)
+
+static char *
+ngx_stream_js_set_ssl(ngx_conf_t *cf, ngx_stream_js_srv_conf_t *pscf)
+{
+    ngx_ssl_t           *ssl;
+    ngx_pool_cleanup_t  *cln;
+
+    ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));
+    if (ssl == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    pscf->ssl = ssl;
+    ssl->log = cf->log;
+
+    if (ngx_ssl_create(ssl, pscf->ssl_protocols, NULL) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    cln = ngx_pool_cleanup_add(cf->pool, 0);
+    if (cln == NULL) {
+        ngx_ssl_cleanup_ctx(ssl);
+        return NGX_CONF_ERROR;
+    }
+
+    cln->handler = ngx_ssl_cleanup_ctx;
+    cln->data = ssl;
+
+    if (ngx_ssl_ciphers(NULL, ssl, &pscf->ssl_ciphers, 0) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (ngx_ssl_trusted_certificate(cf, ssl, &pscf->ssl_trusted_certificate,
+                                     pscf->ssl_verify_depth)
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+#endif
+
+
+static ngx_ssl_t *
+ngx_stream_js_ssl(njs_vm_t *vm, ngx_stream_session_t *s)
+{
+#if (NGX_SSL)
+    ngx_stream_js_srv_conf_t   *pscf;
+
+    pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module);
+
+    return pscf->ssl;
+#else
+    return NULL;
+#endif
+}


More information about the nginx-devel mailing list