vislee / leevis.com

Blog
87 stars 13 forks source link

给nginx添加的特殊功能 #162

Open vislee opened 5 years ago

vislee commented 5 years ago

概述

业务上很多特殊的需求还是需要修改nginx的代码来实现,而这些特殊的需求又不通用,nginx上根本不会支持。

patch

修改nginx代码在proxy_cache_path 添加prefix=$var 支持缓存文件后缀名。 配置实例:

proxy_cache_path ./cache levels=1:2 prefix=$host keys_zone=test:10m max_size=1g;

测试:

./cache/e/0b/www.test.comd0431adbaa72e1b38e0884b71c9d30be
diff --git a/src/http/ngx_http_cache.h b/src/http/ngx_http_cache.h
index f9e966409..7aa8a5f56 100644
--- a/src/http/ngx_http_cache.h
+++ b/src/http/ngx_http_cache.h
@@ -160,6 +160,8 @@ struct ngx_http_file_cache_s {

     ngx_path_t                      *path;

+    ngx_http_complex_value_t         prefix;
+
     off_t                            max_size;
     size_t                           bsize;

diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c
index 3b2b68a26..1f9458313 100644
--- a/src/http/ngx_http_file_cache.c
+++ b/src/http/ngx_http_file_cache.c
@@ -933,6 +933,7 @@ static ngx_int_t
 ngx_http_file_cache_name(ngx_http_request_t *r, ngx_path_t *path)
 {
     u_char            *p;
+    ngx_str_t          prefix;
     ngx_http_cache_t  *c;

     c = r->cache;
@@ -941,8 +942,17 @@ ngx_http_file_cache_name(ngx_http_request_t *r, ngx_path_t *path)
         return NGX_OK;
     }

+    prefix.len = 0;
+    if (c->file_cache->prefix.value.len > 0) {
+        if (ngx_http_complex_value(r, &c->file_cache->prefix, &prefix)
+            != NGX_OK)
+        {
+            return NGX_ERROR;
+        }
+    }
+
     c->file.name.len = path->name.len + 1 + path->len
-                       + 2 * NGX_HTTP_CACHE_KEY_LEN;
+                       + 2 * NGX_HTTP_CACHE_KEY_LEN + prefix.len;

     c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1);
     if (c->file.name.data == NULL) {
@@ -952,6 +962,10 @@ ngx_http_file_cache_name(ngx_http_request_t *r, ngx_path_t *path)
     ngx_memcpy(c->file.name.data, path->name.data, path->name.len);

     p = c->file.name.data + path->name.len + 1 + path->len;
+    if (prefix.len > 0) {
+        ngx_memcpy(p, prefix.data, prefix.len);
+        p += prefix.len;
+    }
     p = ngx_hex_dump(p, c->key, NGX_HTTP_CACHE_KEY_LEN);
     *p = '\0';

@@ -2308,7 +2322,7 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
     u_char                 *last, *p;
     time_t                  inactive;
     ssize_t                 size;
-    ngx_str_t               s, name, *value;
+    ngx_str_t               s, prefix, name, *value;
     ngx_int_t               loader_files, manager_files;
     ngx_msec_t              loader_sleep, manager_sleep, loader_threshold,
                             manager_threshold;
@@ -2316,6 +2330,8 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
     ngx_array_t            *caches;
     ngx_http_file_cache_t  *cache, **ce;

+    ngx_http_compile_complex_value_t   ccv;
+
     cache = ngx_pcalloc(cf->pool, sizeof(ngx_http_file_cache_t));
     if (cache == NULL) {
         return NGX_CONF_ERROR;
@@ -2338,6 +2354,7 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
     manager_sleep = 50;
     manager_threshold = 200;

+    prefix.len = 0;
     name.len = 0;
     size = 0;
     max_size = NGX_MAX_OFF_T_VALUE;
@@ -2393,6 +2410,14 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
             return NGX_CONF_ERROR;
         }

+        if (ngx_strncmp(value[i].data, "prefix=", 7) == 0) {
+
+            prefix.data = value[i].data + 7;
+            prefix.len = value[i].len - 7;
+
+            continue;
+        }
+
         if (ngx_strncmp(value[i].data, "use_temp_path=", 14) == 0) {

             if (ngx_strcmp(&value[i].data[14], "on") == 0) {
@@ -2599,6 +2624,21 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
     cache->inactive = inactive;
     cache->max_size = max_size;

+    // prefix
+    if (prefix.len > 0) {
+        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+        ccv.cf = cf;
+        ccv.value = &prefix;
+        ccv.complex_value = &cache->prefix;
+
+        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "error prefix \"%V\"", &prefix);
+            return NGX_CONF_ERROR;
+        }
+    }
+
     caches = (ngx_array_t *) (confp + cmd->offset);

     ce = ngx_array_push(caches);

类似于proxy_cache_bypass指令。可以在lua中动态控制请求是否缓存。 注意:和proxy_cache_bypass 指令冲突。

例如:

set $pre_cache_bypass "off";

access_lua_by_block {
    ...
    ngx.var.pre_cache_bypass = "cache"
    ...
}
diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c
index 96f3ec69..7d0e19a6 100644
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -247,9 +247,20 @@ ngx_http_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 ngx_int_t
 ngx_http_test_predicates(ngx_http_request_t *r, ngx_array_t *predicates)
 {
-    ngx_str_t                  val;
-    ngx_uint_t                 i;
+    ngx_str_t                  val, name;
+    ngx_uint_t                 i, hash;
     ngx_http_complex_value_t  *cv;
+    ngx_http_variable_value_t *vv;
+
+    ngx_str_set(&name, "pre_cache_bypass");
+    hash = ngx_hash_key(name.data, name.len);
+    vv = ngx_http_get_variable(r, &name, hash);
+
+    if (vv != NULL && vv->valid &&
+        vv->len == 3 && ngx_strncmp(vv->data, "off", 3) == 0)
+    {
+        return NGX_DECLINED;
+    }

     if (predicates == NULL) {
         return NGX_OK;

patch:ngx_http_realip2_module

diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c
index c827c015..138e7b0a 100644
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -15,6 +15,120 @@ ngx_os_io_t  ngx_io;

 static void ngx_drain_connections(ngx_cycle_t *cycle);

+// =======tproxy by liwq (B)=====
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+static ngx_int_t ngx_set_listen_transparent(ngx_cycle_t *cycle,
+    ngx_listening_t *ls);
+static void ngx_set_inherited_transparent (ngx_cycle_t *cycle,
+    ngx_listening_t *ls);
+#endif
+// =======tproxy by liwq (E)=====
+
+// =======downgrade by liwq (B)=====
+ngx_listening_t *
+ngx_attach_listening(ngx_conf_t *cf, struct sockaddr *sockaddr,
+    socklen_t socklen)
+{
+    ngx_uint_t           i, j;
+    ngx_listening_t     *ls, *als = NULL;
+    size_t               len;
+    struct sockaddr     *sa = NULL;
+    u_char               text[NGX_SOCKADDR_STRLEN];
+
+    ls = cf->cycle->listening.elts;
+    for (i = 0; i < cf->cycle->listening.nelts; i++) {
+        if (ls[i].mode == 0) {
+            continue;
+        }
+
+        if (ngx_cmp_sockaddr(ls[i].sockaddr, ls[i].socklen,
+            sockaddr, socklen, 1) != NGX_OK) {
+            continue;
+        }
+
+        als = ngx_palloc(cf->pool, sizeof(ngx_listening_t));
+        ls[i].attach = als;
+        if (als == NULL) {
+            return NULL;
+        }
+
+        if (ls[i].reuseport) {
+            for (j = i; j < cf->cycle->listening.nelts; j++) {
+                if (ngx_cmp_sockaddr(ls[j].sockaddr,
+                    ls[j].socklen, sockaddr, socklen, 1) == NGX_OK)
+                {
+                    ls[j].attach = als;
+
+                    ngx_log_debug2(NGX_LOG_DEBUG_CORE, cf->log, 0,
+                        "attach reuseport addr: %V to worker: %ui",
+                        &ls[j].addr_text, ls[j].worker);
+                }
+            }
+        }
+
+        ngx_memzero(als, sizeof(ngx_listening_t));
+
+        sa = ngx_palloc(cf->pool, socklen);
+        if (sa == NULL) {
+            return NULL;
+        }
+
+        ngx_memcpy(sa, sockaddr, socklen);
+
+        als->sockaddr = sa;
+        als->socklen = socklen;
+
+        len = ngx_sock_ntop(sa, socklen, text, NGX_SOCKADDR_STRLEN, 1);
+        als->addr_text.len = len;
+
+        switch (als->sockaddr->sa_family) {
+    #if (NGX_HAVE_INET6)
+        case AF_INET6:
+            als->addr_text_max_len = NGX_INET6_ADDRSTRLEN;
+            break;
+    #endif
+    #if (NGX_HAVE_UNIX_DOMAIN)
+        case AF_UNIX:
+            als->addr_text_max_len = NGX_UNIX_ADDRSTRLEN;
+            len++;
+            break;
+    #endif
+        case AF_INET:
+            als->addr_text_max_len = NGX_INET_ADDRSTRLEN;
+            break;
+        default:
+            als->addr_text_max_len = NGX_SOCKADDR_STRLEN;
+            break;
+        }
+
+        als->addr_text.data = ngx_pnalloc(cf->pool, len);
+        if (als->addr_text.data == NULL) {
+            return NULL;
+        }
+
+        ngx_memcpy(als->addr_text.data, text, len);
+
+        als->fd = (ngx_socket_t) -1;
+        als->type = SOCK_STREAM;
+
+        als->backlog = NGX_LISTEN_BACKLOG;
+        als->rcvbuf = -1;
+        als->sndbuf = -1;
+
+    #if (NGX_HAVE_SETFIB)
+        als->setfib = -1;
+    #endif
+
+    #if (NGX_HAVE_TCP_FASTOPEN)
+        als->fastopen = -1;
+    #endif
+
+        return als;
+    }
+
+    return NULL;
+}
+// =======downgrade by liwq (B)=====

 ngx_listening_t *
 ngx_create_listening(ngx_conf_t *cf, struct sockaddr *sockaddr,
@@ -375,6 +489,12 @@ ngx_set_inherited_sockets(ngx_cycle_t *cycle)

         ls[i].deferred_accept = 1;
 #endif
+
+// =======tproxy by liwq (B)=====
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+    ngx_set_inherited_transparent(cycle, &ls[i]);
+#endif
+// =======tproxy by liwq (E)=====
     }

     return NGX_OK;
@@ -701,6 +821,12 @@ ngx_configure_listening_sockets(ngx_cycle_t *cycle)
             }
         }

+// =======tproxy by liwq (B)=====
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+    ngx_set_listen_transparent(cycle, &ls[i]);
+#endif
+// =======tproxy by liwq (E)=====
+
 #if (NGX_HAVE_KEEPALIVE_TUNABLE)

         if (ls[i].keepidle) {
@@ -1479,3 +1605,196 @@ ngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text)

     return NGX_ERROR;
 }
