├── COPYRIGHT ├── README.md ├── config ├── doc ├── ngx-http-broadcast-module.md └── ngx-stream-zone-module.md ├── ngx_event_multiport_module.c ├── ngx_http_broadcast_module.c ├── ngx_http_inner_proxy_module.c ├── ngx_multiport.h ├── ngx_multiport_misc.c ├── ngx_process_slot_module.c ├── ngx_stream_zone_module.c ├── ngx_stream_zone_module.h └── t ├── config ├── nginx.conf ├── ngx_multiport_test_module.c ├── ngx_stream_zone_test_module.c └── ngx_test_macro.h /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright (C) 2016-2020, by Jie Wu "AlexWoo" . 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Module nginx-multiport-module 2 | --- 3 | ## Instructions 4 | 5 | Every worker process can bind own port, user can visit specific worker process by using the port. 6 | 7 | - [ngx-stream-zone-module](doc/ngx-stream-zone-module.md) 8 | 9 | Record stream's owner worker process slot 10 | 11 | - [ngx-http-broadcast-module](doc/ngx-http-broadcast-module.md) 12 | 13 | Broadcast HTTP request to all worker processes when receive HTTP request 14 | 15 | ## Directives 16 | 17 | ### multi\_listen 18 | 19 | Syntax : multi_listen multiport relationport; 20 | Default : None; 21 | Context : events 22 | 23 | multiport can configured as below: 24 | 25 | address:port 26 | port 27 | unix:path 28 | 29 | when configured with IPv4 or IPv6 port, worker process listen port plus with worker process's slot. For Example, we start four workers, add configured multiport with 9000. worker 0 will listen 9000, worker 1 will listen 9001, worker 2 will listen 9002, worker 3 will listen 9003 30 | 31 | when configured with unix path, worker will listen path plus with suffix of worker process's slot. For Example, we start four workers, add configured multiport with unix:/tmp/http. worker 0 will listen /tmp/http.0, worker 1 will listen /tmp/http.1, worker 2 will listen /tmp/http.2, worker 3 will listen /tmp/http.3 32 | 33 | 34 | relationport must configured same as listen directives in http server, rtmp server, stream server or other server 35 | 36 | ### inner\_proxy 37 | 38 | Syntax : inner_proxy multiport uri; 39 | Default : None; 40 | Context : http, server, location 41 | 42 | - multiport: configured in multi_listen 43 | - uri: uri for inner_proxy, configured as below 44 | 45 | location /multiport_test/ { 46 | inner_proxy unix:/tmp/http.sock.80 /inner_proxy; 47 | multiport_test; 48 | } 49 | 50 | location /inner_proxy/ { 51 | rewrite ^/inner_proxy/(.*):/(.*) /$2 break; 52 | proxy_pass http://$1:; 53 | } 54 | 55 | As example above, if send subrequest to process whose workerid is 0, the uri will change to /inner_proxy/unix:/tmp/http.sock.80.0:/multiport_test/xxx 56 | 57 | proxy_pass will send current request to process 0 as inner proxy request. 58 | 59 | ## API 60 | 61 | - ngx\_multiport\_get\_port 62 | 63 | ngx_int_t ngx_event_multiport_get_port(ngx_pool_t *pool, ngx_str_t *port, ngx_str_t *multiport, ngx_int_t pslot); 64 | 65 | - para: 66 | 67 | pool: pool for port memory alloc 68 | port: process real listen port while process\_slot is pslot 69 | multiport: port configure for processes, format as below: 70 | 71 | port only: port 72 | IPv4: host:port host must be ipaddr of IPv4 or * 73 | IPv6: [host]:port host must be ipaddr of IPv6 74 | Unix: unix:/path 75 | 76 | pslot: process\_slot, process\_slot of other worker process can get through ngx\_process\_slot\_get\_slot 77 | 78 | - return value: 79 | 80 | NGX\_OK for successd, NGX\_ERROR for failed 81 | 82 | - ngx\_multiport\_get\_slot 83 | 84 | ngx_int_t ngx_multiport_get_slot(ngx_uint_t wpid); 85 | 86 | - para: 87 | 88 | wpid: worker process id, 0 to ccf->worker_processes - 1 89 | 90 | - return value: 91 | 92 | ngx_process_slot for successd, NGX_ERROR for failed 93 | 94 | - ngx\_http\_inner\_proxy\_request 95 | 96 | ngx_int_t ngx_http_inner_proxy_request(ngx_http_request_t *r, ngx_int_t pslot); 97 | 98 | send a inner proxy request to specific process, must use with directives inner\_proxy 99 | 100 | - paras: 101 | 102 | - r: http request for send inner request to sibling worker 103 | - pslot: sibling worker ngx_process_slot 104 | 105 | - return values: 106 | 107 | - NGX_OK: for successd 108 | - NGX_ERROR: for failed 109 | - NGX_DECLINED: for not configured or send inner proxy to self 110 | 111 | ## Build 112 | 113 | cd to NGINX source directory & run this: 114 | 115 | ./configure --add-module=/path/to/nginx-multiport-module/ 116 | make && make install 117 | 118 | ## Example 119 | 120 | See t/ngx\_http\_process\_slot\_test\_module.c as reference 121 | 122 | **Build**: 123 | 124 | ./configure --with-debug --with-ipv6 --add-module=/path/to/nginx-multiport-module/t/ --add-module=/path/to/nginx-multiport-module/ --add-module=/path/to/echo-nginx-module/ 125 | 126 | make && make install 127 | 128 | **Configure**: 129 | 130 | worker_processes 4; 131 | 132 | events { 133 | ... 134 | 135 | multi_listen 9000 80; 136 | multi_listen unix:/tmp/http.sock.80 80; 137 | } 138 | 139 | http { 140 | ... 141 | 142 | server { 143 | ... 144 | 145 | location /multiport_test/ { 146 | inner_proxy unix:/tmp/http.sock.80 /inner_proxy; 147 | multiport_test; 148 | } 149 | 150 | location /inner_proxy/ { 151 | rewrite ^/inner_proxy/(.*):/(.*) /$2 break; 152 | proxy_pass http://$1:; 153 | } 154 | } 155 | } 156 | 157 | **Test for API**: 158 | 159 | $ curl http://192.168.84.254/multiport_test/123 160 | TEST cases 19, 19 pass 161 | 162 | If request send to worker1 to worker3, the request will proxy to worker 0. will get log as below: 163 | 164 | 2017/10/14 20:45:44 [error] 20065#0: *6 multiport test handler, client: 192.168.84.1, server: localhost, request: "GET /multiport_test/123 HTTP/1.1", host: "192.168.84.254:9003" 165 | 2017/10/14 20:45:44 [error] 20065#0: *6 inner proxy return 0, client: 192.168.84.1, server: localhost, request: "GET /multiport_test/123 HTTP/1.1", host: "192.168.84.254:9003" 166 | 2017/10/14 20:45:44 [error] 20062#0: *8 multiport test handler, client: unix:, server: localhost, request: "GET //multiport_test/123 HTTP/1.0", host: "localhost" 167 | 168 | **Test for multiport**: 169 | 170 | curl -v http://127.0.0.1/ 171 | curl -v http://127.0.0.1:9000/ 172 | curl -v http://127.0.0.1:9001/ 173 | curl -v http://127.0.0.1:9002/ 174 | curl -v http://127.0.0.1:9003/ 175 | 176 | curl -v --unix-socket /tmp/http.sock.80.0 http:/ 177 | curl -v --unix-socket /tmp/http.sock.80.1 http:/ 178 | curl -v --unix-socket /tmp/http.sock.80.2 http:/ 179 | curl -v --unix-socket /tmp/http.sock.80.3 http:/ 180 | 181 | Tests will get the same result, for port 9000 will always send to worker process 0, 9001 to worker process 1 and so on 182 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_multiport_module 2 | 3 | EVENT_MODULES="$EVENT_MODULES \ 4 | ngx_event_multiport_module \ 5 | " 6 | 7 | CORE_MODULES="$CORE_MODULES \ 8 | ngx_process_slot_module \ 9 | ngx_stream_zone_module \ 10 | " 11 | 12 | HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES \ 13 | ngx_http_broadcast_module \ 14 | ngx_http_inner_proxy_module \ 15 | " 16 | 17 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ 18 | $ngx_addon_dir/ngx_multiport_misc.c \ 19 | $ngx_addon_dir/ngx_event_multiport_module.c \ 20 | $ngx_addon_dir/ngx_process_slot_module.c \ 21 | $ngx_addon_dir/ngx_stream_zone_module.c \ 22 | $ngx_addon_dir/ngx_http_broadcast_module.c \ 23 | $ngx_addon_dir/ngx_http_inner_proxy_module.c \ 24 | " 25 | 26 | NGX_ADDON_DEPS="$NGX_ADDON_DEPS \ 27 | $ngx_addon_dir/ngx_multiport.h \ 28 | $ngx_addon_dir/ngx_stream_zone_module.h \ 29 | " 30 | 31 | CFLAGS="$CFLAGS -I $ngx_addon_dir" 32 | -------------------------------------------------------------------------------- /doc/ngx-http-broadcast-module.md: -------------------------------------------------------------------------------- 1 | # ngx-http-broadcast-module 2 | --- 3 | ## Instructions 4 | 5 | Broadcast HTTP request to all worker processes when receive HTTP request 6 | 7 | ## Directives 8 | 9 | ### broadcast 10 | 11 | Syntax : broadcast multiport uri; 12 | Default : None; 13 | Context : location 14 | 15 | - multiport is multi_listen port configured in event 16 | - uri is http proxy_pass uri configured as below 17 | 18 | 19 | location /auth_proxy/ { 20 | rewrite ^/auth_proxy/(.*) /auth break; 21 | proxy_pass http://$1:; 22 | } 23 | 24 | 25 | ## Build 26 | 27 | cd to NGINX source directory & run this: 28 | 29 | ./configure --add-module=/path/to/nginx-multiport-module/ 30 | make && make install 31 | 32 | ## Example 33 | 34 | **Build**: 35 | 36 | ./configure --with-debug --with-ipv6 --add-module=/path/to/nginx-multiport-module/t/ --add-module=/path/to/nginx-multiport-module/ --add-module=/path/to/echo-nginx-module/ 37 | make && make install 38 | 39 | **Configure**: 40 | 41 | events { 42 | ... 43 | multi_listen unix:/tmp/http.sock.80 80; 44 | } 45 | 46 | 47 | http { 48 | ... 49 | 50 | server { 51 | listen 80; 52 | server_name localhost; 53 | 54 | ... 55 | 56 | location / { 57 | broadcast unix:/tmp/http.sock.80 /auth_proxy; 58 | } 59 | 60 | location /auth_proxy/ { 61 | rewrite ^/auth_proxy/(.*) /auth break; 62 | proxy_pass http://$1:; 63 | } 64 | 65 | location /auth { 66 | # return 403; 67 | echo "auth"; 68 | echo $scheme://$host$uri?$args; 69 | } 70 | } 71 | } 72 | 73 | **Test**: 74 | 75 | curl -v 'http://192.168.84.254/aa?a=b&c=d' 76 | 77 | curl will get all response content if worker not return non 200 response -------------------------------------------------------------------------------- /doc/ngx-stream-zone-module.md: -------------------------------------------------------------------------------- 1 | # ngx-stream-zone-module 2 | --- 3 | ## Instructions 4 | 5 | Record stream's owner worker process slot 6 | 7 | ## Directives 8 | 9 | ### stream\_zone 10 | 11 | Syntax : stream_zone buckets=$nbuckets streams=$nstreams; 12 | Default : None; 13 | Context : main 14 | 15 | nbuckets is hash buckect number, nstreams is max streams system can store 16 | 17 | nbuckets is recommended use a prime number 18 | 19 | ## API 20 | 21 | **header file** 22 | 23 | For using this API, You should include the header file as below: 24 | 25 | #include "ngx_stream_zone_module.h" 26 | 27 | **ngx\_stream\_zone\_insert\_stream** 28 | 29 | ngx_int_t ngx_stream_zone_insert_stream(ngx_str_t *name); 30 | 31 | - para: 32 | 33 | name: stream name 34 | 35 | - return value: 36 | 37 | process\_slot for owner of stream, NGX\_ERROR for error 38 | 39 | **ngx\_stream\_zone\_delete\_stream** 40 | 41 | void ngx_stream_zone_delete_stream(ngx_str_t *name); 42 | 43 | - para: 44 | 45 | name: stream name 46 | 47 | **ngx\_stream\_zone\_state** 48 | 49 | ngx_chain_t *ngx_stream_zone_state(ngx_http_request_t *r, ngx_flag_t detail); 50 | 51 | - para: 52 | 53 | - r: http request to query status of rbuf 54 | - detail: print stream detail in log 55 | 56 | - return value: 57 | 58 | chain of stream zone state for returning to http client 59 | 60 | ## Build 61 | 62 | cd to NGINX source directory & run this: 63 | 64 | ./configure --add-module=/path/to/nginx-multiport-module/ 65 | make && make install 66 | 67 | ## Example 68 | 69 | See t/ngx\_stream\_zone\_test\_module.c as reference 70 | 71 | **Build**: 72 | 73 | ./configure --with-debug --with-ipv6 --add-module=/path/to/nginx-multiport-module/t/ --add-module=/path/to/nginx-multiport-module/ 74 | make && make install 75 | 76 | **Configure**: 77 | 78 | stream_zone buckets=10007 streams=10000; 79 | 80 | **Test**: 81 | 82 | curl -XPOST -v "http://127.0.0.1:9001/stream_zone_test/ab?stream=test" 83 | curl -XPOST -v "http://127.0.0.1:9002/stream_zone_test/ab?stream=test1" 84 | curl -XPOST -v "http://127.0.0.1:9003/stream_zone_test/ab?stream=test2" 85 | 86 | curl -XPOST -v "http://127.0.0.1:9003/stream_zone_test/ab?stream=test" 87 | 88 | curl -XDELETE -v "http://127.0.0.1:9000/stream_zone_test/ab?stream=test3" 89 | curl -XDELETE -v "http://127.0.0.1:9002/stream_zone_test/ab?stream=test1" 90 | curl -XDELETE -v "http://127.0.0.1:9001/stream_zone_test/ab?stream=test2" 91 | 92 | curl -XGET -v "http://127.0.0.1:9001/stream_zone_test/ab" -------------------------------------------------------------------------------- /ngx_event_multiport_module.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com 3 | */ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | #include "ngx_multiport.h" 10 | 11 | 12 | static ngx_int_t ngx_event_multiport_process_init(ngx_cycle_t *cycle); 13 | static void ngx_event_multiport_process_exit(ngx_cycle_t *cycle); 14 | 15 | static void *ngx_event_multiport_create_conf(ngx_cycle_t *cycle); 16 | static char *ngx_event_multiport_init_conf(ngx_cycle_t *cycle, void *conf); 17 | 18 | static char *ngx_event_multiport_listen(ngx_conf_t *cf, ngx_command_t *cmd, 19 | void *conf); 20 | 21 | 22 | //multiport_listen listenparas relationport 23 | typedef struct { 24 | ngx_str_t multiport; 25 | ngx_str_t relatedport; 26 | ngx_listening_t listening; 27 | } ngx_event_multiport_listen_t; 28 | 29 | typedef struct { 30 | ngx_array_t *ports; /* array of ngx_event_multiport_listen_t */ 31 | } ngx_event_multiport_conf_t; 32 | 33 | 34 | static ngx_str_t event_multiport_name = ngx_string("event_multiport"); 35 | 36 | 37 | static ngx_command_t ngx_event_multiport_commands[] = { 38 | 39 | { ngx_string("multi_listen"), 40 | NGX_EVENT_CONF|NGX_CONF_TAKE2, 41 | ngx_event_multiport_listen, 42 | 0, 43 | 0, 44 | NULL }, 45 | 46 | ngx_null_command 47 | }; 48 | 49 | 50 | ngx_event_module_t ngx_event_multiport_module_ctx = { 51 | &event_multiport_name, 52 | ngx_event_multiport_create_conf, /* create configuration */ 53 | ngx_event_multiport_init_conf, /* init configuration */ 54 | 55 | { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } 56 | }; 57 | 58 | 59 | /* this module use ngx_cycle->log */ 60 | ngx_module_t ngx_event_multiport_module = { 61 | NGX_MODULE_V1, 62 | &ngx_event_multiport_module_ctx, /* module context */ 63 | ngx_event_multiport_commands, /* module directives */ 64 | NGX_EVENT_MODULE, /* module type */ 65 | NULL, /* init master */ 66 | NULL, /* init module */ 67 | ngx_event_multiport_process_init, /* init process */ 68 | NULL, /* init thread */ 69 | NULL, /* exit thread */ 70 | ngx_event_multiport_process_exit, /* exit process */ 71 | NULL, /* exit master */ 72 | NGX_MODULE_V1_PADDING 73 | }; 74 | 75 | 76 | static void * 77 | ngx_event_multiport_create_conf(ngx_cycle_t *cycle) 78 | { 79 | ngx_event_multiport_conf_t *conf; 80 | 81 | conf = ngx_pcalloc(cycle->pool, sizeof(ngx_event_multiport_conf_t)); 82 | if (conf == NULL) { 83 | return NULL; 84 | } 85 | 86 | return conf; 87 | } 88 | 89 | static char * 90 | ngx_event_multiport_init_conf(ngx_cycle_t *cycle, void *conf) 91 | { 92 | return NGX_CONF_OK; 93 | } 94 | 95 | static char * 96 | ngx_event_multiport_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 97 | { 98 | ngx_event_multiport_conf_t *emcf = conf; 99 | 100 | ngx_str_t *value; 101 | ngx_event_multiport_listen_t *mls; 102 | ngx_url_t u; 103 | 104 | value = cf->args->elts; 105 | 106 | ngx_memzero(&u, sizeof(ngx_url_t)); 107 | 108 | /* check listen base port */ 109 | u.url = value[1]; 110 | u.listen = 1; 111 | u.default_port = 0; 112 | 113 | if (ngx_parse_url(cf->temp_pool, &u) != NGX_OK) { 114 | if (u.err) { 115 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 116 | "%s in \"%V\" of the \"multi_listen" 117 | " baseport\" directive", 118 | u.err, &u.url); 119 | } 120 | 121 | return NGX_CONF_ERROR; 122 | } 123 | 124 | ngx_memzero(&u, sizeof(ngx_url_t)); 125 | 126 | /* check relation port */ 127 | u.url = value[2]; 128 | u.listen = 1; 129 | u.default_port = 0; 130 | 131 | if (ngx_parse_url(cf->temp_pool, &u) != NGX_OK) { 132 | if (u.err) { 133 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 134 | "%s in \"%V\" of the \"multi_listen" 135 | " relationport\" directive", 136 | u.err, &u.url); 137 | } 138 | 139 | return NGX_CONF_ERROR; 140 | } 141 | 142 | if (emcf->ports == NULL) { 143 | emcf->ports = ngx_array_create(cf->pool, 4, 144 | sizeof(ngx_event_multiport_listen_t)); 145 | if (emcf->ports == NULL) { 146 | return NGX_CONF_ERROR; 147 | } 148 | } 149 | 150 | mls = ngx_array_push(emcf->ports); 151 | if (mls == NULL) { 152 | return NGX_CONF_ERROR; 153 | } 154 | mls->multiport = value[1]; 155 | mls->relatedport = value[2]; 156 | 157 | return NGX_CONF_OK; 158 | } 159 | 160 | 161 | static ngx_int_t 162 | ngx_event_multiport_init_listening(ngx_cycle_t *cycle, ngx_listening_t *ls, 163 | void *sockaddr, socklen_t socklen, ngx_listening_t *cls) 164 | { 165 | size_t len; 166 | struct sockaddr *sa; 167 | u_char text[NGX_SOCKADDR_STRLEN]; 168 | 169 | ngx_memzero(ls, sizeof(ngx_listening_t)); 170 | 171 | sa = ngx_palloc(cycle->pool, socklen); 172 | if (sa == NULL) { 173 | return NGX_ERROR; 174 | } 175 | 176 | ngx_memcpy(sa, sockaddr, socklen); 177 | 178 | ls->sockaddr = sa; 179 | ls->socklen = socklen; 180 | 181 | len = ngx_sock_ntop(sa, socklen, text, NGX_SOCKADDR_STRLEN, 1); 182 | ls->addr_text.len = len; 183 | 184 | switch (ls->sockaddr->sa_family) { 185 | #if (NGX_HAVE_INET6) 186 | case AF_INET6: 187 | ls->addr_text_max_len = NGX_INET6_ADDRSTRLEN; 188 | break; 189 | #endif 190 | #if (NGX_HAVE_UNIX_DOMAIN) 191 | case AF_UNIX: 192 | ls->addr_text_max_len = NGX_UNIX_ADDRSTRLEN; 193 | len++; 194 | break; 195 | #endif 196 | case AF_INET: 197 | ls->addr_text_max_len = NGX_INET_ADDRSTRLEN; 198 | break; 199 | default: 200 | ls->addr_text_max_len = NGX_SOCKADDR_STRLEN; 201 | break; 202 | } 203 | 204 | ls->addr_text.data = ngx_pnalloc(cycle->pool, len); 205 | if (ls->addr_text.data == NULL) { 206 | return NGX_ERROR; 207 | } 208 | 209 | ngx_memcpy(ls->addr_text.data, text, len); 210 | 211 | #if (NGX_HAVE_UNIX_DOMAIN) 212 | if (ls->sockaddr->sa_family == AF_UNIX) { 213 | u_char *name = ls->addr_text.data + sizeof("unix:") - 1; 214 | ngx_delete_file(name); 215 | } 216 | #endif 217 | 218 | ls->fd = (ngx_socket_t) -1; 219 | ls->type = SOCK_STREAM; 220 | 221 | ls->backlog = cls->backlog; 222 | ls->rcvbuf = cls->rcvbuf; 223 | ls->sndbuf = cls->sndbuf; 224 | 225 | #if (NGX_HAVE_KEEPALIVE_TUNABLE) 226 | ls->keepidle = cls->keepidle; 227 | ls->keepintvl = cls->keepintvl; 228 | ls->keepcnt = cls->keepcnt; 229 | #endif 230 | 231 | ls->addr_ntop = cls->addr_ntop; 232 | ls->handler = cls->handler; 233 | ls->servers = cls->servers; 234 | ls->worker = ngx_worker; 235 | 236 | ls->log= cls->log; 237 | ls->logp = cls->logp; 238 | 239 | ls->pool_size = cls->pool_size; 240 | ls->post_accept_buffer_size = cls->post_accept_buffer_size; 241 | ls->post_accept_timeout = cls->post_accept_timeout; 242 | 243 | #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) 244 | ls->ipv6only = cls->ipv6only; 245 | #endif 246 | 247 | ls->keepalive = cls->keepalive; 248 | 249 | #if (NGX_HAVE_DEFERRED_ACCEPT) 250 | ls->deferred_accept = cls->deferred_accept; 251 | ls->delete_deferred = cls->delete_deferred; 252 | ls->add_deferred = cls->add_deferred; 253 | #endif 254 | 255 | #ifdef SO_ACCEPTFILTER 256 | ls->accept_filter = cls->accept_filter; 257 | #endif 258 | 259 | #if (NGX_HAVE_SETFIB) 260 | ls->setfib = cls->setfib; 261 | #endif 262 | 263 | #if (NGX_HAVE_TCP_FASTOPEN) 264 | ls->fastopen = cls->fastopen; 265 | #endif 266 | 267 | return NGX_OK; 268 | } 269 | 270 | static ngx_int_t 271 | ngx_event_multiport_open_listening_sock(ngx_cycle_t *cycle, ngx_listening_t *ls) 272 | { 273 | int reuseaddr; 274 | ngx_uint_t tries, failed; 275 | ngx_err_t err; 276 | ngx_log_t *log; 277 | ngx_socket_t s; 278 | 279 | reuseaddr = 1; 280 | #if (NGX_SUPPRESS_WARN) 281 | failed = 0; 282 | #endif 283 | 284 | log = cycle->log; 285 | 286 | /* TODO: configurable try number */ 287 | 288 | for (tries = 5; tries; tries--) { 289 | failed = 0; 290 | 291 | if (ls->fd != (ngx_socket_t) -1) { 292 | return NGX_OK; 293 | } 294 | 295 | s = ngx_socket(ls->sockaddr->sa_family, ls->type, 0); 296 | 297 | if (s == (ngx_socket_t) -1) { 298 | ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, 299 | "multiport, " ngx_socket_n " %V failed", 300 | &ls->addr_text); 301 | return NGX_ERROR; 302 | } 303 | 304 | if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 305 | (const void *) &reuseaddr, sizeof(int)) 306 | == -1) 307 | { 308 | ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, 309 | "multiport, setsockopt(SO_REUSEADDR) %V failed", 310 | &ls->addr_text); 311 | 312 | if (ngx_close_socket(s) == -1) { 313 | ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, 314 | "multiport, " ngx_close_socket_n " %V failed", 315 | &ls->addr_text); 316 | } 317 | 318 | return NGX_ERROR; 319 | } 320 | 321 | #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) 322 | 323 | if (ls->sockaddr->sa_family == AF_INET6) { 324 | int ipv6only; 325 | 326 | ipv6only = ls->ipv6only; 327 | 328 | if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, 329 | (const void *) &ipv6only, sizeof(int)) 330 | == -1) 331 | { 332 | ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, 333 | "multiport, setsockopt(IPV6_V6ONLY) %V failed," 334 | " ignored", &ls->addr_text); 335 | } 336 | } 337 | #endif 338 | /* TODO: close on exit */ 339 | 340 | if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) { 341 | if (ngx_nonblocking(s) == -1) { 342 | ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, 343 | "multiport, " ngx_nonblocking_n " %V failed", 344 | &ls->addr_text); 345 | 346 | if (ngx_close_socket(s) == -1) { 347 | ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, 348 | "multiport, " ngx_close_socket_n " %V failed", 349 | &ls->addr_text); 350 | } 351 | 352 | return NGX_ERROR; 353 | } 354 | } 355 | 356 | ngx_log_debug2(NGX_LOG_DEBUG_CORE, log, 0, 357 | "multiport, bind() %V #%d ", &ls->addr_text, s); 358 | 359 | if (bind(s, ls->sockaddr, ls->socklen) == -1) { 360 | ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, 361 | "multiport, bind() to %V failed", &ls->addr_text); 362 | 363 | if (ngx_close_socket(s) == -1) { 364 | ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, 365 | "multiport, " ngx_close_socket_n " %V failed", 366 | &ls->addr_text); 367 | } 368 | 369 | return NGX_ERROR; 370 | } 371 | 372 | #if (NGX_HAVE_UNIX_DOMAIN) 373 | 374 | if (ls->sockaddr->sa_family == AF_UNIX) { 375 | mode_t mode; 376 | u_char *name; 377 | 378 | name = ls->addr_text.data + sizeof("unix:") - 1; 379 | mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); 380 | 381 | if (chmod((char *) name, mode) == -1) { 382 | ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, 383 | "multiport, chmod() \"%s\" failed", name); 384 | } 385 | } 386 | #endif 387 | 388 | if (ls->type != SOCK_STREAM) { 389 | ls->fd = s; 390 | continue; 391 | } 392 | 393 | if (listen(s, ls->backlog) == -1) { 394 | err = ngx_socket_errno; 395 | 396 | /* 397 | * on OpenVZ after suspend/resume EADDRINUSE 398 | * may be returned by listen() instead of bind(), see 399 | * https://bugzilla.openvz.org/show_bug.cgi?id=2470 400 | */ 401 | 402 | if (ngx_close_socket(s) == -1) { 403 | ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, 404 | "multiport, " ngx_close_socket_n " %V failed", 405 | &ls->addr_text); 406 | } 407 | 408 | if (err != NGX_EADDRINUSE) { 409 | ngx_log_error(NGX_LOG_EMERG, log, err, 410 | "multiport, listen() to %V, backlog %d failed", 411 | &ls->addr_text, ls->backlog); 412 | return NGX_ERROR; 413 | } 414 | 415 | failed = 1; 416 | } 417 | 418 | ls->listen = 1; 419 | ls->fd = s; 420 | 421 | if (!failed) { 422 | break; 423 | } 424 | 425 | /* TODO: delay configurable */ 426 | 427 | ngx_log_error(NGX_LOG_NOTICE, log, 0, 428 | "multiport, try again to bind() after 500ms"); 429 | 430 | ngx_msleep(500); 431 | } 432 | 433 | if (failed) { 434 | ngx_log_error(NGX_LOG_EMERG, log, 0, 435 | "multiport, still could not bind()"); 436 | return NGX_ERROR; 437 | } 438 | 439 | return NGX_OK; 440 | } 441 | 442 | static ngx_int_t 443 | ngx_event_multiport_configure_listening_socket(ngx_cycle_t *cycle, 444 | ngx_listening_t *ls) 445 | { 446 | int value; 447 | 448 | #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) 449 | struct accept_filter_arg af; 450 | #endif 451 | 452 | ls->log = *ls->logp; 453 | 454 | if (ls->rcvbuf != -1) { 455 | if (setsockopt(ls->fd, SOL_SOCKET, SO_RCVBUF, 456 | (const void *) &ls->rcvbuf, sizeof(int)) 457 | == -1) 458 | { 459 | ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, 460 | "multiport, setsockopt(SO_RCVBUF, %d) %V failed," 461 | " ignored", ls->rcvbuf, &ls->addr_text); 462 | } 463 | } 464 | 465 | if (ls->sndbuf != -1) { 466 | if (setsockopt(ls->fd, SOL_SOCKET, SO_SNDBUF, 467 | (const void *) &ls->sndbuf, sizeof(int)) 468 | == -1) 469 | { 470 | ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, 471 | "multiport, setsockopt(SO_SNDBUF, %d) %V failed," 472 | " ignored", ls->sndbuf, &ls->addr_text); 473 | } 474 | } 475 | 476 | if (ls->keepalive) { 477 | value = (ls->keepalive == 1) ? 1 : 0; 478 | 479 | if (setsockopt(ls->fd, SOL_SOCKET, SO_KEEPALIVE, 480 | (const void *) &value, sizeof(int)) 481 | == -1) 482 | { 483 | ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, 484 | "multiport, setsockopt(SO_KEEPALIVE, %d) %V failed," 485 | " ignored", value, &ls->addr_text); 486 | } 487 | } 488 | 489 | #if (NGX_HAVE_KEEPALIVE_TUNABLE) 490 | 491 | if (ls->keepidle) { 492 | value = ls->keepidle; 493 | 494 | #if (NGX_KEEPALIVE_FACTOR) 495 | value *= NGX_KEEPALIVE_FACTOR; 496 | #endif 497 | 498 | if (setsockopt(ls->fd, IPPROTO_TCP, TCP_KEEPIDLE, 499 | (const void *) &value, sizeof(int)) 500 | == -1) 501 | { 502 | ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, 503 | "multiport, setsockopt(TCP_KEEPIDLE, %d) %V failed," 504 | " ignored", value, &ls->addr_text); 505 | } 506 | } 507 | 508 | if (ls->keepintvl) { 509 | value = ls->keepintvl; 510 | 511 | #if (NGX_KEEPALIVE_FACTOR) 512 | value *= NGX_KEEPALIVE_FACTOR; 513 | #endif 514 | 515 | if (setsockopt(ls->fd, IPPROTO_TCP, TCP_KEEPINTVL, 516 | (const void *) &value, sizeof(int)) 517 | == -1) 518 | { 519 | ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, 520 | "multiport, setsockopt(TCP_KEEPINTVL, %d) %V failed," 521 | " ignored", value, &ls->addr_text); 522 | } 523 | } 524 | 525 | if (ls->keepcnt) { 526 | if (setsockopt(ls->fd, IPPROTO_TCP, TCP_KEEPCNT, 527 | (const void *) &ls->keepcnt, sizeof(int)) 528 | == -1) 529 | { 530 | ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, 531 | "multiport, setsockopt(TCP_KEEPCNT, %d) %V failed," 532 | " ignored", ls->keepcnt, &ls->addr_text); 533 | } 534 | } 535 | 536 | #endif 537 | 538 | #if (NGX_HAVE_SETFIB) 539 | if (ls->setfib != -1) { 540 | if (setsockopt(ls->fd, SOL_SOCKET, SO_SETFIB, 541 | (const void *) &ls->setfib, sizeof(int)) 542 | == -1) 543 | { 544 | ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, 545 | "multiport, setsockopt(SO_SETFIB, %d) %V failed," 546 | " ignored", ls->setfib, &ls->addr_text); 547 | } 548 | } 549 | #endif 550 | 551 | #if (NGX_HAVE_TCP_FASTOPEN) 552 | if (ls->fastopen != -1) { 553 | if (setsockopt(ls->fd, IPPROTO_TCP, TCP_FASTOPEN, 554 | (const void *) &ls->fastopen, sizeof(int)) 555 | == -1) 556 | { 557 | ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, 558 | "multiport, setsockopt(TCP_FASTOPEN, %d) %V failed," 559 | " ignored", ls->fastopen, &ls->addr_text); 560 | } 561 | } 562 | #endif 563 | 564 | if (ls->listen) { 565 | 566 | /* change backlog via listen() */ 567 | 568 | if (listen(ls->fd, ls->backlog) == -1) { 569 | ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, 570 | "multiport, listen() to %V, backlog %d failed", 571 | &ls->addr_text, ls->backlog); 572 | return NGX_ERROR; 573 | } 574 | } 575 | 576 | /* 577 | * setting deferred mode should be last operation on socket, 578 | * because code may prematurely continue cycle on failure 579 | */ 580 | 581 | #if (NGX_HAVE_DEFERRED_ACCEPT) 582 | 583 | #ifdef SO_ACCEPTFILTER 584 | 585 | if (ls->delete_deferred) { 586 | if (setsockopt(ls->fd, SOL_SOCKET, SO_ACCEPTFILTER, NULL, 0) 587 | == -1) 588 | { 589 | ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, 590 | "multiport, setsockopt(SO_ACCEPTFILTER, NULL) " 591 | "for %V failed, ignored", &ls->addr_text); 592 | 593 | if (ls->accept_filter) { 594 | ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, 595 | "multiport, could not change the accept filter " 596 | "to \"%s\" for %V, ignored", 597 | ls->accept_filter, &ls->addr_text); 598 | } 599 | 600 | continue; 601 | } 602 | 603 | ls->deferred_accept = 0; 604 | } 605 | 606 | if (ls->add_deferred) { 607 | ngx_memzero(&af, sizeof(struct accept_filter_arg)); 608 | (void) ngx_cpystrn((u_char *) af.af_name, 609 | (u_char *) ls->accept_filter, 16); 610 | 611 | if (setsockopt(ls->fd, SOL_SOCKET, SO_ACCEPTFILTER, 612 | &af, sizeof(struct accept_filter_arg)) 613 | == -1) 614 | { 615 | ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, 616 | "multiport, setsockopt(SO_ACCEPTFILTER, \"%s\") " 617 | "for %V failed, ignored", 618 | ls->accept_filter, &ls->addr_text); 619 | continue; 620 | } 621 | 622 | ls->deferred_accept = 1; 623 | } 624 | 625 | #endif 626 | 627 | #ifdef TCP_DEFER_ACCEPT 628 | 629 | if (ls->add_deferred || ls->delete_deferred) { 630 | 631 | if (ls->add_deferred) { 632 | /* 633 | * There is no way to find out how long a connection was 634 | * in queue (and a connection may bypass deferred queue at all 635 | * if syncookies were used), hence we use 1 second timeout 636 | * here. 637 | */ 638 | value = 1; 639 | 640 | } else { 641 | value = 0; 642 | } 643 | 644 | if (setsockopt(ls->fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, 645 | &value, sizeof(int)) 646 | == -1) 647 | { 648 | ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, 649 | "multiport, setsockopt(TCP_DEFER_ACCEPT, %d)" 650 | " for %V failed, ignored", value, &ls->addr_text); 651 | 652 | return NGX_ERROR; 653 | } 654 | } 655 | 656 | if (ls->add_deferred) { 657 | ls->deferred_accept = 1; 658 | } 659 | 660 | #endif 661 | 662 | #endif /* NGX_HAVE_DEFERRED_ACCEPT */ 663 | 664 | #if (NGX_HAVE_IP_RECVDSTADDR) 665 | 666 | if (ls->wildcard 667 | && ls->type == SOCK_DGRAM 668 | && ls->sockaddr->sa_family == AF_INET) 669 | { 670 | value = 1; 671 | 672 | if (setsockopt(ls->fd, IPPROTO_IP, IP_RECVDSTADDR, 673 | (const void *) &value, sizeof(int)) 674 | == -1) 675 | { 676 | ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, 677 | "multiport, setsockopt(IP_RECVDSTADDR) " 678 | "for %V failed, ignored", &ls->addr_text); 679 | } 680 | } 681 | 682 | #elif (NGX_HAVE_IP_PKTINFO) 683 | 684 | if (ls->wildcard 685 | && ls->type == SOCK_DGRAM 686 | && ls->sockaddr->sa_family == AF_INET) 687 | { 688 | value = 1; 689 | 690 | if (setsockopt(ls->fd, IPPROTO_IP, IP_PKTINFO, 691 | (const void *) &value, sizeof(int)) 692 | == -1) 693 | { 694 | ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, 695 | "multiport, setsockopt(IP_PKTINFO) " 696 | "for %V failed, ignored", &ls->addr_text); 697 | } 698 | } 699 | 700 | #endif 701 | 702 | #if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO) 703 | 704 | if (ls->wildcard 705 | && ls->type == SOCK_DGRAM 706 | && ls->sockaddr->sa_family == AF_INET6) 707 | { 708 | value = 1; 709 | 710 | if (setsockopt(ls->fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, 711 | (const void *) &value, sizeof(int)) 712 | == -1) 713 | { 714 | ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, 715 | "multiport, setsockopt(IPV6_RECVPKTINFO) " 716 | "for %V failed, ignored", &ls->addr_text); 717 | } 718 | } 719 | 720 | #endif 721 | 722 | return NGX_OK; 723 | } 724 | 725 | static void 726 | ngx_event_multiport_close_listening_sock(ngx_cycle_t *cycle, 727 | ngx_listening_t *ls) 728 | { 729 | ngx_connection_t *c; 730 | 731 | c = ls->connection; 732 | 733 | if (c) { 734 | if (c->read->active) { 735 | if (ngx_event_flags & NGX_USE_EPOLL_EVENT) { 736 | 737 | /* 738 | * it seems that Linux-2.6.x OpenVZ sends events 739 | * for closed shared listening sockets unless 740 | * the events was explicitly deleted 741 | */ 742 | 743 | ngx_del_event(c->read, NGX_READ_EVENT, 0); 744 | 745 | } else { 746 | ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT); 747 | } 748 | } 749 | 750 | ngx_free_connection(c); 751 | 752 | c->fd = (ngx_socket_t) -1; 753 | } 754 | 755 | ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, 756 | "multiport, close listening %V #%d", &ls->addr_text, ls->fd); 757 | 758 | if (ngx_close_socket(ls->fd) == -1) { 759 | ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, 760 | "multiport, ", ngx_close_socket_n " %V failed", 761 | &ls->addr_text); 762 | } 763 | 764 | #if (NGX_HAVE_UNIX_DOMAIN) 765 | 766 | if (ls->sockaddr->sa_family == AF_UNIX 767 | && ngx_process == NGX_PROCESS_WORKER) 768 | { 769 | u_char *name = ls->addr_text.data + sizeof("unix:") - 1; 770 | 771 | if (ngx_delete_file(name) == NGX_FILE_ERROR) { 772 | ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, 773 | "multiport, " ngx_delete_file_n " %s failed", name); 774 | } 775 | } 776 | 777 | #endif 778 | 779 | ls->fd = (ngx_socket_t) -1; 780 | } 781 | 782 | static ngx_listening_t * 783 | ngx_event_multiport_find_relation_port(ngx_cycle_t *cycle, ngx_str_t *str) 784 | { 785 | ngx_url_t u; 786 | ngx_listening_t *ls; 787 | ngx_uint_t i; 788 | 789 | ngx_memzero(&u, sizeof(ngx_url_t)); 790 | 791 | u.url = *str; 792 | u.listen = 1; 793 | u.default_port = 0; 794 | 795 | if (ngx_parse_url(cycle->pool, &u) != NGX_OK) { 796 | if (u.err) { 797 | ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, 798 | "multiport, relation port err: %V", str); 799 | } 800 | 801 | return NULL; 802 | } 803 | 804 | ls = cycle->listening.elts; 805 | 806 | for (i = 0; i < cycle->listening.nelts; ++i) { 807 | 808 | if (ls[i].socklen == u.socklen 809 | && ngx_memcmp(ls[i].sockaddr, &u.sockaddr, u.socklen) == 0) 810 | { 811 | return &ls[i]; 812 | } 813 | } 814 | 815 | ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, 816 | "multiport, can not find relation port: %V", str); 817 | return NULL; 818 | } 819 | 820 | static ngx_int_t 821 | ngx_event_multiport_set_port(ngx_cycle_t *cycle, 822 | ngx_event_multiport_listen_t *mls, ngx_listening_t *ls) 823 | { 824 | ngx_str_t port; 825 | ngx_int_t rc; 826 | ngx_url_t u; 827 | 828 | ngx_memzero(&port, sizeof(ngx_str_t)); 829 | 830 | rc = ngx_multiport_get_port(cycle->pool, &port, &mls->multiport, 831 | ngx_process_slot); 832 | if (rc == NGX_ERROR) { 833 | ngx_log_error(NGX_LOG_ERR, cycle->log, 0, 834 | "multiport, get multiport error"); 835 | return NGX_ERROR; 836 | } 837 | 838 | ngx_memzero(&u, sizeof(ngx_url_t)); 839 | u.url = port; 840 | u.listen = 1; 841 | u.default_port = 0; 842 | 843 | if (ngx_parse_url(cycle->pool, &u) != NGX_OK) { 844 | if (u.err) { 845 | ngx_log_error(NGX_LOG_ERR, cycle->log, 0, 846 | "multiport, %s in \"%V\" when set port", u.err, &u.url); 847 | } 848 | 849 | return NGX_ERROR; 850 | } 851 | 852 | /* init multiport listening */ 853 | if (ngx_event_multiport_init_listening(cycle, &mls->listening, 854 | (struct sockaddr *) &u.sockaddr, u.socklen, ls) != NGX_OK) 855 | { 856 | ngx_log_error(NGX_LOG_ERR, cycle->log, 0, 857 | "multiport, init listening error: %V", &mls->multiport); 858 | return NGX_ERROR; 859 | } 860 | 861 | /* open multiport listening */ 862 | if (ngx_event_multiport_open_listening_sock(cycle, &mls->listening) 863 | != NGX_OK) 864 | { 865 | ngx_log_error(NGX_LOG_ERR, cycle->log, 0, 866 | "multiport, open listening error: %V", &mls->multiport); 867 | return NGX_ERROR; 868 | } 869 | 870 | /* config multiport listening */ 871 | if (ngx_event_multiport_configure_listening_socket(cycle, 872 | &mls->listening) != NGX_OK) 873 | { 874 | return NGX_ERROR; 875 | } 876 | 877 | return NGX_OK; 878 | } 879 | 880 | static ngx_int_t 881 | ngx_event_multiport_enable_accept_event(ngx_listening_t *ls) 882 | { 883 | ngx_connection_t *c; 884 | ngx_event_t *rev; 885 | 886 | c = ngx_get_connection(ls->fd, ngx_cycle->log); 887 | 888 | if (c == NULL) { 889 | return NGX_ERROR; 890 | } 891 | 892 | c->type = ls->type; 893 | c->log = &ls->log; 894 | 895 | c->listening = ls; 896 | ls->connection = c; 897 | 898 | rev = c->read; 899 | 900 | rev->log = c->log; 901 | rev->accept = 1; 902 | 903 | #if (NGX_HAVE_DEFERRED_ACCEPT) 904 | rev->deferred_accept = ls->deferred_accept; 905 | #endif 906 | 907 | #if (NGX_WIN32) 908 | 909 | if (ngx_event_flags & NGX_USE_IOCP_EVENT) { 910 | ngx_iocp_conf_t *iocpcf; 911 | 912 | rev->handler = ngx_event_acceptex; 913 | 914 | if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) { 915 | return NGX_ERROR; 916 | } 917 | 918 | ls->log.handler = ngx_acceptex_log_error; 919 | 920 | iocpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module); 921 | if (ngx_event_post_acceptex(ls, iocpcf->post_acceptex) 922 | == NGX_ERROR) 923 | { 924 | return NGX_ERROR; 925 | } 926 | 927 | } else { 928 | rev->handler = ngx_event_accept; 929 | 930 | if (ngx_use_accept_mutex) { 931 | continue; 932 | } 933 | 934 | if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { 935 | return NGX_ERROR; 936 | } 937 | } 938 | 939 | #else 940 | 941 | rev->handler = (c->type == SOCK_STREAM)? ngx_event_accept 942 | : ngx_event_recvmsg; 943 | 944 | if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { 945 | return NGX_ERROR; 946 | } 947 | 948 | #endif 949 | 950 | return NGX_OK; 951 | } 952 | 953 | static ngx_int_t 954 | ngx_event_multiport_process_init(ngx_cycle_t *cycle) 955 | { 956 | ngx_event_multiport_conf_t *emcf; 957 | ngx_event_multiport_listen_t *mls; 958 | ngx_listening_t *rls; 959 | ngx_uint_t i; 960 | 961 | if (ngx_process != NGX_PROCESS_WORKER) { 962 | return NGX_OK; 963 | } 964 | 965 | emcf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_multiport_module); 966 | if (emcf->ports == NULL) { 967 | return NGX_OK; 968 | } 969 | 970 | mls = emcf->ports->elts; 971 | for (i = 0; i < emcf->ports->nelts; ++i) { 972 | /* find relation listening in cycle->listening */ 973 | rls = ngx_event_multiport_find_relation_port(cycle, 974 | &mls[i].relatedport); 975 | if (rls == NULL) { 976 | return NGX_ERROR; 977 | } 978 | 979 | /* set process port with relation port */ 980 | if (ngx_event_multiport_set_port(cycle, &mls[i], rls) != NGX_OK) { 981 | return NGX_ERROR; 982 | } 983 | 984 | /* enable accept */ 985 | if (ngx_event_multiport_enable_accept_event(&mls[i].listening) 986 | != NGX_OK) 987 | { 988 | ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, 989 | "multiport, enable accept failed"); 990 | return NGX_ERROR; 991 | } 992 | } 993 | 994 | return NGX_OK; 995 | } 996 | 997 | static void 998 | ngx_event_multiport_process_exit(ngx_cycle_t *cycle) 999 | { 1000 | ngx_event_multiport_conf_t *emcf; 1001 | ngx_event_multiport_listen_t *mls; 1002 | ngx_uint_t i; 1003 | 1004 | if (ngx_process != NGX_PROCESS_WORKER) { 1005 | return; 1006 | } 1007 | 1008 | emcf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_multiport_module); 1009 | if (emcf->ports == NULL) { 1010 | return; 1011 | } 1012 | 1013 | mls = emcf->ports->elts; 1014 | 1015 | for (i = 0; i < emcf->ports->nelts; ++i) { 1016 | ngx_event_multiport_close_listening_sock(cycle, &mls[i].listening); 1017 | } 1018 | } 1019 | -------------------------------------------------------------------------------- /ngx_http_broadcast_module.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com 3 | */ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | #include "ngx_multiport.h" 10 | 11 | 12 | typedef struct { 13 | ngx_str_t multiport; 14 | ngx_str_t uri; 15 | } ngx_http_broadcast_conf_t; 16 | 17 | typedef struct { 18 | ngx_int_t workerid; 19 | ngx_http_request_t *sr; 20 | } ngx_http_broadcast_ctx_t; 21 | 22 | 23 | static ngx_int_t ngx_http_broadcast_filter_init(ngx_conf_t *cf); 24 | 25 | static void *ngx_http_broadcast_create_conf(ngx_conf_t *cf); 26 | static char *ngx_http_broadcast_merge_conf(ngx_conf_t *cf, 27 | void *parent, void *child); 28 | static char *ngx_http_broadcast(ngx_conf_t *cf, ngx_command_t *cmd, 29 | void *conf); 30 | 31 | 32 | static ngx_command_t ngx_http_broadcast_commands[] = { 33 | 34 | { ngx_string("broadcast"), 35 | NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, 36 | ngx_http_broadcast, 37 | NGX_HTTP_LOC_CONF_OFFSET, 38 | 0, 39 | NULL }, 40 | 41 | ngx_null_command 42 | }; 43 | 44 | 45 | static ngx_http_module_t ngx_http_broadcast_module_ctx = { 46 | NULL, /* preconfiguration */ 47 | ngx_http_broadcast_filter_init, /* postconfiguration */ 48 | 49 | NULL, /* create main configuration */ 50 | NULL, /* init main configuration */ 51 | 52 | NULL, /* create server configuration */ 53 | NULL, /* merge server configuration */ 54 | 55 | ngx_http_broadcast_create_conf, /* create location configuration */ 56 | ngx_http_broadcast_merge_conf /* merge location configuration */ 57 | }; 58 | 59 | 60 | ngx_module_t ngx_http_broadcast_module = { 61 | NGX_MODULE_V1, 62 | &ngx_http_broadcast_module_ctx, /* module context */ 63 | ngx_http_broadcast_commands, /* module directives */ 64 | NGX_HTTP_MODULE, /* module type */ 65 | NULL, /* init master */ 66 | NULL, /* init module */ 67 | NULL, /* init process */ 68 | NULL, /* init thread */ 69 | NULL, /* exit thread */ 70 | NULL, /* exit process */ 71 | NULL, /* exit master */ 72 | NGX_MODULE_V1_PADDING 73 | }; 74 | 75 | 76 | static ngx_http_output_header_filter_pt ngx_http_next_header_filter; 77 | static ngx_http_output_body_filter_pt ngx_http_next_body_filter; 78 | 79 | 80 | static void * 81 | ngx_http_broadcast_create_conf(ngx_conf_t *cf) 82 | { 83 | ngx_http_broadcast_conf_t *conf; 84 | 85 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_broadcast_conf_t)); 86 | if (conf == NULL) { 87 | return NULL; 88 | } 89 | 90 | return conf; 91 | } 92 | 93 | static char * 94 | ngx_http_broadcast_merge_conf(ngx_conf_t *cf, void *parent, void *child) 95 | { 96 | ngx_http_broadcast_conf_t *prev = parent; 97 | ngx_http_broadcast_conf_t *conf = child; 98 | 99 | ngx_conf_merge_str_value(conf->multiport, prev->multiport, ""); 100 | ngx_conf_merge_str_value(conf->uri, prev->uri, ""); 101 | 102 | return NGX_CONF_OK; 103 | } 104 | 105 | static char * 106 | ngx_http_broadcast(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 107 | { 108 | ngx_http_broadcast_conf_t *hbcf; 109 | ngx_str_t *value; 110 | 111 | hbcf = conf; 112 | 113 | if (hbcf->multiport.data != NULL) { 114 | return "is duplicate"; 115 | } 116 | 117 | value = cf->args->elts; 118 | 119 | hbcf->multiport = value[1]; 120 | hbcf->uri = value[2]; 121 | 122 | return NGX_CONF_OK; 123 | } 124 | 125 | 126 | static ngx_int_t 127 | ngx_http_broadcast_header_filter(ngx_http_request_t *r) 128 | { 129 | ngx_http_broadcast_conf_t *hbcf; 130 | 131 | hbcf = ngx_http_get_module_loc_conf(r, ngx_http_broadcast_module); 132 | 133 | if (hbcf == NULL || hbcf->multiport.len == 0) { /* not configured */ 134 | goto next; 135 | } 136 | 137 | ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 138 | "broadcast header filter, r:%p r->main:%p, %ui, %O", 139 | r, r->main, r->headers_out.status, r->headers_out.content_length_n); 140 | 141 | r->headers_out.status = NGX_HTTP_OK; 142 | ngx_http_clear_content_length(r); 143 | ngx_http_clear_accept_ranges(r); 144 | 145 | next: 146 | return ngx_http_next_header_filter(r); 147 | } 148 | 149 | 150 | static ngx_int_t 151 | ngx_http_broadcast_send_subrequest(ngx_http_request_t *r, ngx_int_t pslot) 152 | { 153 | ngx_http_broadcast_conf_t *hbcf; 154 | ngx_str_t uri; 155 | ngx_str_t port; 156 | ngx_http_request_t *sr; 157 | ngx_int_t rc; 158 | 159 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 160 | "broadcast send subrequest to %i", pslot); 161 | 162 | hbcf = ngx_http_get_module_loc_conf(r, ngx_http_broadcast_module); 163 | 164 | if (ngx_multiport_get_port(r->pool, &port, &hbcf->multiport, pslot) 165 | == NGX_ERROR) 166 | { 167 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 168 | "broadcast get port error, %V %i", &hbcf->multiport, pslot); 169 | return NGX_ERROR; 170 | } 171 | 172 | uri.len = hbcf->uri.len + 1 + port.len; 173 | uri.data = ngx_pcalloc(r->pool, uri.len); 174 | ngx_snprintf(uri.data, uri.len, "%V/%V", &hbcf->uri, &port); 175 | 176 | rc = ngx_http_subrequest(r, &uri, &r->args, &sr, NULL, 0); 177 | sr->method = r->method; 178 | sr->method_name = r->method_name; 179 | 180 | return rc; 181 | } 182 | 183 | static ngx_int_t 184 | ngx_http_broadcast_body_filter(ngx_http_request_t *r, ngx_chain_t *in) 185 | { 186 | ngx_http_broadcast_conf_t *hbcf; 187 | ngx_http_broadcast_ctx_t *ctx; 188 | ngx_core_conf_t *ccf; 189 | ngx_int_t rc; 190 | ngx_buf_t *b; 191 | ngx_chain_t cl; 192 | 193 | hbcf = ngx_http_get_module_loc_conf(r->main, ngx_http_broadcast_module); 194 | 195 | if (hbcf == NULL || hbcf->multiport.len == 0) { /* not configured */ 196 | return ngx_http_next_body_filter(r, in); 197 | } 198 | 199 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 200 | "broadcast body filter, r:%p r->main:%p", r, r->main); 201 | 202 | if (r != r->main) { /* send subrequest */ 203 | if (r->headers_out.status != NGX_HTTP_OK) { 204 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 205 | "broadcast subrequest send non 200 response: %i", 206 | r->headers_out.status); 207 | return NGX_OK; 208 | } 209 | 210 | return ngx_http_next_body_filter(r, in); 211 | } 212 | 213 | ctx = ngx_http_get_module_ctx(r, ngx_http_broadcast_module); 214 | 215 | if (ctx == NULL) { 216 | ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_broadcast_ctx_t)); 217 | if (ctx == NULL) { 218 | return NGX_ERROR; 219 | } 220 | 221 | ngx_http_set_ctx(r, ctx, ngx_http_broadcast_module); 222 | } 223 | 224 | /* send to all process */ 225 | 226 | ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, 227 | ngx_core_module); 228 | 229 | while (ctx->workerid < ccf->worker_processes) { 230 | 231 | rc = ngx_http_broadcast_send_subrequest(r, 232 | ngx_multiport_get_slot(ctx->workerid)); 233 | ++ctx->workerid; 234 | 235 | return rc; 236 | } 237 | 238 | b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); 239 | 240 | if (b == NULL) { 241 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 242 | } 243 | 244 | b->last_buf = 1; 245 | 246 | cl.buf = b; 247 | cl.next = NULL; 248 | 249 | return ngx_http_next_body_filter(r, &cl); 250 | } 251 | 252 | 253 | static ngx_int_t 254 | ngx_http_broadcast_filter_init(ngx_conf_t *cf) 255 | { 256 | ngx_http_next_header_filter = ngx_http_top_header_filter; 257 | ngx_http_top_header_filter = ngx_http_broadcast_header_filter; 258 | 259 | ngx_http_next_body_filter = ngx_http_top_body_filter; 260 | ngx_http_top_body_filter = ngx_http_broadcast_body_filter; 261 | 262 | return NGX_OK; 263 | } 264 | -------------------------------------------------------------------------------- /ngx_http_inner_proxy_module.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com 3 | */ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | #include "ngx_multiport.h" 10 | 11 | 12 | typedef struct { 13 | ngx_str_t multiport; 14 | ngx_str_t uri; 15 | } ngx_http_inner_proxy_conf_t; 16 | 17 | typedef struct { 18 | ngx_str_t port; 19 | ngx_flag_t last; 20 | } ngx_http_inner_proxy_ctx_t; 21 | 22 | 23 | static ngx_int_t ngx_http_inner_proxy_filter_init(ngx_conf_t *cf); 24 | 25 | static void *ngx_http_inner_proxy_create_conf(ngx_conf_t *cf); 26 | static char *ngx_http_inner_proxy_merge_conf(ngx_conf_t *cf, 27 | void *parent, void *child); 28 | static char *ngx_http_inner_proxy(ngx_conf_t *cf, ngx_command_t *cmd, 29 | void *conf); 30 | 31 | 32 | static ngx_command_t ngx_http_inner_proxy_commands[] = { 33 | 34 | { ngx_string("inner_proxy"), 35 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, 36 | ngx_http_inner_proxy, 37 | NGX_HTTP_LOC_CONF_OFFSET, 38 | 0, 39 | NULL }, 40 | 41 | ngx_null_command 42 | }; 43 | 44 | 45 | static ngx_http_module_t ngx_http_inner_proxy_module_ctx = { 46 | NULL, /* preconfiguration */ 47 | ngx_http_inner_proxy_filter_init, /* postconfiguration */ 48 | 49 | NULL, /* create main configuration */ 50 | NULL, /* init main configuration */ 51 | 52 | NULL, /* create server configuration */ 53 | NULL, /* merge server configuration */ 54 | 55 | ngx_http_inner_proxy_create_conf, /* create location configuration */ 56 | ngx_http_inner_proxy_merge_conf /* merge location configuration */ 57 | }; 58 | 59 | 60 | ngx_module_t ngx_http_inner_proxy_module = { 61 | NGX_MODULE_V1, 62 | &ngx_http_inner_proxy_module_ctx, /* module context */ 63 | ngx_http_inner_proxy_commands, /* module directives */ 64 | NGX_HTTP_MODULE, /* module type */ 65 | NULL, /* init master */ 66 | NULL, /* init module */ 67 | NULL, /* init process */ 68 | NULL, /* init thread */ 69 | NULL, /* exit thread */ 70 | NULL, /* exit process */ 71 | NULL, /* exit master */ 72 | NGX_MODULE_V1_PADDING 73 | }; 74 | 75 | 76 | static ngx_http_output_header_filter_pt ngx_http_next_header_filter; 77 | static ngx_http_output_body_filter_pt ngx_http_next_body_filter; 78 | 79 | 80 | static void * 81 | ngx_http_inner_proxy_create_conf(ngx_conf_t *cf) 82 | { 83 | ngx_http_inner_proxy_conf_t *conf; 84 | 85 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_inner_proxy_conf_t)); 86 | if (conf == NULL) { 87 | return NULL; 88 | } 89 | 90 | return conf; 91 | } 92 | 93 | static char * 94 | ngx_http_inner_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child) 95 | { 96 | ngx_http_inner_proxy_conf_t *prev = parent; 97 | ngx_http_inner_proxy_conf_t *conf = child; 98 | 99 | ngx_conf_merge_str_value(conf->multiport, prev->multiport, ""); 100 | ngx_conf_merge_str_value(conf->uri, prev->uri, ""); 101 | 102 | return NGX_CONF_OK; 103 | } 104 | 105 | static char * 106 | ngx_http_inner_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 107 | { 108 | ngx_http_inner_proxy_conf_t *hipcf; 109 | ngx_str_t *value; 110 | 111 | hipcf = conf; 112 | 113 | if (hipcf->multiport.data != NULL) { 114 | return "is duplicate"; 115 | } 116 | 117 | value = cf->args->elts; 118 | 119 | hipcf->multiport = value[1]; 120 | hipcf->uri = value[2]; 121 | 122 | return NGX_CONF_OK; 123 | } 124 | 125 | 126 | static ngx_int_t 127 | ngx_http_inner_proxy_header_filter(ngx_http_request_t *r) 128 | { 129 | ngx_http_inner_proxy_ctx_t *ctx; 130 | 131 | ctx = ngx_http_get_module_ctx(r->main, ngx_http_inner_proxy_module); 132 | 133 | if (ctx == NULL) { /* not configured */ 134 | return ngx_http_next_header_filter(r); 135 | } 136 | 137 | if (r == r->main) { 138 | return NGX_OK; 139 | } 140 | 141 | r->main->headers_out = r->headers_out; 142 | 143 | return ngx_http_next_header_filter(r->main); 144 | } 145 | 146 | 147 | static ngx_int_t 148 | ngx_http_inner_proxy_body_filter(ngx_http_request_t *r, ngx_chain_t *in) 149 | { 150 | ngx_http_inner_proxy_ctx_t *ctx; 151 | ngx_chain_t *cl, l; 152 | ngx_buf_t *b; 153 | 154 | ctx = ngx_http_get_module_ctx(r->main, ngx_http_inner_proxy_module); 155 | 156 | if (ctx == NULL) { /* not configured */ 157 | return ngx_http_next_body_filter(r, in); 158 | } 159 | 160 | if (r == r->main) { 161 | if (ctx->last == 0) { 162 | return NGX_OK; 163 | } 164 | 165 | b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); 166 | 167 | if (b == NULL) { 168 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 169 | } 170 | 171 | b->last_buf = 1; 172 | 173 | l.buf = b; 174 | l.next = NULL; 175 | 176 | return ngx_http_next_body_filter(r, &l); 177 | } 178 | 179 | for (cl = in; cl; cl = cl->next) { 180 | if (cl->buf->last_in_chain) { 181 | ctx->last = 1; 182 | } 183 | } 184 | 185 | return ngx_http_next_body_filter(r, in); 186 | } 187 | 188 | 189 | static ngx_int_t 190 | ngx_http_inner_proxy_filter_init(ngx_conf_t *cf) 191 | { 192 | ngx_http_next_header_filter = ngx_http_top_header_filter; 193 | ngx_http_top_header_filter = ngx_http_inner_proxy_header_filter; 194 | 195 | ngx_http_next_body_filter = ngx_http_top_body_filter; 196 | ngx_http_top_body_filter = ngx_http_inner_proxy_body_filter; 197 | 198 | return NGX_OK; 199 | } 200 | 201 | 202 | ngx_int_t 203 | ngx_http_inner_proxy_request(ngx_http_request_t *r, ngx_int_t pslot) 204 | { 205 | ngx_http_inner_proxy_conf_t *hipcf; 206 | ngx_http_inner_proxy_ctx_t *ctx; 207 | ngx_http_request_t *sr; 208 | ngx_str_t uri; 209 | ngx_int_t rc; 210 | 211 | hipcf = ngx_http_get_module_loc_conf(r, ngx_http_inner_proxy_module); 212 | 213 | if (hipcf == NULL || hipcf->multiport.len == 0) { /* not configured */ 214 | return NGX_DECLINED; 215 | } 216 | 217 | if (pslot == ngx_process_slot) { 218 | ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, 219 | "inner proxy send request to self: %i", ngx_process_slot); 220 | return NGX_DECLINED; 221 | } 222 | 223 | ctx = ngx_http_get_module_ctx(r, ngx_http_inner_proxy_module); 224 | if (ctx) { 225 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 226 | "inner proxy has been called in this request"); 227 | return NGX_ERROR; 228 | } 229 | 230 | ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_inner_proxy_ctx_t)); 231 | if (ctx == NULL) { 232 | return NGX_ERROR; 233 | } 234 | ngx_http_set_ctx(r, ctx, ngx_http_inner_proxy_module); 235 | 236 | if (ngx_multiport_get_port(r->pool, &ctx->port, &hipcf->multiport, pslot) 237 | == NGX_ERROR) 238 | { 239 | return NGX_ERROR; 240 | } 241 | 242 | uri.len = hipcf->uri.len + 1 + ctx->port.len + 2 + r->uri.len; 243 | uri.data = ngx_pcalloc(r->pool, uri.len); 244 | ngx_snprintf(uri.data, uri.len, "%V/%V:/%V", 245 | &hipcf->uri, &ctx->port, &r->uri); 246 | 247 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 248 | "inner proxy send request to %V", &ctx->port); 249 | rc = ngx_http_subrequest(r, &uri, &r->args, &sr, NULL, 0); 250 | sr->method = r->method; 251 | sr->method_name = r->method_name; 252 | 253 | return rc; 254 | } 255 | -------------------------------------------------------------------------------- /ngx_multiport.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com 3 | */ 4 | 5 | 6 | #ifndef _NGX_MULTIPORT_H_INCLUDED_ 7 | #define _NGX_MULTIPORT_H_INCLUDED_ 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | /* 16 | * return value: 17 | * NGX_OK for success, NGX_ERROR for failed 18 | * paras: 19 | * pool: pool for port memory alloc 20 | * port: process real listen port while process_slot is pslot 21 | * multiport: port configure for processes, format as below: 22 | * port only: port 23 | * IPv4: host:port host must be ipaddr of IPv4 or * 24 | * IPv6: [host]:port host must be ipaddr of IPv6 25 | * Unix: unix:/path 26 | * pslot: process_slot 27 | */ 28 | ngx_int_t ngx_multiport_get_port(ngx_pool_t *pool, ngx_str_t *port, 29 | ngx_str_t *multiport, ngx_int_t pslot); 30 | 31 | 32 | /* 33 | * return value: 34 | * ngx_process_slot for successd, NGX_ERROR for failed 35 | * paras: 36 | * wpid: worker process id, 0 to ccf->worker_processes - 1 37 | */ 38 | ngx_int_t ngx_multiport_get_slot(ngx_uint_t wpid); 39 | 40 | 41 | /* 42 | * return value: 43 | * NGX_OK : for successd 44 | * NGX_ERROR : for failed 45 | * NGX_DECLINED: for not configured or send inner proxy to self 46 | * paras: 47 | * r : http request for send inner request to sibling worker 48 | * pslot: sibling worker ngx_process_slot 49 | */ 50 | ngx_int_t ngx_http_inner_proxy_request(ngx_http_request_t *r, ngx_int_t pslot); 51 | 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /ngx_multiport_misc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | static ngx_int_t 6 | ngx_multiport_get_port_unix(ngx_pool_t *pool, ngx_str_t *port, 7 | ngx_str_t *multiport, ngx_int_t pslot) 8 | { 9 | #if (NGX_HAVE_UNIX_DOMAIN) 10 | u_char *p; 11 | size_t len; 12 | 13 | len = multiport->len + 5; /* unix:/path -> unix:/path.127\0 */ 14 | port->data = ngx_pcalloc(pool, len); 15 | if (port->data == NULL) { 16 | return NGX_ERROR; 17 | } 18 | 19 | p = ngx_snprintf(port->data, len, "%V.%i", multiport, pslot); 20 | *p = 0; 21 | port->len = p - port->data; 22 | 23 | return NGX_OK; 24 | #else 25 | ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, 26 | "the unix domain sockets not support"); 27 | return NGX_ERROR; 28 | #endif 29 | } 30 | 31 | static ngx_int_t 32 | ngx_multiport_get_port_inet6(ngx_pool_t *pool, ngx_str_t *port, 33 | ngx_str_t *multiport, ngx_int_t pslot) 34 | { 35 | #if (NGX_HAVE_INET6) 36 | u_char *p, *last; 37 | ngx_str_t addr; 38 | size_t len; 39 | ngx_int_t n; 40 | 41 | last = multiport->data + multiport->len; 42 | p = ngx_strlchr(multiport->data, last, ']'); 43 | 44 | if (p == NULL) { 45 | ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "invalid INET6 host"); 46 | return NGX_ERROR; 47 | } 48 | 49 | ++p; 50 | if (p == last || *p != ':') { 51 | ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "no INET6 port"); 52 | return NGX_ERROR; 53 | } 54 | 55 | ++p; 56 | addr.data = multiport->data; 57 | addr.len = p - multiport->data; 58 | 59 | len = last - p; 60 | n = ngx_atoi(p, len); 61 | 62 | /* 65408 + 127 = 65535, pslot in [0, 127] */ 63 | if (n < 1 || n + pslot > 65408) { 64 | ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "invalid INET6 port"); 65 | return NGX_ERROR; 66 | } 67 | n += pslot; 68 | 69 | len = multiport->len + 3; /* [::]:1 -> [::]:128\0 */ 70 | port->data = ngx_pcalloc(pool, len); 71 | if (port->data == NULL) { 72 | return NGX_ERROR; 73 | } 74 | 75 | p = port->data; 76 | p = ngx_snprintf(p, len, "%V%i", &addr, n); 77 | port->len = p - port->data; 78 | 79 | return NGX_OK; 80 | #else 81 | ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, 82 | "the INET6 sockets not support"); 83 | return NGX_ERROR; 84 | #endif 85 | } 86 | 87 | static ngx_int_t 88 | ngx_multiport_get_port_inet(ngx_pool_t *pool, ngx_str_t *port, 89 | ngx_str_t *multiport, ngx_int_t pslot) 90 | { 91 | u_char *p, *last; 92 | ngx_str_t addr; 93 | size_t len; 94 | ngx_int_t n; 95 | 96 | last = multiport->data + multiport->len; 97 | p = ngx_strlchr(multiport->data, last, ':'); 98 | 99 | if (p == NULL) { /* port */ 100 | p = multiport->data; 101 | addr.len = 0; 102 | } else { /* host:port */ 103 | ++p; 104 | if (p == last) { 105 | ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "no port"); 106 | return NGX_ERROR; 107 | } 108 | 109 | addr.data = multiport->data; 110 | addr.len = p - multiport->data; 111 | } 112 | 113 | len = last - p; 114 | n = ngx_atoi(p, len); 115 | 116 | /* 65408 + 127 = 65535, pslot in [0, 127] */ 117 | if (n < 1 || n + pslot > 65408) { 118 | ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "invalid port"); 119 | return NGX_ERROR; 120 | } 121 | n += pslot; 122 | 123 | len = multiport->len + 3; /* 127.0.0.1:1 -> 127.0.0.1:128\0 */ 124 | port->data = ngx_pcalloc(pool, len); 125 | if (port->data == NULL) { 126 | return NGX_ERROR; 127 | } 128 | 129 | p = port->data; 130 | if (addr.len == 0) { 131 | p = ngx_snprintf(p, len, "%i", n); 132 | } else { 133 | p = ngx_snprintf(p, len, "%V%i", &addr, n); 134 | } 135 | port->len = p - port->data; 136 | 137 | return NGX_OK; 138 | } 139 | 140 | ngx_int_t 141 | ngx_multiport_get_port(ngx_pool_t *pool, ngx_str_t *port, 142 | ngx_str_t *multiport, ngx_int_t pslot) 143 | { 144 | u_char *p; 145 | size_t len; 146 | 147 | p = multiport->data; 148 | len = multiport->len; 149 | 150 | if (pslot < 0 || pslot > 127) { 151 | ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "invalid pslot: %i", 152 | pslot); 153 | return NGX_ERROR; 154 | } 155 | 156 | if (len >= 5 && ngx_strncasecmp(p, (u_char *) "unix:", 5) == 0) { 157 | return ngx_multiport_get_port_unix(pool, port, multiport, pslot); 158 | } 159 | 160 | if (len && p[0] == '[') { 161 | return ngx_multiport_get_port_inet6(pool, port, multiport, pslot); 162 | } 163 | 164 | return ngx_multiport_get_port_inet(pool, port, multiport, pslot); 165 | } 166 | -------------------------------------------------------------------------------- /ngx_process_slot_module.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com 3 | */ 4 | 5 | 6 | #include 7 | #include 8 | 9 | 10 | static ngx_int_t ngx_process_slot_module_init(ngx_cycle_t *cycle); 11 | static ngx_int_t ngx_process_slot_process_init(ngx_cycle_t *cycle); 12 | static void ngx_process_slot_process_exit(ngx_cycle_t *cycle); 13 | 14 | static void *ngx_process_slot_module_create_conf(ngx_cycle_t *cycle); 15 | static char *ngx_process_slot_module_init_conf(ngx_cycle_t *cycle, void *conf); 16 | 17 | 18 | #define MAX_PROCESSES 128 19 | 20 | typedef struct { 21 | ngx_atomic_int_t process_slot[MAX_PROCESSES]; 22 | } ngx_process_slot_ctx_t; 23 | 24 | typedef struct { 25 | ngx_process_slot_ctx_t *ctx; 26 | } ngx_process_slot_conf_t; 27 | 28 | 29 | static ngx_command_t ngx_process_slot_commands[] = { 30 | 31 | ngx_null_command 32 | }; 33 | 34 | 35 | static ngx_core_module_t ngx_process_slot_module_ctx = { 36 | ngx_string("process_slot"), 37 | ngx_process_slot_module_create_conf, 38 | ngx_process_slot_module_init_conf 39 | }; 40 | 41 | 42 | ngx_module_t ngx_process_slot_module = { 43 | NGX_MODULE_V1, 44 | &ngx_process_slot_module_ctx, /* module context */ 45 | ngx_process_slot_commands, /* module directives */ 46 | NGX_CORE_MODULE, /* module type */ 47 | NULL, /* init master */ 48 | ngx_process_slot_module_init, /* init module */ 49 | ngx_process_slot_process_init, /* init process */ 50 | NULL, /* init thread */ 51 | NULL, /* exit thread */ 52 | ngx_process_slot_process_exit, /* exit process */ 53 | NULL, /* exit master */ 54 | NGX_MODULE_V1_PADDING 55 | }; 56 | 57 | 58 | static void * 59 | ngx_process_slot_module_create_conf(ngx_cycle_t *cycle) 60 | { 61 | ngx_process_slot_conf_t *pscf; 62 | 63 | pscf = ngx_palloc(cycle->pool, sizeof(ngx_process_slot_conf_t)); 64 | if (pscf == NULL) { 65 | return NULL; 66 | } 67 | 68 | return pscf; 69 | } 70 | 71 | static char * 72 | ngx_process_slot_module_init_conf(ngx_cycle_t *cycle, void *conf) 73 | { 74 | return NGX_CONF_OK; 75 | } 76 | 77 | static ngx_int_t 78 | ngx_process_slot_module_init(ngx_cycle_t *cycle) 79 | { 80 | ngx_process_slot_conf_t *pscf; 81 | ngx_shm_t shm; 82 | ngx_uint_t i; 83 | 84 | pscf = (ngx_process_slot_conf_t *) ngx_get_conf(cycle->conf_ctx, 85 | ngx_process_slot_module); 86 | 87 | shm.size = sizeof(ngx_process_slot_ctx_t); 88 | shm.name.len = sizeof("process_slot_zone") - 1; 89 | shm.name.data = (u_char *) "process_slot_zone"; 90 | shm.log = cycle->log; 91 | 92 | if (ngx_shm_alloc(&shm) != NGX_OK) { 93 | return NGX_ERROR; 94 | } 95 | 96 | pscf->ctx = (ngx_process_slot_ctx_t *) shm.addr; 97 | 98 | for (i = 0; i < MAX_PROCESSES; ++i) { 99 | pscf->ctx->process_slot[i] = -1; 100 | } 101 | 102 | return NGX_OK; 103 | } 104 | 105 | static ngx_int_t 106 | ngx_process_slot_process_init(ngx_cycle_t *cycle) 107 | { 108 | ngx_process_slot_conf_t *pscf; 109 | ngx_process_slot_ctx_t *ctx; 110 | 111 | pscf = (ngx_process_slot_conf_t *) ngx_get_conf(cycle->conf_ctx, 112 | ngx_process_slot_module); 113 | ctx = pscf->ctx; 114 | 115 | for (;;) { 116 | if (ngx_atomic_cmp_set((ngx_atomic_t *) &ctx->process_slot[ngx_worker], 117 | (ngx_atomic_uint_t)ctx->process_slot[ngx_worker], ngx_process_slot)) 118 | { 119 | break; 120 | } 121 | } 122 | 123 | return NGX_OK; 124 | } 125 | 126 | static void 127 | ngx_process_slot_process_exit(ngx_cycle_t *cycle) 128 | { 129 | ngx_process_slot_conf_t *pscf; 130 | ngx_process_slot_ctx_t *ctx; 131 | 132 | pscf = (ngx_process_slot_conf_t *) ngx_get_conf(cycle->conf_ctx, 133 | ngx_process_slot_module); 134 | ctx = pscf->ctx; 135 | 136 | ngx_atomic_cmp_set((ngx_atomic_t *) &ctx->process_slot[ngx_worker], 137 | (ngx_atomic_uint_t)ngx_process_slot, -1); 138 | } 139 | 140 | ngx_int_t 141 | ngx_multiport_get_slot(ngx_uint_t wpid) 142 | { 143 | ngx_process_slot_conf_t *pscf; 144 | ngx_process_slot_ctx_t *ctx; 145 | ngx_core_conf_t *ccf; 146 | 147 | ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, 148 | ngx_core_module); 149 | 150 | if (wpid >= (ngx_uint_t)ccf->worker_processes) { 151 | return -1; 152 | } 153 | 154 | pscf = (ngx_process_slot_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, 155 | ngx_process_slot_module); 156 | ctx = pscf->ctx; 157 | 158 | return ctx->process_slot[wpid]; 159 | } 160 | -------------------------------------------------------------------------------- /ngx_stream_zone_module.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com 3 | */ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | static ngx_int_t ngx_stream_zone_init_process(ngx_cycle_t *cycle); 12 | static void ngx_stream_zone_exit_process(ngx_cycle_t *cycle); 13 | static void *ngx_stream_zone_create_conf(ngx_cycle_t *cf); 14 | static char *ngx_stream_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 15 | 16 | 17 | #define NAME_LEN 1024 18 | 19 | static ngx_str_t stream_zone_key = ngx_string("stream_zone"); 20 | 21 | typedef struct { 22 | u_char name[NAME_LEN]; 23 | ngx_int_t slot; /* process slot */ 24 | ngx_int_t idx; 25 | ngx_int_t next; /* idx of stream node */ 26 | } ngx_stream_zone_node_t; 27 | 28 | typedef struct { 29 | ngx_shmtx_t mutex; 30 | ngx_shmtx_sh_t lock; 31 | ngx_int_t node; /* idx of stream node */ 32 | } ngx_stream_zone_hash_t; 33 | 34 | typedef struct { 35 | ngx_int_t nbuckets; 36 | ngx_int_t nstreams; 37 | 38 | ngx_shmtx_t *mutex; 39 | ngx_shmtx_sh_t *lock; 40 | ngx_stream_zone_hash_t *hash; /* hash in shm */ 41 | ngx_stream_zone_node_t *stream_node;/* node in shm */ 42 | ngx_int_t *free_node; /* free node chain */ 43 | ngx_int_t *alloc; /* node number in use*/ 44 | } ngx_stream_zone_conf_t; 45 | 46 | 47 | static ngx_command_t ngx_stream_zone_commands[] = { 48 | 49 | { ngx_string("stream_zone"), 50 | NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE2, 51 | ngx_stream_zone, 52 | 0, 53 | 0, 54 | NULL }, 55 | 56 | ngx_null_command 57 | }; 58 | 59 | 60 | static ngx_core_module_t ngx_stream_zone_module_ctx = { 61 | ngx_string("rtmp_stream_zone"), 62 | ngx_stream_zone_create_conf, /* create conf */ 63 | NULL /* init conf */ 64 | }; 65 | 66 | 67 | ngx_module_t ngx_stream_zone_module = { 68 | NGX_MODULE_V1, 69 | &ngx_stream_zone_module_ctx, /* module context */ 70 | ngx_stream_zone_commands, /* module directives */ 71 | NGX_CORE_MODULE, /* module type */ 72 | NULL, /* init master */ 73 | NULL, /* init module */ 74 | ngx_stream_zone_init_process, /* init process */ 75 | NULL, /* init thread */ 76 | NULL, /* exit thread */ 77 | ngx_stream_zone_exit_process, /* exit process */ 78 | NULL, /* exit master */ 79 | NGX_MODULE_V1_PADDING 80 | }; 81 | 82 | 83 | static ngx_stream_zone_node_t * 84 | ngx_stream_zone_get_node(ngx_str_t *name, ngx_int_t pslot) 85 | { 86 | ngx_stream_zone_conf_t *szcf; 87 | ngx_stream_zone_node_t *node; 88 | 89 | szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, 90 | ngx_stream_zone_module); 91 | 92 | ngx_shmtx_lock(szcf->mutex); 93 | 94 | if (*szcf->free_node == -1) { 95 | ngx_shmtx_unlock(szcf->mutex); 96 | return NULL; 97 | } 98 | 99 | node = &szcf->stream_node[*szcf->free_node]; 100 | *szcf->free_node = node->next; 101 | 102 | *ngx_copy(node->name, name->data, ngx_min(NAME_LEN - 1, name->len)) = '\0'; 103 | node->slot = pslot; 104 | node->next = -1; 105 | 106 | ++*szcf->alloc; 107 | 108 | ngx_shmtx_unlock(szcf->mutex); 109 | 110 | return node; 111 | } 112 | 113 | static void 114 | ngx_stream_zone_put_node(ngx_int_t idx) 115 | { 116 | ngx_stream_zone_conf_t *szcf; 117 | ngx_stream_zone_node_t *node; 118 | 119 | szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, 120 | ngx_stream_zone_module); 121 | 122 | ngx_shmtx_lock(szcf->mutex); 123 | 124 | node = &szcf->stream_node[idx]; 125 | 126 | node->next = *szcf->free_node; 127 | *szcf->free_node = idx; 128 | 129 | --*szcf->alloc; 130 | 131 | ngx_shmtx_unlock(szcf->mutex); 132 | } 133 | 134 | static void * 135 | ngx_stream_zone_create_conf(ngx_cycle_t *cf) 136 | { 137 | ngx_stream_zone_conf_t *conf; 138 | 139 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_zone_conf_t)); 140 | if (conf == NULL) { 141 | return NULL; 142 | } 143 | 144 | conf->nbuckets = NGX_CONF_UNSET; 145 | conf->nstreams = NGX_CONF_UNSET; 146 | 147 | return conf; 148 | } 149 | 150 | static void 151 | ngx_stream_zone_clear(ngx_cycle_t *cycle) 152 | { 153 | ngx_stream_zone_conf_t *szcf; 154 | volatile ngx_int_t idx, cur, next; 155 | 156 | szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(cycle->conf_ctx, 157 | ngx_stream_zone_module); 158 | 159 | if (szcf->nbuckets <= 0 || szcf->nstreams <= 0) { 160 | return; 161 | } 162 | 163 | for (idx = 0; idx < szcf->nbuckets; ++idx) { 164 | 165 | ngx_shmtx_lock(&szcf->hash[idx].mutex); 166 | cur = -1; 167 | 168 | while (1) { 169 | if (cur == -1) { 170 | next = szcf->hash[idx].node; 171 | } else { 172 | next = szcf->stream_node[cur].next; 173 | } 174 | 175 | if (next == -1) { 176 | break; 177 | } 178 | 179 | if (szcf->stream_node[next].slot == ngx_process_slot) { 180 | if (cur == -1) { 181 | szcf->hash[idx].node = szcf->stream_node[next].next; 182 | } else { 183 | szcf->stream_node[cur].next = szcf->stream_node[next].next; 184 | } 185 | 186 | ngx_stream_zone_put_node(next); 187 | continue; 188 | } 189 | 190 | cur = next; 191 | } 192 | ngx_shmtx_unlock(&szcf->hash[idx].mutex); 193 | } 194 | } 195 | 196 | static ngx_int_t 197 | ngx_stream_zone_init_process(ngx_cycle_t *cycle) 198 | { 199 | ngx_stream_zone_clear(cycle); 200 | 201 | return NGX_OK; 202 | } 203 | 204 | static void 205 | ngx_stream_zone_exit_process(ngx_cycle_t *cycle) 206 | { 207 | ngx_stream_zone_clear(cycle); 208 | } 209 | 210 | static char * 211 | ngx_stream_zone_shm_init(ngx_conf_t *cf, ngx_shm_t *shm, 212 | ngx_stream_zone_conf_t *szcf) 213 | { 214 | u_char *p; 215 | ngx_int_t i, next; 216 | 217 | p = shm->addr; 218 | 219 | szcf->mutex = (ngx_shmtx_t *) p; 220 | p += sizeof(ngx_shmtx_t); 221 | 222 | szcf->lock = (ngx_shmtx_sh_t *) p; 223 | p += sizeof(ngx_shmtx_sh_t); 224 | 225 | szcf->hash = (ngx_stream_zone_hash_t *) p; 226 | p += sizeof(ngx_stream_zone_hash_t) * szcf->nbuckets; 227 | 228 | szcf->stream_node = (ngx_stream_zone_node_t *) p; 229 | p += sizeof(ngx_stream_zone_node_t) * szcf->nstreams; 230 | 231 | szcf->free_node = (ngx_int_t *) p; 232 | p += sizeof(ngx_int_t); 233 | 234 | szcf->alloc = (ngx_int_t *) p; 235 | 236 | /* init shm zone */ 237 | #if (NGX_HAVE_ATOMIC_OPS) 238 | 239 | p = NULL; 240 | 241 | #else 242 | p = ngx_pnalloc(cf->pool, cf->cycle->lock_file.len 243 | + stream_zone_key.len); 244 | if (p == NULL) { 245 | return NGX_CONF_ERROR; 246 | } 247 | ngx_sprintf(p, "%V%V", &cf->cycle->lock_file, &stream_zone_key) = 0; 248 | 249 | #endif 250 | 251 | if (ngx_shmtx_create(szcf->mutex, szcf->lock, p) != NGX_OK) { 252 | return NGX_CONF_ERROR; 253 | } 254 | 255 | for (i = 0; i < szcf->nbuckets; ++i) { 256 | #if (NGX_HAVE_ATOMIC_OPS) 257 | 258 | p = NULL; 259 | 260 | #else 261 | p = ngx_pnalloc(cf->pool, cf->cycle->lock_file.len + stream_zone_key.len 262 | + NGX_INT32_LEN); 263 | if (p == NULL) { 264 | return NGX_CONF_ERROR; 265 | } 266 | ngx_sprintf(p, "%V%V%d", &cf->cycle->lock_file, 267 | &stream_zone_key, i) = 0; 268 | 269 | #endif 270 | 271 | if (ngx_shmtx_create(&szcf->hash[i].mutex, &szcf->hash[i].lock, p) 272 | != NGX_OK) 273 | { 274 | return NGX_CONF_ERROR; 275 | } 276 | 277 | szcf->hash[i].node = -1; 278 | } 279 | 280 | next = -1; 281 | i = szcf->nstreams; 282 | 283 | do { 284 | --i; 285 | 286 | szcf->stream_node[i].slot = -1; 287 | szcf->stream_node[i].idx = i; 288 | szcf->stream_node[i].next = next; 289 | next = i; 290 | } while (i); 291 | 292 | *szcf->free_node = i; 293 | *szcf->alloc = 0; 294 | 295 | return NGX_CONF_OK; 296 | } 297 | 298 | static char * 299 | ngx_stream_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 300 | { 301 | ngx_uint_t i; 302 | ngx_str_t *value; 303 | size_t len; 304 | ngx_shm_t shm; 305 | ngx_stream_zone_conf_t *szcf = conf; 306 | 307 | value = cf->args->elts; 308 | 309 | for (i = 1; i < cf->args->nelts; ++i) { 310 | 311 | if (ngx_strncmp(value[i].data, "buckets=", 8) == 0) { 312 | szcf->nbuckets = ngx_atoi(value[i].data + 8, value[i].len - 8); 313 | 314 | if (szcf->nbuckets <= 0) { 315 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 316 | "invalid buckets \"%V\"", &value[i]); 317 | return NGX_CONF_ERROR; 318 | } 319 | 320 | continue; 321 | } 322 | 323 | if (ngx_strncmp(value[i].data, "streams=", 8) == 0) { 324 | szcf->nstreams = ngx_atoi(value[i].data + 8, value[i].len - 8); 325 | 326 | if (szcf->nstreams <= 0) { 327 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 328 | "invalid streams \"%V\"", &value[i]); 329 | return NGX_CONF_ERROR; 330 | } 331 | 332 | continue; 333 | } 334 | 335 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 336 | "invalid parameter \"%V\"", &value[i]); 337 | return NGX_CONF_ERROR; 338 | } 339 | 340 | /* create shm zone */ 341 | len = sizeof(ngx_shmtx_t) + sizeof(ngx_shmtx_sh_t) 342 | + sizeof(ngx_stream_zone_hash_t) * szcf->nbuckets 343 | + sizeof(ngx_stream_zone_node_t) * szcf->nstreams 344 | + sizeof(ngx_int_t) + sizeof(ngx_int_t); 345 | 346 | shm.size = len; 347 | shm.name = stream_zone_key; 348 | shm.log = cf->cycle->log; 349 | 350 | if (ngx_shm_alloc(&shm) != NGX_OK) { 351 | return NGX_CONF_ERROR; 352 | } 353 | 354 | return ngx_stream_zone_shm_init(cf, &shm, szcf); 355 | } 356 | 357 | 358 | ngx_int_t 359 | ngx_stream_zone_insert_stream(ngx_str_t *name) 360 | { 361 | ngx_stream_zone_conf_t *szcf; 362 | volatile ngx_uint_t idx; 363 | volatile ngx_int_t i, pslot; 364 | ngx_stream_zone_node_t *node; 365 | 366 | szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, 367 | ngx_stream_zone_module); 368 | 369 | if (szcf->nbuckets <= 0 || szcf->nstreams <= 0) { 370 | return NGX_ERROR; 371 | } 372 | 373 | if (name->len >= NAME_LEN) { 374 | ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, 375 | "stream name(%V) too long", name); 376 | return NGX_ERROR; 377 | } 378 | 379 | idx = ngx_hash_key(name->data, name->len) % szcf->nbuckets; 380 | 381 | ngx_shmtx_lock(&szcf->hash[idx].mutex); 382 | i = szcf->hash[idx].node; 383 | pslot = -1; 384 | while (i != -1) { 385 | if (ngx_strlen(szcf->stream_node[i].name) == name->len 386 | && ngx_memcmp(szcf->stream_node[i].name, name->data, name->len) 387 | == 0) 388 | { 389 | pslot = szcf->stream_node[i].slot; 390 | break; 391 | } 392 | 393 | i = szcf->stream_node[i].next; 394 | } 395 | 396 | if (i == -1) { /* stream not in hash */ 397 | node = ngx_stream_zone_get_node(name, ngx_process_slot); 398 | if (node == NULL) { 399 | ngx_shmtx_unlock(&szcf->hash[idx].mutex); 400 | ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, 401 | "stream zone get node failed"); 402 | return NGX_ERROR; 403 | } 404 | node->slot = ngx_process_slot; 405 | 406 | node->next = szcf->hash[idx].node; 407 | szcf->hash[idx].node = node->idx; 408 | 409 | pslot = ngx_process_slot; 410 | } 411 | ngx_shmtx_unlock(&szcf->hash[idx].mutex); 412 | 413 | return pslot; 414 | } 415 | 416 | void 417 | ngx_stream_zone_delete_stream(ngx_str_t *name) 418 | { 419 | ngx_stream_zone_conf_t *szcf; 420 | volatile ngx_uint_t idx; 421 | volatile ngx_int_t cur, next; 422 | 423 | szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, 424 | ngx_stream_zone_module); 425 | 426 | if (szcf->nbuckets <= 0 || szcf->nstreams <= 0) { 427 | return; 428 | } 429 | 430 | idx = ngx_hash_key(name->data, name->len) % szcf->nbuckets; 431 | 432 | ngx_shmtx_lock(&szcf->hash[idx].mutex); 433 | cur = -1; 434 | next = szcf->hash[idx].node; 435 | while (next != -1) { 436 | if (ngx_strlen(szcf->stream_node[next].name) == name->len 437 | && ngx_memcmp(szcf->stream_node[next].name, name->data, name->len) 438 | == 0) 439 | { 440 | if (szcf->stream_node[next].slot != ngx_process_slot) { 441 | break; 442 | } 443 | 444 | if (cur == -1) { /* link header */ 445 | szcf->hash[idx].node = szcf->stream_node[next].next; 446 | } else { 447 | szcf->stream_node[cur].next = szcf->stream_node[next].next; 448 | } 449 | ngx_stream_zone_put_node(next); 450 | break; 451 | } 452 | 453 | cur = next; 454 | next = szcf->stream_node[next].next; 455 | } 456 | ngx_shmtx_unlock(&szcf->hash[idx].mutex); 457 | } 458 | 459 | ngx_chain_t * 460 | ngx_stream_zone_state(ngx_http_request_t *r, ngx_flag_t detail) 461 | { 462 | ngx_stream_zone_conf_t *szcf; 463 | ngx_chain_t *cl; 464 | ngx_buf_t *b; 465 | size_t len; 466 | volatile ngx_int_t idx, next; 467 | 468 | szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, 469 | ngx_stream_zone_module); 470 | 471 | if (szcf->nbuckets <= 0 || szcf->nstreams <= 0) { 472 | return NULL; 473 | } 474 | 475 | len = sizeof("##########stream zone state##########\n") - 1 476 | + sizeof("ngx_stream_zone buckets: \n") - 1 + NGX_OFF_T_LEN 477 | + sizeof("ngx_stream_zone streams: \n") - 1 + NGX_OFF_T_LEN 478 | + sizeof("ngx_stream_zone alloc: \n") - 1 + NGX_OFF_T_LEN; 479 | 480 | cl = ngx_alloc_chain_link(r->pool); 481 | if (cl == NULL) { 482 | return NULL; 483 | } 484 | cl->next = NULL; 485 | 486 | b = ngx_create_temp_buf(r->pool, len); 487 | if (b == NULL) { 488 | return NULL; 489 | } 490 | cl->buf = b; 491 | 492 | b->last = ngx_snprintf(b->last, len, 493 | "##########stream zone state##########\n" 494 | "ngx_stream_zone buckets: %i\nngx_stream_zone streams: %i\n" 495 | "ngx_stream_zone alloc: %i\n", 496 | szcf->nbuckets, szcf->nstreams, *szcf->alloc); 497 | 498 | if (detail) { 499 | for (idx = 0; idx < szcf->nbuckets; ++idx) { 500 | ngx_shmtx_lock(&szcf->hash[idx].mutex); 501 | 502 | next = szcf->hash[idx].node; 503 | if (next == -1) { 504 | ngx_shmtx_unlock(&szcf->hash[idx].mutex); 505 | continue; 506 | } 507 | ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "slot: %i", idx); 508 | 509 | while (next != -1) { 510 | ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, 511 | "\t\tname:%s, slot:%i, idx:%i, next:%i", 512 | szcf->stream_node[next].name, 513 | szcf->stream_node[next].slot, 514 | szcf->stream_node[next].idx, 515 | szcf->stream_node[next].next); 516 | 517 | next = szcf->stream_node[next].next; 518 | } 519 | 520 | ngx_shmtx_unlock(&szcf->hash[idx].mutex); 521 | } 522 | } 523 | 524 | return cl; 525 | } 526 | -------------------------------------------------------------------------------- /ngx_stream_zone_module.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com 3 | */ 4 | 5 | 6 | #ifndef _NGX_STREAM_ZONE_MODULE_H_INCLUDED_ 7 | #define _NGX_STREAM_ZONE_MODULE_H_INCLUDED_ 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | /* 16 | * return value: 17 | * process_slot for owner of stream, NGX_ERROR for error 18 | * name: 19 | * stream name 20 | */ 21 | ngx_int_t ngx_stream_zone_insert_stream(ngx_str_t *name); 22 | 23 | /* 24 | * name: 25 | * stream name 26 | */ 27 | void ngx_stream_zone_delete_stream(ngx_str_t *name); 28 | 29 | /* 30 | * return value: 31 | * chain of stream zone state for returning to http client 32 | * paras: 33 | * r: http request to query status of rbuf 34 | * detail: print stream detail in log 35 | */ 36 | ngx_chain_t *ngx_stream_zone_state(ngx_http_request_t *r, ngx_flag_t detail); 37 | 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /t/config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_multiport_test_module 2 | 3 | HTTP_MODULES="$HTTP_MODULES \ 4 | ngx_stream_zone_test_module \ 5 | ngx_multiport_test_module \ 6 | " 7 | 8 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS 9 | $ngx_addon_dir/ngx_stream_zone_test_module.c \ 10 | $ngx_addon_dir/ngx_multiport_test_module.c \ 11 | " 12 | 13 | -------------------------------------------------------------------------------- /t/nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | user root; 3 | worker_processes 4; 4 | 5 | #error_log logs/error.log; 6 | #error_log logs/error.log notice; 7 | error_log logs/error.log info; 8 | 9 | #pid logs/nginx.pid; 10 | 11 | stream_zone buckets=10007 streams=10000; 12 | 13 | events { 14 | worker_connections 1024; 15 | multi_listen 9000 80; 16 | multi_listen unix:/tmp/http.sock.80 80; 17 | } 18 | 19 | 20 | http { 21 | include mime.types; 22 | default_type application/octet-stream; 23 | 24 | #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 25 | # '$status $body_bytes_sent "$http_referer" ' 26 | # '"$http_user_agent" "$http_x_forwarded_for"'; 27 | 28 | #access_log logs/access.log main; 29 | 30 | sendfile on; 31 | #tcp_nopush on; 32 | 33 | #keepalive_timeout 0; 34 | keepalive_timeout 65; 35 | 36 | #gzip on; 37 | 38 | server { 39 | listen 80; 40 | server_name localhost; 41 | 42 | location /stream_zone_test/ { 43 | stream_zone_test; 44 | } 45 | 46 | location /multiport_test/ { 47 | inner_proxy unix:/tmp/http.sock.80 /inner_proxy; 48 | multiport_test; 49 | } 50 | 51 | location /inner_proxy/ { 52 | rewrite ^/inner_proxy/(.*):/(.*) /$2 break; 53 | proxy_pass http://$1:; 54 | } 55 | 56 | location / { 57 | broadcast unix:/tmp/http.sock.80 /auth_proxy; 58 | } 59 | 60 | location /auth_proxy/ { 61 | rewrite ^/auth_proxy/(.*) /auth break; 62 | proxy_pass http://$1:; 63 | } 64 | 65 | location /auth { 66 | # return 403; 67 | echo "auth"; 68 | echo $scheme://$host$uri?$args; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /t/ngx_multiport_test_module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "ngx_multiport.h" 5 | #include "ngx_test_macro.h" 6 | 7 | 8 | static char *ngx_multiport_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 9 | 10 | 11 | static ngx_command_t ngx_multiport_test_commands[] = { 12 | 13 | { ngx_string("multiport_test"), 14 | NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS, 15 | ngx_multiport_test, 16 | 0, 17 | 0, 18 | NULL }, 19 | 20 | ngx_null_command 21 | }; 22 | 23 | 24 | static ngx_http_module_t ngx_multiport_test_module_ctx = { 25 | NULL, /* preconfiguration */ 26 | NULL, /* postconfiguration */ 27 | 28 | NULL, /* create main configuration */ 29 | NULL, /* init main configuration */ 30 | 31 | NULL, /* create server configuration */ 32 | NULL, /* merge server configuration */ 33 | 34 | NULL, /* create location configuration */ 35 | NULL /* merge location configuration */ 36 | }; 37 | 38 | 39 | ngx_module_t ngx_multiport_test_module = { 40 | NGX_MODULE_V1, 41 | &ngx_multiport_test_module_ctx, /* module context */ 42 | ngx_multiport_test_commands, /* module directives */ 43 | NGX_HTTP_MODULE, /* module type */ 44 | NULL, /* init master */ 45 | NULL, /* init module */ 46 | NULL, /* init process */ 47 | NULL, /* init thread */ 48 | NULL, /* exit thread */ 49 | NULL, /* exit process */ 50 | NULL, /* exit master */ 51 | NGX_MODULE_V1_PADDING 52 | }; 53 | 54 | 55 | ngx_int_t 56 | ngx_multiport_test_get_port(ngx_http_request_t *r, char *multiport, 57 | ngx_int_t pslot, char *expect) 58 | { 59 | ngx_str_t port; 60 | ngx_int_t rc; 61 | ngx_str_t mp; 62 | 63 | mp.data = (u_char *) multiport; 64 | mp.len = ngx_strlen(multiport); 65 | 66 | ngx_memzero(&port, sizeof(ngx_str_t)); 67 | rc = ngx_multiport_get_port(r->pool, &port, &mp, pslot); 68 | if (port.len) { 69 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "port: %V, %s, %d", 70 | &port, port.data, port.len); 71 | } 72 | if (expect == NULL && rc == NGX_ERROR) { 73 | return 1; 74 | } else { 75 | return ngx_test_str(&port, expect); 76 | } 77 | 78 | return 0; 79 | } 80 | 81 | static ngx_int_t 82 | ngx_multiport_test_handler(ngx_http_request_t *r) 83 | { 84 | ngx_buf_t *b; 85 | ngx_chain_t cl; 86 | size_t len; 87 | ngx_int_t rc; 88 | 89 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 90 | "multiport test handler"); 91 | 92 | rc = ngx_http_inner_proxy_request(r, ngx_multiport_get_slot(0)); 93 | if (rc != NGX_DECLINED) { 94 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 95 | "inner proxy return %i", rc); 96 | return rc; 97 | } 98 | 99 | NGX_TEST_INIT 100 | 101 | /* test ngx_multiport_get_port */ 102 | /* normal format */ 103 | NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "10", 127, "137")); 104 | NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "127.0.0.1:55635", 4, 105 | "127.0.0.1:55639")); 106 | NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1]:1024", 0, 107 | "[::127.0.0.1]:1024")); 108 | NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "unix:/tmp.test", 7, 109 | "unix:/tmp.test.7")); 110 | 111 | /* inet6 format error */ 112 | NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1:1024", 0, NULL)); 113 | NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1]:abcd", 114 | 0, NULL)); 115 | NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1]:65409", 116 | 0, NULL)); 117 | NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1]:", 0, NULL)); 118 | NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1]", 0, NULL)); 119 | 120 | /* inet format error */ 121 | NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "127.0.0.1:", 4, NULL)); 122 | NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "abcd", 4, NULL)); 123 | NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "65410", 4, NULL)); 124 | 125 | /* pslot error */ 126 | NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "65410", -1, NULL)); 127 | NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "65410", 128, NULL)); 128 | 129 | /* test ngx_multiport_get_slot */ 130 | NGX_TEST_ISOK(ngx_multiport_get_slot(4) == -1); 131 | NGX_TEST_ISOK(ngx_multiport_get_slot(0) == 0); 132 | NGX_TEST_ISOK(ngx_multiport_get_slot(1) == 1); 133 | NGX_TEST_ISOK(ngx_multiport_get_slot(2) == 2); 134 | NGX_TEST_ISOK(ngx_multiport_get_slot(3) == 3); 135 | 136 | r->headers_out.status = NGX_HTTP_OK; 137 | 138 | ngx_http_send_header(r); 139 | 140 | len = sizeof("pslot: %i TEST cases 4294967296, 4294967296 pass\n") - 1; 141 | b = ngx_create_temp_buf(r->pool, len); 142 | 143 | if (b == NULL) { 144 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 145 | } 146 | 147 | b->last = ngx_snprintf(b->last, len, "pslot: %i TEST cases %d, %d pass\n", 148 | ngx_process_slot, count, pass); 149 | b->last_buf = 1; 150 | b->last_in_chain = 1; 151 | 152 | cl.buf = b; 153 | cl.next = NULL; 154 | 155 | return ngx_http_output_filter(r, &cl); 156 | } 157 | 158 | 159 | static char * 160 | ngx_multiport_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 161 | { 162 | ngx_http_core_loc_conf_t *clcf; 163 | 164 | clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); 165 | clcf->handler = ngx_multiport_test_handler; 166 | 167 | return NGX_CONF_OK; 168 | } 169 | -------------------------------------------------------------------------------- /t/ngx_stream_zone_test_module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "ngx_stream_zone_module.h" 5 | 6 | 7 | static char *ngx_stream_zone_test(ngx_conf_t *cf, ngx_command_t *cmd, 8 | void *conf); 9 | 10 | static ngx_command_t ngx_stream_zone_test_commands[] = { 11 | 12 | { ngx_string("stream_zone_test"), 13 | NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS, 14 | ngx_stream_zone_test, 15 | 0, 16 | 0, 17 | NULL }, 18 | 19 | ngx_null_command 20 | }; 21 | 22 | 23 | static ngx_http_module_t ngx_stream_zone_test_module_ctx = { 24 | NULL, /* preconfiguration */ 25 | NULL, /* postconfiguration */ 26 | 27 | NULL, /* create main configuration */ 28 | NULL, /* init main configuration */ 29 | 30 | NULL, /* create server configuration */ 31 | NULL, /* merge server configuration */ 32 | 33 | NULL, /* create location configuration */ 34 | NULL /* merge location configuration */ 35 | }; 36 | 37 | 38 | ngx_module_t ngx_stream_zone_test_module = { 39 | NGX_MODULE_V1, 40 | &ngx_stream_zone_test_module_ctx, /* module context */ 41 | ngx_stream_zone_test_commands, /* module directives */ 42 | NGX_HTTP_MODULE, /* module type */ 43 | NULL, /* init master */ 44 | NULL, /* init module */ 45 | NULL, /* init process */ 46 | NULL, /* init thread */ 47 | NULL, /* exit thread */ 48 | NULL, /* exit process */ 49 | NULL, /* exit master */ 50 | NGX_MODULE_V1_PADDING 51 | }; 52 | 53 | 54 | static ngx_int_t 55 | ngx_stream_zone_test_handler(ngx_http_request_t *r) 56 | { 57 | ngx_chain_t cl, *out; 58 | ngx_buf_t *b; 59 | size_t len; 60 | ngx_str_t stream; 61 | ngx_int_t rc; 62 | 63 | ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "stream zone test handler"); 64 | 65 | rc = -1; 66 | if (r->method == NGX_HTTP_GET) { 67 | out = ngx_stream_zone_state(r, 1); 68 | out->buf->last_buf = 1; 69 | out->buf->last_in_chain = 1; 70 | 71 | r->headers_out.status = NGX_HTTP_OK; 72 | ngx_http_send_header(r); 73 | 74 | return ngx_http_output_filter(r, out); 75 | } else if (r->method == NGX_HTTP_DELETE) { 76 | 77 | if (ngx_http_arg(r, (u_char *) "stream", 6, &stream) != NGX_OK) { 78 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 79 | "stream zone test, no stream in http args"); 80 | return NGX_HTTP_BAD_REQUEST; 81 | } 82 | 83 | len = sizeof("delete stream=\n") - 1 + stream.len; 84 | 85 | ngx_stream_zone_delete_stream(&stream); 86 | } else if (r->method == NGX_HTTP_POST) { 87 | 88 | if (ngx_http_arg(r, (u_char *) "stream", 6, &stream) != NGX_OK) { 89 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 90 | "stream zone test, no stream in http args"); 91 | return NGX_HTTP_BAD_REQUEST; 92 | } 93 | 94 | len = sizeof("stream= in process:4294967296\n") - 1 + stream.len; 95 | 96 | rc = ngx_stream_zone_insert_stream(&stream); 97 | } else { 98 | return NGX_HTTP_BAD_REQUEST; 99 | } 100 | 101 | r->headers_out.status = NGX_HTTP_OK; 102 | ngx_http_send_header(r); 103 | 104 | b = ngx_create_temp_buf(r->pool, len); 105 | 106 | if (b == NULL) { 107 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 108 | } 109 | 110 | if (r->method == NGX_HTTP_DELETE) { 111 | b->last = ngx_snprintf(b->last, len, "delete stream=%V\n", &stream); 112 | } else { 113 | b->last = ngx_snprintf(b->last, len, 114 | "stream=%V in process:%i\n", &stream, rc); 115 | } 116 | b->last_buf = 1; 117 | b->last_in_chain = 1; 118 | 119 | cl.buf = b; 120 | cl.next = NULL; 121 | 122 | return ngx_http_output_filter(r, &cl); 123 | } 124 | 125 | static char * 126 | ngx_stream_zone_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 127 | { 128 | ngx_http_core_loc_conf_t *clcf; 129 | 130 | clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); 131 | clcf->handler = ngx_stream_zone_test_handler; 132 | 133 | return NGX_CONF_OK; 134 | } 135 | -------------------------------------------------------------------------------- /t/ngx_test_macro.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com 3 | */ 4 | 5 | 6 | #ifndef _NGX_TEST_MACRO_H_INCLUDE_ 7 | #define _NGX_TEST_MACRO_H_INCLUDE_ 8 | 9 | 10 | #include 11 | #include 12 | 13 | 14 | static ngx_int_t count = 0; 15 | static ngx_int_t pass = 0; 16 | 17 | #define NGX_TEST_INIT count = 0, pass = 0; 18 | 19 | #define NGX_TEST_ISOK(testcase) \ 20 | { \ 21 | ngx_int_t __ret = testcase; \ 22 | ++count; \ 23 | if (__ret) ++pass; \ 24 | ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, " TEST "#testcase"%s",\ 25 | (__ret ? " ...OK" : " ...ERROR")); \ 26 | } 27 | 28 | #define NGX_TEST_INT(di, si) \ 29 | (di == si) 30 | 31 | static ngx_inline ngx_int_t 32 | ngx_test_str(ngx_str_t *nstr, char *cstr) 33 | { 34 | size_t len; 35 | 36 | len = ngx_strlen(cstr); 37 | 38 | return (nstr->len == len && ngx_memcmp(nstr->data, cstr, len) == 0); 39 | } 40 | 41 | #endif 42 | --------------------------------------------------------------------------------