+
+// =======tproxy by liwq (B)=====
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+
+static ngx_int_t
+ngx_set_listen_transparent(ngx_cycle_t *cycle, ngx_listening_t *ls)
+{
+    int  value;
+
+    value = ls->transparent;
+
+#if defined(SO_BINDANY)
+
+    if (setsockopt(ls->fd, SOL_SOCKET, SO_BINDANY,
+                   (const void *) &value, sizeof(int)) == -1)
+    {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+                      "setsockopt(SO_BINDANY) failed");
+        return NGX_ERROR;
+    }
+
+#else
+
+    switch (ls->sockaddr->sa_family) {
+
+    case AF_INET:
+
+#if defined(IP_TRANSPARENT)
+
+        if (setsockopt(ls->fd, IPPROTO_IP, IP_TRANSPARENT,
+                       (const void *) &value, sizeof(int)) == -1)
+        {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+                          "setsockopt(IP_TRANSPARENT) failed");
+            return NGX_ERROR;
+        }
+
+#elif defined(IP_BINDANY)
+
+        if (setsockopt(ls->fd, IPPROTO_IP, IP_BINDANY,
+                       (const void *) &value, sizeof(int)) == -1)
+        {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+                          "setsockopt(IP_BINDANY) failed");
+            return NGX_ERROR;
+        }
+
+#endif
+
+        break;
+
+#if (NGX_HAVE_INET6)
+
+    case AF_INET6:
+
+#if defined(IPV6_TRANSPARENT)
+
+        if (setsockopt(ls->fd, IPPROTO_IPV6, IPV6_TRANSPARENT,
+                       (const void *) &value, sizeof(int)) == -1)
+        {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+                          "setsockopt(IPV6_TRANSPARENT) failed");
+            return NGX_ERROR;
+        }
+
+#elif defined(IPV6_BINDANY)
+
+        if (setsockopt(ls->fd, IPPROTO_IPV6, IPV6_BINDANY,
+                       (const void *) &value, sizeof(int)) == -1)
+        {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+                          "setsockopt(IPV6_BINDANY) failed");
+            return NGX_ERROR;
+        }
+
+#else
+
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                      "could not enable transparent listen for IPv6 "
+                      "on this platform");
+
+        return NGX_ERROR;
+
+#endif
+
+        break;
+
+#endif /* NGX_HAVE_INET6 */
+
+    }
+
+#endif /* SO_BINDANY */
+
+    return NGX_OK;
+}
+
+
+static void
+ngx_set_inherited_transparent(ngx_cycle_t *cycle, ngx_listening_t *ls)
+{
+    int        value;
+    socklen_t  olen;
+
+    olen = sizeof(int);
+
+#if defined(SO_BINDANY)
+
+    if (getsockopt(ls->fd, SOL_SOCKET, SO_BINDANY,
+                   (void *) &value, &olen) == -1)
+    {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+                      "getsockopt(SO_BINDANY) failed");
+        return;
+    } else {
+        ls->transparent = value ? 1 : 0;
+    }
+
+#else
+
+    switch (ls->sockaddr->sa_family) {
+
+    case AF_INET:
+
+#if defined(IP_TRANSPARENT)
+
+        if (getsockopt(ls->fd, IPPROTO_IP, IP_TRANSPARENT,
+                       (void *) &value, &olen) == -1)
+        {
+            ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_socket_errno,
+                          "getsockopt(IP_TRANSPARENT) failed");
+            return;
+        } else {
+            ls->transparent = value ? 1 : 0;
+        }
+
+#elif defined(IP_BINDANY)
+
+        if (getsockopt(ls->fd, IPPROTO_IP, IP_BINDANY,
+                       (void *) &value, &olen) == -1)
+        {
+            ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_socket_errno,
+                          "getsockopt(IP_BINDANY) failed");
+            return;
+        } else {
+            ls->transparent = value ? 1 : 0;
+        }
+
+#endif
+
+        break;
+
+#if (NGX_HAVE_INET6)
+
+    case AF_INET6:
+
+#if defined(IPV6_TRANSPARENT)
+
+        if (getsockopt(ls->fd, IPPROTO_IPV6, IPV6_TRANSPARENT,
+                       (void *) &value, &olen) == -1)
+        {
+            ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_socket_errno,
+                          "getsockopt(IPV6_TRANSPARENT) failed");
+            return;
+        } else {
+            ls->transparent = value ? 1 : 0;
+        }
+
+#elif defined(IPV6_BINDANY)
+
+        if (getsockopt(ls->fd, IPPROTO_IPV6, IPV6_BINDANY,
+                       (void *) &value, &olen) == -1)
+        {
+            ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_socket_errno,
+                          "getsockopt(IPV6_BINDANY) failed");
+            return;
+        } else {
+            ls->transparent = value ? 1 : 0;
+        }
+
+#endif
+
+        break;
+
+#endif /* NGX_HAVE_INET6 */
+
+    }
+
+#endif /* SO_BINDANY */
+
+    return;
+}
+#endif
+// =======tproxy by liwq (E)=====
diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h
index 7f358898..692d6f6a 100644
--- a/src/core/ngx_connection.h
+++ b/src/core/ngx_connection.h
@@ -53,10 +53,21 @@ struct ngx_listening_s {

     ngx_uint_t          worker;

+// =======downgrade by liwq (B)=====
+    ngx_listening_t    *attach;
+    ngx_uint_t          mode;
+// =======downgrade by liwq (E)=====
+
     unsigned            open:1;
     unsigned            remain:1;
     unsigned            ignore:1;

+// =======tproxy by liwq (B)=====
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+    unsigned            transparent:1;
+#endif
+// =======tproxy by liwq (E)=====
+
     unsigned            bound:1;       /* already bound */
     unsigned            inherited:1;   /* inherited from previous process */
     unsigned            nonblocking_accept:1;
@@ -214,7 +225,11 @@ struct ngx_connection_s {
         c->log->log_level = l->log_level;                                    \
     }

-
+// =======downgrade by liwq (B)=====
+ngx_listening_t *
+ngx_attach_listening(ngx_conf_t *cf, struct sockaddr *sockaddr,
+    socklen_t socklen);
+// =======downgrade by liwq (E)=====
 ngx_listening_t *ngx_create_listening(ngx_conf_t *cf, struct sockaddr *sockaddr,
     socklen_t socklen);
 ngx_int_t ngx_clone_listening(ngx_conf_t *cf, ngx_listening_t *ls);
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
index 6305ac21..4de3d097 100644
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -243,6 +243,15 @@ static ngx_command_t  ngx_http_ssl_commands[] = {
       offsetof(ngx_http_ssl_srv_conf_t, stapling_verify),
       NULL },

+// =======downgrade by liwq (B)=====
+    { ngx_string("ssl_sni"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_array_slot,
+      NGX_HTTP_SRV_CONF_OFFSET,
+      offsetof(ngx_http_ssl_srv_conf_t, ssl_sni),
+      NULL },
+// =======downgrade by liwq (E)=====
+
       ngx_null_command
 };

@@ -570,6 +579,11 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
     sscf->stapling = NGX_CONF_UNSET;
     sscf->stapling_verify = NGX_CONF_UNSET;

+
+// =======downgrade by liwq (B)=====
+    sscf->ssl_sni = NGX_CONF_UNSET_PTR;
+// =======downgrade by liwq (E)=====
+
     return sscf;
 }

@@ -645,6 +659,10 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
     ngx_conf_merge_str_value(conf->stapling_responder,
                          prev->stapling_responder, "");

+// =======downgrade by liwq (B)=====
+    ngx_conf_merge_ptr_value(conf->ssl_sni, prev->ssl_sni, NULL);
+// =======downgrade by liwq (E)=====
+
     conf->ssl.log = cf->log;

     if (conf->enable) {
diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h
index b97417a2..10c6de1b 100644
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -57,6 +57,10 @@ typedef struct {
     ngx_str_t                       stapling_file;
     ngx_str_t                       stapling_responder;

+// =======downgrade by liwq (B)=====
+    ngx_array_t                    *ssl_sni;
+// =======downgrade by liwq (E)=====
+
     u_char                         *file;
     ngx_uint_t                      line;
 } ngx_http_ssl_srv_conf_t;
diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c
index 9d8b6d79..483943ac 100644
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -1772,6 +1772,16 @@ ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr)
     ls->reuseport = addr->opt.reuseport;
 #endif

+// =======downgrade by liwq (B)=====
+    ls->mode = NGX_HTTP_MODULE;
+// =======downgrade by liwq (E)=====
+
+// =======tproxy by liwq (B)=====
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+    ls->transparent = addr->opt.transparent;
+#endif
+// =======tproxy by liwq (E)=====
+
     return ls;
 }

diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
index 57a47427..c9027208 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -4067,6 +4067,27 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
             continue;
         }

+        // =======tproxy by liwq (B)=====
+        if (ngx_strcmp(value[n].data, "transparent") == 0) {
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+            if (lsopt.wildcard) {
+                lsopt.set = 1;
+                lsopt.bind = 1;
+                lsopt.transparent = 1;
+            } else {
+                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                            "transparent listen only supported wildcard addr, "
+                            "this addr ignored");
+            }
+#else
+            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                               "transparent listen is not supported "
+                               "on this platform, ignored");
+#endif
+            continue;
+        }
+        // =======tproxy by liwq (E)=====
+
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "invalid parameter \"%V\"", &value[n]);
         return NGX_CONF_ERROR;
diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h
index a6128b54..8ecbce51 100644
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -81,6 +81,12 @@ typedef struct {
     unsigned                   reuseport:1;
     unsigned                   so_keepalive:2;
     unsigned                   proxy_protocol:1;
+// =======tproxy by liwq (B)=====
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+    unsigned                   transparent:1; /* unsigned  transparent:1; */
+#endif
+// =======tproxy by liwq (E)=====
+

     int                        backlog;
     int                        rcvbuf;
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index 0fa7f08a..35783d44 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -9,6 +9,11 @@
 #include <ngx_core.h>
 #include <ngx_http.h>

+// =======downgrade by liwq (B)=====
+extern void ngx_stream_init_connection(ngx_connection_t *c);
+extern ngx_int_t ngx_stream_ssl_get_server_name(ngx_pool_t *pool,
+    ngx_log_t *log, u_char *pos, u_char *last, ngx_str_t *server_name);
+// =======downgrade by liwq (E)=====

 static void ngx_http_wait_request_handler(ngx_event_t *ev);
 static void ngx_http_process_request_line(ngx_event_t *rev);
@@ -757,6 +762,43 @@ ngx_http_create_request(ngx_connection_t *c)

 #if (NGX_HTTP_SSL)

+// =======downgrade by liwq (B)=====
+static ngx_int_t
+ngx_http_check_cert(ngx_array_t *ssl_sni, ngx_str_t *sni)
+{
+    ngx_uint_t           i;
+    ngx_str_t           *ss;
+
+    if (ssl_sni == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (sni == NULL || sni->len == 0) {
+        return NGX_ERROR;
+    }
+
+    ss = ssl_sni->elts;
+    for (i = 0; i < ssl_sni->nelts; i++) {
+        if (ss[i].len == sni->len &&
+            ngx_memcmp(ss[i].data, sni->data, sni->len) == 0) {
+
+            return NGX_OK;
+        }
+
+        if (ss[i].len > 2 && ss[i].len <= sni->len &&
+            ss[i].data[0] == '*' && ss[i].data[1] == '.' &&
+            ngx_memcmp(ss[i].data + 1, sni->data + (sni->len - ss[i].len + 1),
+            ss[i].len - 1) == 0 ) {
+
+            return NGX_OK;
+        }
+
+    }
+
+    return NGX_ERROR;
+}
+// =======downgrade by liwq (E)=====
+
 static void
 ngx_http_ssl_handshake(ngx_event_t *rev)
 {
@@ -770,6 +812,13 @@ ngx_http_ssl_handshake(ngx_event_t *rev)
     ngx_http_ssl_srv_conf_t   *sscf;
     ngx_http_core_loc_conf_t  *clcf;

+// =======downgrade by liwq (B)=====
+    u_char                     pb[1024+1];
+    ssize_t                    m;
+    ngx_int_t                  rt;
+    ngx_str_t                  sni;
+// =======downgrade by liwq (E)=====
+
     c = rev->data;
     hc = c->data;

@@ -873,6 +922,34 @@ ngx_http_ssl_handshake(ngx_event_t *rev)
             sscf = ngx_http_get_module_srv_conf(hc->conf_ctx,
                                                 ngx_http_ssl_module);

+// =======downgrade by liwq (B)=====
+            if (c->listening->attach && sscf->ssl_sni) {
+                m = recv(c->fd, (char *) pb, 1024, MSG_PEEK);
+                // rt = ngx_http_ssl_sni(pb, m, &sni);
+
+                rt = ngx_stream_ssl_get_server_name(c->pool, c->log,
+                    pb, pb+m, &sni);
+
+                ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+                               "https downgrade: %d, %d, SNI hostname %V",
+                               m, rt, &sni);
+
+                if (rt == NGX_OK &&
+                    ngx_http_check_cert(sscf->ssl_sni, &sni) == NGX_ERROR) {
+
+                    if (c->log->data != NULL) {
+                        ngx_pfree(c->pool, c->log->data);
+                    }
+                    if (c->data != NULL) {
+                        ngx_pfree(c->pool, c->data);
+                    }
+
+                    ngx_stream_init_connection(c);
+                    return;
+                }
+            }
+// =======downgrade by liwq (E)=====
+
             if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER)
                 != NGX_OK)
             {
diff --git a/src/stream/ngx_stream.c b/src/stream/ngx_stream.c
index 0efbda89..3dbf9aed 100644
--- a/src/stream/ngx_stream.c
+++ b/src/stream/ngx_stream.c
@@ -476,8 +476,22 @@ ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)
                 continue;
             }

-            ls = ngx_create_listening(cf, &addr[i].opt.sockaddr.sockaddr,
-                                      addr[i].opt.socklen);
+// =======downgrade by liwq (B)=====
+            if (addr[i].opt.attach) {
+                ls = ngx_attach_listening(cf, &addr[i].opt.sockaddr.sockaddr,
+                                        addr[i].opt.socklen);
+                if (ls == NULL) {
+                    ngx_log_error(NGX_LOG_ERR, cf->log, 0,
+                        "stream attach error");
+                }
+            } else {
+                ls = ngx_create_listening(cf, &addr[i].opt.sockaddr.sockaddr,
+                                        addr[i].opt.socklen);
+            }
+// =======downgrade by liwq (E)=====
+
+            // ls = ngx_create_listening(cf, &addr[i].opt.sockaddr.sockaddr,
+            //                           addr[i].opt.socklen);
             if (ls == NULL) {
                 return NGX_CONF_ERROR;
             }
diff --git a/src/stream/ngx_stream.h b/src/stream/ngx_stream.h
index 09d24593..182148f5 100644
--- a/src/stream/ngx_stream.h
+++ b/src/stream/ngx_stream.h
@@ -47,6 +47,10 @@ typedef struct {
     /* server ctx */
     ngx_stream_conf_ctx_t         *ctx;

+// =======downgrade by liwq (B)=====
+    unsigned                       attach:1;
+// =======downgrade by liwq (E)=====
+
     unsigned                       bind:1;
     unsigned                       wildcard:1;
     unsigned                       ssl:1;
diff --git a/src/stream/ngx_stream_core_module.c b/src/stream/ngx_stream_core_module.c
index 272708d6..cd66ba7d 100644
--- a/src/stream/ngx_stream_core_module.c
+++ b/src/stream/ngx_stream_core_module.c
@@ -628,6 +628,13 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
         }
 #endif

+// =======downgrade by liwq (B)=====
+        if (ngx_strcmp(value[i].data, "attach") == 0) {
+            ls->attach = 1;
+            continue;
+        }
+// =======downgrade by liwq (E)=====
+
         if (ngx_strcmp(value[i].data, "bind") == 0) {
             ls->bind = 1;
             continue;
@@ -864,6 +871,17 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
         }
     }

+// =======downgrade by liwq (B)=====
+    if (ls->attach) {
+        if (ls->type == SOCK_DGRAM) {
+            return "\"attach\" parameter is incompatible with \"udp\"";
+        }
+        if (ls->bind) {
+            return "\"attach\" parameter is incompatible with \"bind\"";
+        }
+    }
+// =======downgrade by liwq (B)=====
+
     als = cmcf->listen.elts;

     for (i = 0; i < cmcf->listen.nelts - 1; i++) {
diff --git a/src/stream/ngx_stream_handler.c b/src/stream/ngx_stream_handler.c
index 2b6881bc..69ba8068 100644
--- a/src/stream/ngx_stream_handler.c
+++ b/src/stream/ngx_stream_handler.c
@@ -40,7 +40,15 @@ ngx_stream_init_connection(ngx_connection_t *c)

     /* find the server configuration for the address:port */

-    port = c->listening->servers;
+// =======downgrade by liwq (B)=====
+    if (c->listening->mode && c->listening->attach) {
+        port = c->listening->attach->servers;
+    } else {
+        port = c->listening->servers;
+    }
+// =======downgrade by liwq (E)=====
+
+    // port = c->listening->servers;

     if (port->naddrs > 1) {

diff --git a/src/stream/ngx_stream_ssl_preread_module.c b/src/stream/ngx_stream_ssl_preread_module.c
index e3d11fd9..86833a04 100644
--- a/src/stream/ngx_stream_ssl_preread_module.c
+++ b/src/stream/ngx_stream_ssl_preread_module.c
@@ -360,6 +360,53 @@ ngx_stream_ssl_preread_parse_record(ngx_stream_ssl_preread_ctx_t *ctx,
 }

+// =======downgrade by liwq (B)=====
+ngx_int_t
+ngx_stream_ssl_get_server_name(ngx_pool_t *pool, ngx_log_t *log,
+    u_char *pos, u_char *last, ngx_str_t *server_name)
+{
+    ngx_stream_ssl_preread_ctx_t    ctx;
+    ngx_int_t                       rc;
+    size_t                          len;
+
+    ngx_memzero(&ctx, sizeof(ngx_stream_ssl_preread_ctx_t));
+
+    ctx.pool = pool;
+    ctx.log  = log;
+    ctx.pos  = pos;
+
+    if (last - pos < 5) {
+        return NGX_ERROR;
+    }
+
+    if (pos[0] != 0x16) {
+        return NGX_ERROR;
+    }
+
+    if (pos[1] != 3) {
+        return NGX_ERROR;
+    }
+
+    len = (pos[3] << 8) + pos[4];
+
+    if ((size_t) (last - pos) < len + 5) {
+        return NGX_ERROR;
+    }
+
+    pos += 5;
+
+    rc = ngx_stream_ssl_preread_parse_record(&ctx, pos, pos + len);
+    if (rc != NGX_OK) {
+        return rc;
+    }
+
+    *server_name = ctx.host;
+
+    return NGX_OK;
+}
+// =======downgrade by liwq (E)=====
+
+
 static ngx_int_t
 ngx_stream_ssl_preread_server_name_variable(ngx_stream_session_t *s,
     ngx_variable_value_t *v, uintptr_t data)