├── .gitignore
├── README.md
├── config
├── ngx_http_dyups.h
├── ngx_http_dyups_lua.c
├── ngx_http_dyups_lua.h
├── ngx_http_dyups_module.c
└── t
├── addhosts.sh
├── curlhosts.sh
├── delhosts.sh
└── dyups.t
/.gitignore:
--------------------------------------------------------------------------------
1 | youcompleteme.py
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Description
2 | This module can be used to update your upstream-list without reloadding Nginx.
3 |
4 |
5 | ## Table of Contents
6 |
7 | * [Description](#description)
8 | * [Table of Contents](#table-of-contents)
9 | * [Example](#example)
10 | * [Installation](#installation)
11 | * [Directives](#directives)
12 | * [dyups_interface](#dyups_interface)
13 | * [dyups_shm_zone_size](#dyups_shm_zone_size)
14 | * [restful interface](#restful-interface)
15 | * [GET](#get)
16 | * [POST](#post)
17 | * [DELETE](#delete)
18 | * [Sample](#sample)
19 | * [C API](#c-api)
20 | * [Lua API Example](#lua-api-example)
21 | * [Change Log](#change-log)
22 | * [RELEASE V0.2.9](#release-v029)
23 | * [RELEASE V0.2.8](#release-v028)
24 | * [RELEASE V0.2.7](#release-v027)
25 | * [RELEASE V0.2.6](#release-v026)
26 | * [RELEASE V0.2.5](#release-v025)
27 | * [RELEASE V0.2.4](#release-v024)
28 | * [RELEASE V0.2.2](#release-v022)
29 | * [RELEASE V0.2.0](#release-v020)
30 | * [Compatibility](#compatibility)
31 | * [Tengine Compatibility](#tengine-compatibility)
32 | * [Module Compatibility](#module-compatibility)
33 | * [Run Tests](#run-tests)
34 | * [Author](#author)
35 | * [Copyright & License](#copyright--license)
36 |
37 | ## Example
38 |
39 | file: conf/nginx.conf
40 |
41 | `ATTENTION`: You MUST use nginx variable to do proxy_pass
42 |
43 | daemon off;
44 | error_log logs/error.log debug;
45 |
46 | events {
47 | }
48 |
49 | http {
50 |
51 | include conf/upstream.conf;
52 |
53 | server {
54 | listen 8080;
55 |
56 | location / {
57 | # The upstream here must be a nginx variable
58 | proxy_pass http://$dyups_host;
59 | }
60 | }
61 |
62 | server {
63 | listen 8088;
64 | location / {
65 | return 200 "8088";
66 | }
67 | }
68 |
69 | server {
70 | listen 8089;
71 | location / {
72 | return 200 "8089";
73 | }
74 | }
75 |
76 | server {
77 | listen 8081;
78 | location / {
79 | dyups_interface;
80 | }
81 | }
82 | }
83 |
84 | If your original config looks like this:
85 |
86 | proxy_pass http://upstream_name;
87 |
88 | please replace it with:
89 |
90 | set $ups upstream_name;
91 | proxy_pass http://$ups;
92 |
93 | `$ups` can be any valid nginx variable.
94 |
95 | file: conf/upstream.conf
96 |
97 | upstream host1 {
98 | server 127.0.0.1:8088;
99 | }
100 |
101 | upstream host2 {
102 | server 127.0.0.1:8089;
103 | }
104 |
105 |
106 | ## Installation
107 |
108 | * Only install dyups module
109 |
110 | ```bash
111 |
112 | $ git clone git://github.com/yzprofile/ngx_http_dyups_module.git
113 |
114 | # to compile as a static module
115 | $ ./configure --add-module=./ngx_http_dyups_module
116 |
117 | # to compile as a dynamic module
118 | $ ./configure --add-dynamic-module=./ngx_http_dyups_module
119 | ```
120 |
121 | * Install dyups module with lua-nginx-module and upstream check module
122 | * upstream check module: To make upstream check module work well with dyups module, you should use [patched upstream check module](https://github.com/yzprofile/nginx_upstream_check_module).
123 | * lua-nginx-module: To enable [dyups LUA API](#lua-api-example), you MUST put `--add-module=./lua-nginx-module` in front of `--add-module=./ngx_http_dyups_module` in the `./configure` command.
124 |
125 | ```bash
126 |
127 | $ git clone git://github.com/yzprofile/ngx_http_dyups_module.git
128 | $ git clone git@github.com:yzprofile/nginx_upstream_check_module.git
129 | $ git clone git@github.com:openresty/lua-nginx-module.git
130 |
131 | # to compile as a static module
132 | $ ./configure --add-module=./nginx_upstream_check_module --add-module=./lua-nginx-module --add-module=./ngx_http_dyups_module
133 | ```
134 |
135 | ## Directives
136 |
137 | ### dyups_interface
138 |
139 | Syntax: **dyups_interface**
140 |
141 | Default: `none`
142 |
143 | Context: `loc`
144 |
145 | This directive set the interface location where you can add or delete the upstream list. See the section of Interface for detail.
146 |
147 |
148 | ### dyups_shm_zone_size
149 |
150 | Syntax: **dyups_shm_zone_size** `size`
151 |
152 | Default: `2MB`
153 |
154 | Context: `main`
155 |
156 | This directive set the size of share memory which used to store the commands.
157 |
158 |
159 | ## restful interface
160 |
161 | ### GET
162 | - `/list` get the list of upstreams
163 |
164 | ### POST
165 | - `/upstream/name` update one upstream
166 | - `body` commands;
167 | - `body` server ip:port;
168 |
169 | ### DELETE
170 | - `/upstream/name` delete one upstream
171 |
172 | Call the interface, when you get the return code is `HTTP_INTERNAL_SERVER_ERROR 500`, you need to reload nginx to make the Nginx work at a good state.
173 |
174 | If you got `HTTP_CONFLICT 409`, you need resend the same commands again latter.
175 |
176 | The /list and /detail interface will return `HTTP_NO_CONTENT 204` when there is no upstream.
177 |
178 | Other code means you should modify your commands and call the interface again.
179 |
180 | `ATTENTION`: You also need a `third-party` to generate the new config and dump it to Nginx'conf directory.
181 |
182 | ### Sample
183 |
184 | ```bash
185 | » curl -H "host: dyhost" 127.0.0.1:8080
186 |
187 |
502 Bad Gateway
188 |
189 | 502 Bad Gateway
190 |
nginx/1.3.13
191 |
192 |
193 |
194 | » curl -d "server 127.0.0.1:8089;server 127.0.0.1:8088;" 127.0.0.1:8081/upstream/dyhost
195 | success
196 |
197 | » curl -H "host: dyhost" 127.0.0.1:8080
198 | 8089
199 |
200 | » curl -H "host: dyhost" 127.0.0.1:8080
201 | 8088
202 |
203 | » curl 127.0.0.1:8081/detail
204 | host1
205 | server 127.0.0.1:8088 weight=1 max_conns=0 max_fails=1 fail_timeout=10 backup=0 down=0
206 |
207 | host2
208 | server 127.0.0.1:8089 weight=1 max_conns=0 max_fails=1 fail_timeout=10 backup=0 down=0
209 |
210 | dyhost
211 | server 127.0.0.1:8089 weight=1 max_conns=0 max_fails=1 fail_timeout=10 backup=0 down=0
212 | server 127.0.0.1:8088 weight=1 max_conns=0 max_fails=1 fail_timeout=10 backup=0 down=0
213 |
214 | » curl -i -X DELETE 127.0.0.1:8081/upstream/dyhost
215 | success
216 |
217 | » curl 127.0.0.1:8081/detail
218 | host1
219 | server 127.0.0.1:8088 weight=1 max_conns=0 max_fails=1 fail_timeout=10 backup=0 down=0
220 |
221 | host2
222 | server 127.0.0.1:8089 weight=1 max_conns=0 max_fails=1 fail_timeout=10 backup=0 down=0
223 | ```
224 |
225 | ## C API
226 |
227 | ```c
228 | extern ngx_flag_t ngx_http_dyups_api_enable;
229 | ngx_int_t ngx_dyups_update_upstream(ngx_str_t *name, ngx_buf_t *buf,
230 | ngx_str_t *rv);
231 | ngx_int_t ngx_dyups_delete_upstream(ngx_str_t *name, ngx_str_t *rv);
232 |
233 | extern ngx_dyups_add_upstream_filter_pt ngx_dyups_add_upstream_top_filter;
234 | extern ngx_dyups_del_upstream_filter_pt ngx_dyups_del_upstream_top_filter;
235 |
236 | ```
237 |
238 | ## Lua API Example
239 |
240 | NOTICE:
241 | you should add the directive `dyups_interface` into your config file to active this feature
242 |
243 | ```lua
244 | content_by_lua '
245 | local dyups = require "ngx.dyups"
246 |
247 | local status, rv = dyups.update("test", [[server 127.0.0.1:8088;]]);
248 | ngx.print(status, rv)
249 | if status ~= ngx.HTTP_OK then
250 | ngx.print(status, rv)
251 | return
252 | end
253 | ngx.print("update success")
254 |
255 | status, rv = dyups.delete("test")
256 | if status ~= ngx.HTTP_OK then
257 | ngx.print(status, rv)
258 | return
259 | end
260 | ngx.print("delete success")
261 | ';
262 |
263 | ```
264 |
265 |
266 | ## Change Log
267 |
268 | ### RELEASE V0.2.9
269 |
270 | Feature: Added add/del upstream filter to make other modules operate upstream easily after upstream changed
271 |
272 | ### RELEASE V0.2.8
273 |
274 | Bugfixed: upstream connect failed caused coredump
275 |
276 |
277 | ### RELEASE V0.2.7
278 |
279 | Supported: C API and Lua API
280 |
281 |
282 | ### RELEASE V0.2.6
283 | Bugfixed: Supported sandbox before updatting
284 |
285 |
286 | ### RELEASE V0.2.5
287 | 1. Bugfixed: wrong string comparison for string "upstream", @chobits
288 | 2. Bugfixed: that response of /detail uri has no Content-Length header, @chobits
289 | 3. Feature: if you use this [branch of tengine](https://github.com/alibaba/tengine/tree/jst), update upstream rbtree, @SarahWang
290 | 4. Feature: simplify upstream parsing methods via ngx_conf_parse api, @chobits
291 |
292 | ### RELEASE V0.2.4
293 |
294 | 1. Bugfixed: client timed out cause a coredumped while adding an exist upstream
295 | 2. Bugfixed: when proxy_pass to a no-variable address dyups will coredump
296 |
297 | ### RELEASE V0.2.2
298 |
299 | 1. Bugfixed: upstream will be deleted in the process of finding upstream.
300 |
301 | ### RELEASE V0.2.0
302 |
303 | 1. check every commands to make sure they are all ok before update upstream. `done`
304 |
305 | 2. support ip_hash and keepalive or other upstream module `done`
306 |
307 | 3. support `weight`,`max_fails`,`fail_timeout`,`backup` `done`
308 |
309 | 4. support health check module, you should use [this branch of Tengine](https://github.com/yaoweibin/tengine/tree/dynamic_upstream_check) or wait for it's release. `done`
310 |
311 | 5. restore upstream configuration in `init process` handler. `done`
312 |
313 |
314 | ## Compatibility
315 |
316 | ### Tengine Compatibility
317 |
318 | This module has been merged into Tengine.
319 |
320 | * [Tengine dyups module documentation](http://tengine.taobao.org/document/http_dyups.html)
321 |
322 | ### Module Compatibility
323 |
324 | * [lua-upstream-nginx-module](https://github.com/agentzh/lua-upstream-nginx-module): You can use `lua-upstream-nginx-module` to get more detail infomation of upstream.
325 | * [yzprofile/upstream check module](https://github.com/yzprofile/nginx_upstream_check_module): To make upstream check module work well with dyups module, you should use [patched upstream check module](https://github.com/yzprofile/nginx_upstream_check_module).
326 |
327 | ## Run Tests
328 |
329 | ```bash
330 | $ hg clone http://hg.nginx.org/nginx-tests/ ..
331 | (Or git clone git@github.com:nginx/nginx-tests/ .. if you prefer the GitHub mirror)
332 | $ TEST_NGINX_BINARY=/path/to/your/nginx/dir/sbin/nginx prove -I ../nginx-tests/lib ./t/dyups.t
333 | ```
334 |
335 | To make the tests pass, you should also install lua-nginx-module and patched upstream check module.
336 |
337 | ## Author
338 |
339 | yzprofile (袁茁) yzprofile@gmail.com
340 |
341 | chobits (王笑臣) wangxiaochen0@gmail.com, Alibaba Inc.
342 |
343 | ## Copyright & License
344 |
345 | These codes are licenced under the BSD license.
346 |
347 | ```
348 | Copyright (C) 2012-2020 by Zhuo Yuan (yzprofile)
349 |
350 | All rights reserved.
351 |
352 | Redistribution and use in source and binary forms, with or without
353 | modification, are permitted provided that the following conditions
354 | are met:
355 |
356 | * Redistributions of source code must retain the above copyright
357 | notice, this list of conditions and the following disclaimer.
358 |
359 | * Redistributions in binary form must reproduce the above copyright
360 | notice, this list of conditions and the following disclaimer in the
361 | documentation and/or other materials provided with the distribution.
362 |
363 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
364 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
365 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
366 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
367 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
368 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
369 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
370 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
371 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
372 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
373 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
374 | ```
375 |
--------------------------------------------------------------------------------
/config:
--------------------------------------------------------------------------------
1 | ngx_addon_name=ngx_http_dyups_module
2 |
3 | HTTP_DYUPS_SRCS="$ngx_addon_dir/ngx_http_dyups_module.c"
4 | HTTP_DYUPS_DEPS="$ngx_addon_dir/ngx_http_dyups.h"
5 |
6 | dyups_lua() {
7 | echo " + dyups module support lua"
8 | have=NGX_DYUPS_LUA . auto/have
9 | HTTP_DYUPS_SRCS="$HTTP_DYUPS_SRCS $ngx_addon_dir/ngx_http_dyups_lua.c"
10 | HTTP_DYUPS_DEPS="$HTTP_DYUPS_DEPS $ngx_addon_dir/ngx_http_dyups_lua.h"
11 | }
12 |
13 | if test -n "$ngx_module_link"; then
14 | if test -n "$HTTP_LUA_SRCS"; then
15 | dyups_lua
16 | if test -n "$ngx_http_lua_module_LIBS"; then
17 | ngx_module_libs="$ngx_module_libs $ngx_http_lua_module_LIBS"
18 | fi
19 | fi
20 | ngx_module_type=HTTP
21 | ngx_module_name=$ngx_addon_name
22 | ngx_module_srcs="$HTTP_DYUPS_SRCS"
23 | ngx_module_incs="$ngx_addon_dir"
24 | ngx_module_deps="$HTTP_DYUPS_DEPS"
25 | . auto/module
26 | else
27 | if $HTTP_AUX_FILTER_MODULES | grep "ngx_http_lua_module" > /dev/null; then
28 | dyups_lua
29 | fi
30 | HTTP_MODULES="$HTTP_MODULES ngx_http_dyups_module"
31 | HTTP_INCS="$HTTP_INCS $ngx_addon_dir/"
32 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $HTTP_DYUPS_SRCS"
33 | NGX_ADDON_DEPS="$NGX_ADDON_DEPS $HTTP_DYUPS_DEPS"
34 | fi
35 |
36 | have=NGX_DYUPS . auto/have
37 |
--------------------------------------------------------------------------------
/ngx_http_dyups.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010-2015 Alibaba Group Holding Limited
3 | */
4 |
5 |
6 | #ifndef _NGX_HTTP_DYUPS_H_INCLUDE_
7 | #define _NGX_HTTP_DYUPS_H_INCLUDE_
8 |
9 |
10 | #include
11 | #include
12 |
13 |
14 | ngx_int_t ngx_dyups_update_upstream(ngx_str_t *name, ngx_buf_t *buf,
15 | ngx_str_t *rv);
16 |
17 | ngx_int_t ngx_dyups_delete_upstream(ngx_str_t *name, ngx_str_t *rv);
18 |
19 |
20 | typedef ngx_int_t (*ngx_dyups_add_upstream_filter_pt)
21 | (ngx_http_upstream_main_conf_t *umcf, ngx_http_upstream_srv_conf_t *uscf);
22 | typedef ngx_int_t (*ngx_dyups_del_upstream_filter_pt)
23 | (ngx_http_upstream_main_conf_t *umcf, ngx_http_upstream_srv_conf_t *uscf);
24 |
25 |
26 | extern ngx_flag_t ngx_http_dyups_api_enable;
27 | extern ngx_dyups_add_upstream_filter_pt ngx_dyups_add_upstream_top_filter;
28 | extern ngx_dyups_del_upstream_filter_pt ngx_dyups_del_upstream_top_filter;
29 |
30 | #endif
31 |
--------------------------------------------------------------------------------
/ngx_http_dyups_lua.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 |
6 | static int ngx_http_dyups_lua_register(lua_State *L);
7 | static int ngx_http_lua_update_upstream(lua_State *L);
8 | static int ngx_http_lua_delete_upstream(lua_State *L);
9 |
10 |
11 | static int
12 | ngx_http_lua_update_upstream(lua_State *L)
13 | {
14 | size_t size;
15 | ngx_int_t status;
16 | ngx_str_t name, rv;
17 | ngx_buf_t buf;
18 |
19 | if (lua_gettop(L) != 2) {
20 | return luaL_error(L, "exactly 2 arguments expected");
21 | }
22 |
23 | name.data = (u_char *) luaL_checklstring(L, 1, &name.len);
24 | buf.pos = buf.start = (u_char *) luaL_checklstring(L, 2, &size);
25 | buf.last = buf.end = buf.pos + size;
26 |
27 | status = ngx_dyups_update_upstream(&name, &buf, &rv);
28 |
29 | lua_pushinteger(L, (lua_Integer) status);
30 | lua_pushlstring(L, (char *) rv.data, rv.len);
31 |
32 | return 2;
33 | }
34 |
35 |
36 | static int
37 | ngx_http_lua_delete_upstream(lua_State *L)
38 | {
39 | ngx_int_t status;
40 | ngx_str_t name, rv;
41 |
42 | if (lua_gettop(L) != 1) {
43 | return luaL_error(L, "exactly 1 argument expected");
44 | }
45 |
46 | name.data = (u_char *) luaL_checklstring(L, 1, &name.len);
47 |
48 | status = ngx_dyups_delete_upstream(&name, &rv);
49 |
50 | lua_pushinteger(L, (lua_Integer) status);
51 | lua_pushlstring(L, (char *) rv.data, rv.len);
52 |
53 | return 2;
54 | }
55 |
56 |
57 | static int
58 | ngx_http_dyups_lua_register(lua_State *L)
59 | {
60 | lua_createtable(L, 0, 1);
61 |
62 | lua_pushcfunction(L, ngx_http_lua_update_upstream);
63 | lua_setfield(L, -2, "update");
64 |
65 | lua_pushcfunction(L, ngx_http_lua_delete_upstream);
66 | lua_setfield(L, -2, "delete");
67 |
68 | return 1;
69 | }
70 |
71 |
72 | ngx_int_t
73 | ngx_http_dyups_lua_preload(ngx_conf_t *cf)
74 | {
75 | if (ngx_http_lua_add_package_preload(cf, "ngx.dyups",
76 | ngx_http_dyups_lua_register)
77 | != NGX_OK)
78 | {
79 | return NGX_ERROR;
80 | }
81 |
82 | return NGX_OK;
83 | }
84 |
--------------------------------------------------------------------------------
/ngx_http_dyups_lua.h:
--------------------------------------------------------------------------------
1 | #ifndef _NGX_HTTP_DYUPS_LUA_H_INCLUDE_
2 | #define _NGX_HTTP_DYUPS_LUA_H_INCLUDE_
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 |
11 | ngx_int_t ngx_http_dyups_lua_preload(ngx_conf_t *cf);
12 |
13 |
14 | #endif
15 |
--------------------------------------------------------------------------------
/ngx_http_dyups_module.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #ifdef NGX_DYUPS_LUA
4 | #include
5 | #endif
6 |
7 |
8 | #define NGX_DYUPS_DELETING 1
9 | #define NGX_DYUPS_DELETED 2
10 |
11 | #define NGX_DYUPS_SHM_NAME_LEN 32
12 |
13 | #define NGX_DYUPS_DELETE 1
14 | #define NGX_DYUPS_ADD 2
15 |
16 | #if (NGX_DEBUG)
17 | #define NGX_DYUPS_INIT_SIZE 1
18 | #else
19 | #define NGX_DYUPS_INIT_SIZE 1024
20 | #endif
21 |
22 |
23 | typedef struct {
24 | ngx_uint_t idx;
25 | ngx_uint_t *ref;
26 | ngx_uint_t deleted;
27 | ngx_flag_t dynamic;
28 | ngx_pool_t *pool;
29 | ngx_http_conf_ctx_t *ctx;
30 | ngx_http_upstream_srv_conf_t *upstream;
31 | } ngx_http_dyups_srv_conf_t;
32 |
33 |
34 | typedef struct {
35 | ngx_flag_t enable;
36 | ngx_array_t dy_upstreams;/* ngx_http_dyups_srv_conf_t */
37 | ngx_str_t shm_name;
38 | ngx_uint_t shm_size;
39 | } ngx_http_dyups_main_conf_t;
40 |
41 |
42 | typedef struct {
43 | ngx_uint_t ref;
44 | ngx_http_upstream_init_peer_pt init;
45 | } ngx_http_dyups_upstream_srv_conf_t;
46 |
47 |
48 | typedef struct {
49 | void *data;
50 | ngx_http_dyups_upstream_srv_conf_t *scf;
51 | ngx_event_get_peer_pt get;
52 | ngx_event_free_peer_pt free;
53 | #if (NGX_HTTP_SSL)
54 | ngx_ssl_session_t *ssl_session;
55 | #endif
56 | } ngx_http_dyups_ctx_t;
57 |
58 |
59 | typedef struct ngx_dyups_upstream_s {
60 | ngx_rbtree_node_t node;
61 | ngx_str_t name;
62 | ngx_str_t content;
63 | ngx_uint_t version;
64 | } ngx_dyups_upstream_t;
65 |
66 |
67 | typedef struct ngx_dyups_shctx_s {
68 | ngx_uint_t version;
69 | ngx_rbtree_t rbtree;
70 | ngx_rbtree_node_t sentinel;
71 | } ngx_dyups_shctx_t;
72 |
73 |
74 | typedef struct ngx_dyups_global_ctx_s {
75 | ngx_slab_pool_t *shpool;
76 | ngx_dyups_shctx_t *sh;
77 | ngx_uint_t version;
78 | } ngx_dyups_global_ctx_t;
79 |
80 |
81 | static ngx_int_t ngx_http_dyups_pre_conf(ngx_conf_t *cf);
82 | static ngx_int_t ngx_http_dyups_init(ngx_conf_t *cf);
83 | static void *ngx_http_dyups_create_main_conf(ngx_conf_t *cf);
84 | static char *ngx_http_dyups_init_main_conf(ngx_conf_t *cf, void *conf);
85 | static char *ngx_http_dyups_interface(ngx_conf_t *cf, ngx_command_t *cmd,
86 | void *conf);
87 | static ngx_int_t ngx_http_dyups_interface_handler(ngx_http_request_t *r);
88 | static ngx_int_t ngx_http_dyups_interface_read_body(ngx_http_request_t *r);
89 | static ngx_buf_t *ngx_http_dyups_read_body(ngx_http_request_t *r);
90 | static ngx_buf_t *ngx_http_dyups_read_body_from_file(ngx_http_request_t *r);
91 | static void ngx_http_dyups_body_handler(ngx_http_request_t *r);
92 | static void ngx_http_dyups_send_response(ngx_http_request_t *r,
93 | ngx_int_t status, ngx_str_t *content);
94 | static ngx_int_t ngx_http_dyups_do_get(ngx_http_request_t *r,
95 | ngx_array_t *resource);
96 | static ngx_int_t ngx_http_dyups_do_delete(ngx_http_request_t *r,
97 | ngx_array_t *resource);
98 | static ngx_http_dyups_srv_conf_t *ngx_dyups_find_upstream(ngx_str_t *name,
99 | ngx_int_t *idx);
100 | static ngx_int_t ngx_dyups_add_server(ngx_http_dyups_srv_conf_t *duscf,
101 | ngx_buf_t *buf);
102 | static ngx_int_t ngx_dyups_init_upstream(ngx_http_dyups_srv_conf_t *duscf,
103 | ngx_str_t *name, ngx_uint_t index);
104 | static void ngx_dyups_mark_upstream_delete(ngx_http_dyups_srv_conf_t *duscf);
105 | static ngx_int_t ngx_http_dyups_init_peer(ngx_http_request_t *r,
106 | ngx_http_upstream_srv_conf_t *us);
107 | static ngx_int_t ngx_http_dyups_get_peer(ngx_peer_connection_t *pc, void *data);
108 | static void ngx_http_dyups_free_peer(ngx_peer_connection_t *pc, void *data,
109 | ngx_uint_t state);
110 | static void *ngx_http_dyups_create_srv_conf(ngx_conf_t *cf);
111 | static ngx_buf_t *ngx_http_dyups_show_list(ngx_http_request_t *r);
112 | static ngx_int_t ngx_http_dyups_init_shm_zone(ngx_shm_zone_t *shm_zone,
113 | void *data);
114 | static char *ngx_http_dyups_init_shm(ngx_conf_t *cf, void *conf);
115 | static ngx_int_t ngx_http_dyups_get_shm_name(ngx_str_t *shm_name,
116 | ngx_pool_t *pool);
117 | static ngx_int_t ngx_http_dyups_init_process(ngx_cycle_t *cycle);
118 | static void ngx_http_dyups_exit_process(ngx_cycle_t *cycle);
119 | static ngx_int_t ngx_dyups_shm_update_ups(ngx_str_t *name, ngx_buf_t *body);
120 | static ngx_int_t ngx_dyups_shm_delete_ups(ngx_str_t *name);
121 | static void ngx_dyups_shm_free_ups(ngx_slab_pool_t *shpool,
122 | ngx_dyups_upstream_t *ups);
123 | static ngx_array_t *ngx_dyups_parse_path(ngx_pool_t *pool, ngx_str_t *path);
124 | static ngx_int_t ngx_dyups_do_delete(ngx_str_t *name, ngx_str_t *rv);
125 | static ngx_int_t ngx_dyups_do_update(ngx_str_t *name, ngx_buf_t *buf,
126 | ngx_str_t *rv);
127 | static ngx_int_t ngx_dyups_sandbox_update(ngx_buf_t *buf, ngx_str_t *rv);
128 | static void ngx_http_dyups_clean_request(void *data);
129 | static ngx_int_t ngx_http_variable_dyups(ngx_http_request_t *r,
130 | ngx_http_variable_value_t *v, uintptr_t data);
131 | static ngx_int_t ngx_http_dyups_add_vars(ngx_conf_t *cf);
132 | static ngx_int_t ngx_http_dyups_reload();
133 |
134 | #if (NGX_HTTP_SSL)
135 | static ngx_int_t ngx_http_dyups_set_peer_session(ngx_peer_connection_t *pc,
136 | void *data);
137 | static void ngx_http_dyups_save_peer_session(ngx_peer_connection_t *pc,
138 | void *data);
139 | #endif
140 |
141 |
142 | static ngx_int_t ngx_dyups_add_upstream_filter(
143 | ngx_http_upstream_main_conf_t *umcf, ngx_http_upstream_srv_conf_t *uscf);
144 | static ngx_int_t ngx_dyups_del_upstream_filter(
145 | ngx_http_upstream_main_conf_t *umcf, ngx_http_upstream_srv_conf_t *uscf);
146 |
147 |
148 | ngx_int_t (*ngx_dyups_add_upstream_top_filter)
149 | (ngx_http_upstream_main_conf_t *umcf, ngx_http_upstream_srv_conf_t *uscf);
150 | ngx_int_t (*ngx_dyups_del_upstream_top_filter)
151 | (ngx_http_upstream_main_conf_t *umcf, ngx_http_upstream_srv_conf_t *uscf);
152 |
153 |
154 | static ngx_http_variable_t ngx_http_dyups_variables[] = {
155 | { ngx_string("dyups_"), NULL, ngx_http_variable_dyups,
156 | 0, NGX_HTTP_VAR_PREFIX, 0 },
157 |
158 | ngx_http_null_variable
159 | };
160 |
161 |
162 | static ngx_command_t ngx_http_dyups_commands[] = {
163 |
164 | { ngx_string("dyups_interface"),
165 | NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
166 | ngx_http_dyups_interface,
167 | 0,
168 | 0,
169 | NULL },
170 |
171 | { ngx_string("dyups_shm_zone_size"),
172 | NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
173 | ngx_conf_set_size_slot,
174 | NGX_HTTP_MAIN_CONF_OFFSET,
175 | offsetof(ngx_http_dyups_main_conf_t, shm_size),
176 | NULL },
177 |
178 | ngx_null_command
179 | };
180 |
181 |
182 | static ngx_http_module_t ngx_http_dyups_module_ctx = {
183 | ngx_http_dyups_pre_conf, /* preconfiguration */
184 | ngx_http_dyups_init, /* postconfiguration */
185 |
186 | ngx_http_dyups_create_main_conf, /* create main configuration */
187 | ngx_http_dyups_init_main_conf, /* init main configuration */
188 |
189 | ngx_http_dyups_create_srv_conf, /* create server configuration */
190 | NULL, /* merge server configuration */
191 |
192 | NULL, /* create location configuration */
193 | NULL /* merge location configuration */
194 | };
195 |
196 |
197 | ngx_module_t ngx_http_dyups_module = {
198 | NGX_MODULE_V1,
199 | &ngx_http_dyups_module_ctx, /* module context */
200 | ngx_http_dyups_commands, /* module directives */
201 | NGX_HTTP_MODULE, /* module type */
202 | NULL, /* init master */
203 | NULL, /* init module */
204 | ngx_http_dyups_init_process, /* init process */
205 | NULL, /* init thread */
206 | NULL, /* exit thread */
207 | ngx_http_dyups_exit_process, /* exit process */
208 | NULL, /* exit master */
209 | NGX_MODULE_V1_PADDING
210 | };
211 |
212 |
213 | ngx_flag_t ngx_http_dyups_api_enable = 0;
214 | static ngx_http_upstream_srv_conf_t ngx_http_dyups_deleted_upstream;
215 | static ngx_dyups_global_ctx_t ngx_dyups_global_ctx;
216 |
217 |
218 | static ngx_int_t
219 | ngx_http_dyups_pre_conf(ngx_conf_t *cf)
220 | {
221 | ngx_dyups_add_upstream_top_filter = ngx_dyups_add_upstream_filter;
222 | ngx_dyups_del_upstream_top_filter = ngx_dyups_del_upstream_filter;
223 |
224 | return ngx_http_dyups_add_vars(cf);
225 | }
226 |
227 | static ngx_int_t
228 | ngx_http_dyups_add_vars(ngx_conf_t *cf)
229 | {
230 | ngx_http_variable_t *cv, *v;
231 |
232 | for (cv = ngx_http_dyups_variables; cv->name.len; cv++) {
233 | v = ngx_http_add_variable(cf, &cv->name, cv->flags);
234 | if (v == NULL) {
235 | return NGX_ERROR;
236 | }
237 | *v = *cv;
238 | }
239 |
240 | return NGX_OK;
241 | }
242 |
243 |
244 | static char *
245 | ngx_http_dyups_interface(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
246 | {
247 | ngx_http_core_loc_conf_t *clcf;
248 | ngx_http_dyups_main_conf_t *dmcf;
249 |
250 | dmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_dyups_module);
251 | clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
252 | clcf->handler = ngx_http_dyups_interface_handler;
253 | dmcf->enable = 1;
254 |
255 | return NGX_CONF_OK;
256 | }
257 |
258 |
259 | static void *
260 | ngx_http_dyups_create_main_conf(ngx_conf_t *cf)
261 | {
262 | ngx_http_dyups_main_conf_t *dmcf;
263 |
264 | dmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dyups_main_conf_t));
265 | if (dmcf == NULL) {
266 | return NULL;
267 | }
268 |
269 | if (ngx_array_init(&dmcf->dy_upstreams, cf->pool, NGX_DYUPS_INIT_SIZE,
270 | sizeof(ngx_http_dyups_srv_conf_t))
271 | != NGX_OK)
272 | {
273 | return NULL;
274 | }
275 |
276 | dmcf->enable = NGX_CONF_UNSET;
277 | dmcf->shm_size = NGX_CONF_UNSET_UINT;
278 |
279 | return dmcf;
280 | }
281 |
282 |
283 | static char *
284 | ngx_http_dyups_init_main_conf(ngx_conf_t *cf, void *conf)
285 | {
286 | ngx_http_dyups_main_conf_t *dmcf = conf;
287 |
288 | if (dmcf->enable == NGX_CONF_UNSET) {
289 | dmcf->enable = 0;
290 | }
291 |
292 | dmcf->enable = dmcf->enable || ngx_http_dyups_api_enable;
293 |
294 | if (!dmcf->enable) {
295 | return NGX_CONF_OK;
296 | }
297 |
298 | if (dmcf->shm_size == NGX_CONF_UNSET_UINT) {
299 | dmcf->shm_size = 2 * 1024 * 1024;
300 | }
301 |
302 | return ngx_http_dyups_init_shm(cf, conf);
303 | }
304 |
305 |
306 | static char *
307 | ngx_http_dyups_init_shm(ngx_conf_t *cf, void *conf)
308 | {
309 | ngx_http_dyups_main_conf_t *dmcf = conf;
310 |
311 | ngx_shm_zone_t *shm_zone;
312 |
313 | if (ngx_http_dyups_get_shm_name(&dmcf->shm_name, cf->pool)
314 | != NGX_OK)
315 | {
316 | return NGX_CONF_ERROR;
317 | }
318 |
319 | shm_zone = ngx_shared_memory_add(cf, &dmcf->shm_name, dmcf->shm_size,
320 | &ngx_http_dyups_module);
321 | if (shm_zone == NULL) {
322 | return NGX_CONF_ERROR;
323 | }
324 |
325 | ngx_log_error(NGX_LOG_DEBUG, cf->log, 0,
326 | "[dyups] init shm:%V, size:%ui", &dmcf->shm_name,
327 | dmcf->shm_size);
328 |
329 | shm_zone->data = &ngx_dyups_global_ctx;
330 | shm_zone->init = ngx_http_dyups_init_shm_zone;
331 |
332 | return NGX_CONF_OK;
333 | }
334 |
335 |
336 | static ngx_int_t
337 | ngx_http_dyups_get_shm_name(ngx_str_t *shm_name, ngx_pool_t *pool)
338 | {
339 | u_char *last;
340 |
341 | shm_name->data = ngx_palloc(pool, NGX_DYUPS_SHM_NAME_LEN);
342 | if (shm_name->data == NULL) {
343 | return NGX_ERROR;
344 | }
345 |
346 | last = ngx_snprintf(shm_name->data, NGX_DYUPS_SHM_NAME_LEN, "%s#shm",
347 | "ngx_http_dyups_module");
348 |
349 | shm_name->len = last - shm_name->data;
350 |
351 | return NGX_OK;
352 | }
353 |
354 |
355 | static ngx_int_t
356 | ngx_http_dyups_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data)
357 | {
358 | ngx_dyups_global_ctx_t *octx = data;
359 |
360 | ngx_slab_pool_t *shpool;
361 | ngx_dyups_shctx_t *sh;
362 |
363 | if (octx != NULL) {
364 | ngx_dyups_global_ctx.sh = octx->sh;
365 | ngx_dyups_global_ctx.shpool = octx->shpool;
366 | return NGX_OK;
367 | }
368 |
369 | shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
370 | sh = ngx_slab_alloc(shpool, sizeof(ngx_dyups_shctx_t));
371 | if (sh == NULL) {
372 | return NGX_ERROR;
373 | }
374 |
375 | ngx_dyups_global_ctx.sh = sh;
376 | ngx_dyups_global_ctx.shpool = shpool;
377 |
378 | sh->version = 0;
379 |
380 | ngx_rbtree_init(&sh->rbtree, &sh->sentinel, ngx_str_rbtree_insert_value);
381 |
382 | return NGX_OK;
383 | }
384 |
385 |
386 | static void *
387 | ngx_http_dyups_create_srv_conf(ngx_conf_t *cf)
388 | {
389 | ngx_http_dyups_upstream_srv_conf_t *conf;
390 |
391 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dyups_upstream_srv_conf_t));
392 | if (conf == NULL) {
393 | return NULL;
394 | }
395 |
396 | /*
397 | conf->init = NULL;
398 | */
399 | return conf;
400 | }
401 |
402 |
403 | static ngx_int_t
404 | ngx_http_dyups_init(ngx_conf_t *cf)
405 | {
406 | ngx_url_t u;
407 | ngx_uint_t i;
408 | ngx_http_dyups_srv_conf_t *duscf;
409 | ngx_http_upstream_server_t *us;
410 | ngx_http_dyups_main_conf_t *dmcf;
411 | ngx_http_upstream_srv_conf_t **uscfp;
412 | ngx_http_upstream_main_conf_t *umcf;
413 | ngx_http_dyups_upstream_srv_conf_t *dscf;
414 |
415 | dmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_dyups_module);
416 | umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);
417 |
418 | if (!dmcf->enable) {
419 | return NGX_OK;
420 | }
421 |
422 | uscfp = umcf->upstreams.elts;
423 | for (i = 0; i < umcf->upstreams.nelts; i++) {
424 |
425 | duscf = ngx_array_push(&dmcf->dy_upstreams);
426 | if (duscf == NULL) {
427 | return NGX_ERROR;
428 | }
429 |
430 | ngx_memzero(duscf, sizeof(ngx_http_dyups_srv_conf_t));
431 |
432 | duscf->pool = NULL;
433 | duscf->upstream = uscfp[i];
434 | duscf->dynamic = (uscfp[i]->port == 0
435 | && uscfp[i]->srv_conf && uscfp[i]->servers
436 | && uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE);
437 | duscf->deleted = 0;
438 | duscf->idx = i;
439 |
440 | if (duscf->dynamic) {
441 | dscf = duscf->upstream->srv_conf[ngx_http_dyups_module.ctx_index];
442 | duscf->ref = &dscf->ref;
443 | }
444 | }
445 |
446 | /* alloc a dummy upstream */
447 |
448 | ngx_memzero(&ngx_http_dyups_deleted_upstream,
449 | sizeof(ngx_http_upstream_srv_conf_t));
450 | ngx_http_dyups_deleted_upstream.srv_conf = ((ngx_http_conf_ctx_t *)
451 | (cf->ctx))->srv_conf;
452 | ngx_http_dyups_deleted_upstream.servers = ngx_array_create(cf->pool, 1,
453 | sizeof(ngx_http_upstream_server_t));
454 |
455 | us = ngx_array_push(ngx_http_dyups_deleted_upstream.servers);
456 | if (us == NULL) {
457 | return NGX_ERROR;
458 | }
459 |
460 | ngx_memzero(&u, sizeof(ngx_url_t));
461 | ngx_memzero(us, sizeof(ngx_http_upstream_server_t));
462 |
463 | u.default_port = 80;
464 | ngx_str_set(&u.url, "0.0.0.0");
465 |
466 | if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
467 | if (u.err) {
468 | ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
469 | "[dyups] %s in init", u.err);
470 | }
471 |
472 | return NGX_ERROR;
473 | }
474 |
475 | us->addrs = u.addrs;
476 | us->naddrs = u.naddrs;
477 | us->down = 1;
478 |
479 | ngx_str_set(&ngx_http_dyups_deleted_upstream.host,
480 | "_dyups_upstream_down_host_");
481 | ngx_http_dyups_deleted_upstream.file_name = (u_char *) "dyups_upstream";
482 |
483 | #ifdef NGX_DYUPS_LUA
484 | return ngx_http_dyups_lua_preload(cf);
485 | #else
486 | return NGX_OK;
487 | #endif
488 | }
489 |
490 |
491 | static ngx_int_t
492 | ngx_http_dyups_init_process(ngx_cycle_t *cycle)
493 | {
494 | ngx_core_conf_t *ccf;
495 | ngx_slab_pool_t *shpool;
496 | ngx_dyups_shctx_t *sh;
497 | ngx_http_dyups_main_conf_t *dmcf;
498 |
499 | ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
500 |
501 | dmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,
502 | ngx_http_dyups_module);
503 |
504 | if (!dmcf || !dmcf->enable || ngx_process == NGX_PROCESS_HELPER) {
505 | ngx_http_dyups_api_enable = 0;
506 | return NGX_OK;
507 | }
508 |
509 | ngx_http_dyups_api_enable = 1;
510 |
511 | shpool = ngx_dyups_global_ctx.shpool;
512 | sh = ngx_dyups_global_ctx.sh;
513 |
514 | ngx_shmtx_lock(&shpool->mutex);
515 |
516 | ngx_http_dyups_reload();
517 |
518 | ngx_shmtx_unlock(&shpool->mutex);
519 | return NGX_OK;
520 | }
521 |
522 |
523 | static void
524 | ngx_http_dyups_exit_process(ngx_cycle_t *cycle)
525 | {
526 | ngx_uint_t i;
527 | ngx_http_dyups_srv_conf_t *duscfs, *duscf;
528 | ngx_http_dyups_main_conf_t *dumcf;
529 |
530 | dumcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,
531 | ngx_http_dyups_module);
532 | if (!dumcf) {
533 | return;
534 | }
535 |
536 | duscfs = dumcf->dy_upstreams.elts;
537 | for (i = 0; i < dumcf->dy_upstreams.nelts; i++) {
538 |
539 | duscf = &duscfs[i];
540 |
541 | if (duscf->pool) {
542 | ngx_destroy_pool(duscf->pool);
543 | duscf->pool = NULL;
544 | }
545 | }
546 | }
547 |
548 |
549 | static ngx_int_t
550 | ngx_http_dyups_interface_handler(ngx_http_request_t *r)
551 | {
552 | ngx_array_t *res;
553 |
554 | res = ngx_dyups_parse_path(r->pool, &r->uri);
555 | if (res == NULL) {
556 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
557 | }
558 |
559 | if (r->method == NGX_HTTP_GET) {
560 | return ngx_http_dyups_do_get(r, res);
561 | }
562 |
563 | if (r->method == NGX_HTTP_DELETE) {
564 | return ngx_http_dyups_do_delete(r, res);
565 | }
566 |
567 | return ngx_http_dyups_interface_read_body(r);
568 | }
569 |
570 |
571 | ngx_int_t
572 | ngx_dyups_delete_upstream(ngx_str_t *name, ngx_str_t *rv)
573 | {
574 | ngx_int_t status, rc;
575 | ngx_slab_pool_t *shpool;
576 | ngx_http_dyups_main_conf_t *dmcf;
577 |
578 | dmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,
579 | ngx_http_dyups_module);
580 | shpool = ngx_dyups_global_ctx.shpool;
581 |
582 | if (!ngx_http_dyups_api_enable) {
583 | ngx_str_set(rv, "API disabled\n");
584 | return NGX_HTTP_NOT_ALLOWED;
585 | }
586 |
587 | ngx_shmtx_lock(&shpool->mutex);
588 |
589 | status = ngx_dyups_do_delete(name, rv);
590 | if (status != NGX_HTTP_OK) {
591 | goto finish;
592 | }
593 |
594 | rc = ngx_dyups_shm_delete_ups(name);
595 | if (rc != NGX_OK) {
596 | ngx_str_set(rv, "shm delete failed");
597 | ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "[dyups] %V", &rv);
598 | status = NGX_HTTP_INTERNAL_SERVER_ERROR;
599 | }
600 |
601 | finish:
602 |
603 | ngx_shmtx_unlock(&shpool->mutex);
604 |
605 | return status;
606 | }
607 |
608 |
609 | static ngx_int_t
610 | ngx_http_dyups_do_get(ngx_http_request_t *r, ngx_array_t *resource)
611 | {
612 | ngx_int_t rc, status;
613 | ngx_buf_t *buf;
614 | ngx_str_t *value;
615 | ngx_chain_t out;
616 |
617 | rc = ngx_http_discard_request_body(r);
618 | if (rc != NGX_OK) {
619 | return rc;
620 | }
621 |
622 | if (resource->nelts == 0) {
623 | return NGX_HTTP_NOT_FOUND;
624 | }
625 |
626 | buf = NULL;
627 | value = resource->elts;
628 |
629 | if (value[0].len == 4
630 | && ngx_strncasecmp(value[0].data, (u_char *) "list", 4) == 0)
631 | {
632 | buf = ngx_http_dyups_show_list(r);
633 | if (buf == NULL) {
634 | status = NGX_HTTP_INTERNAL_SERVER_ERROR;
635 | goto finish;
636 | }
637 | }
638 |
639 | if (buf != NULL && ngx_buf_size(buf) == 0) {
640 | status = NGX_HTTP_NO_CONTENT;
641 | } else {
642 | status = buf ? NGX_HTTP_OK : NGX_HTTP_NOT_FOUND;
643 | }
644 |
645 | finish:
646 |
647 | r->headers_out.status = status;
648 |
649 | if (status != NGX_HTTP_OK) {
650 | r->headers_out.content_length_n = 0;
651 | } else {
652 | r->headers_out.content_length_n = ngx_buf_size(buf);
653 | }
654 |
655 | rc = ngx_http_send_header(r);
656 | if (rc == NGX_ERROR || rc > NGX_OK) {
657 | return rc;
658 | }
659 |
660 | if (status != NGX_HTTP_OK) {
661 | return ngx_http_send_special(r, NGX_HTTP_FLUSH);
662 | }
663 |
664 | buf->last_buf = 1;
665 | out.buf = buf;
666 | out.next = NULL;
667 |
668 | return ngx_http_output_filter(r, &out);
669 | }
670 |
671 |
672 |
673 | static ngx_int_t
674 | ngx_http_dyups_reload()
675 | {
676 | ngx_buf_t body;
677 | ngx_int_t rc;
678 | ngx_str_t rv;
679 | ngx_slab_pool_t *shpool;
680 | ngx_dyups_shctx_t *sh;
681 | ngx_rbtree_node_t *node, *root, *sentinel;
682 | ngx_dyups_upstream_t *ups;
683 |
684 | sh = ngx_dyups_global_ctx.sh;
685 | shpool = ngx_dyups_global_ctx.shpool;
686 | sentinel = sh->rbtree.sentinel;
687 | root = sh->rbtree.root;
688 |
689 | if (root == sentinel) {
690 | return NGX_OK;
691 | }
692 |
693 | for (node = ngx_rbtree_min(root, sentinel);
694 | node;
695 | node = ngx_rbtree_next(&sh->rbtree, node))
696 | {
697 | ups = (ngx_dyups_upstream_t*)
698 | ((char*) node - offsetof(ngx_dyups_upstream_t, node));
699 |
700 | if (ups->version > ngx_dyups_global_ctx.version) {
701 | continue;
702 | }
703 |
704 | body.start = body.pos = ups->content.data;
705 | body.end = body.last = ups->content.data + ups->content.len;
706 | rc = ngx_dyups_do_update(&ups->name, &body, &rv);
707 | ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
708 | "[dyups] sync add: %V rv: %V rc: %i",
709 | &ups->name, &rv, rc);
710 | }
711 |
712 | ngx_dyups_global_ctx.version = sh->version;
713 |
714 | return NGX_OK;
715 | }
716 |
717 |
718 | static ngx_buf_t *
719 | ngx_http_dyups_show_list(ngx_http_request_t *r)
720 | {
721 | ngx_uint_t len;
722 | ngx_buf_t *buf;
723 | ngx_slab_pool_t *shpool;
724 | ngx_dyups_shctx_t *sh;
725 | ngx_rbtree_node_t *node, *root, *sentinel;
726 | ngx_dyups_upstream_t *ups;
727 |
728 | sh = ngx_dyups_global_ctx.sh;
729 | shpool = ngx_dyups_global_ctx.shpool;
730 | sentinel = sh->rbtree.sentinel;
731 | root = sh->rbtree.root;
732 |
733 | if (root == sentinel) {
734 | return ngx_create_temp_buf(r->pool, 0);
735 | }
736 |
737 | ngx_shmtx_lock(&shpool->mutex);
738 |
739 | len = 0;
740 | for (node = ngx_rbtree_min(root, sentinel);
741 | node;
742 | node = ngx_rbtree_next(&sh->rbtree, node))
743 | {
744 | ups = (ngx_dyups_upstream_t*)
745 | ((char*) node - offsetof(ngx_dyups_upstream_t, node));
746 | len += + ups->name.len + ups->content.len + sizeof("upstream {\n\n}\n");
747 | }
748 |
749 | buf = ngx_create_temp_buf(r->pool, len);
750 | if (buf == NULL) {
751 | goto done;
752 | }
753 |
754 | for (node = ngx_rbtree_min(root, sentinel);
755 | node;
756 | node = ngx_rbtree_next(&sh->rbtree, node))
757 | {
758 | ups = (ngx_dyups_upstream_t*)
759 | ((char*) node - offsetof(ngx_dyups_upstream_t, node));
760 | buf->last = ngx_sprintf(buf->last, "upstream %V {\n", &ups->name);
761 | buf->last = ngx_sprintf(buf->last, "%V\n}\n", &ups->content);
762 | }
763 |
764 | done:
765 |
766 | ngx_shmtx_unlock(&shpool->mutex);
767 |
768 | return buf;
769 | }
770 |
771 |
772 | static ngx_int_t
773 | ngx_dyups_do_delete(ngx_str_t *name, ngx_str_t *rv)
774 | {
775 | ngx_int_t dumy;
776 | ngx_http_dyups_srv_conf_t *duscf;
777 |
778 | duscf = ngx_dyups_find_upstream(name, &dumy);
779 |
780 | if (duscf == NULL || duscf->deleted) {
781 |
782 | ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,
783 | "[dyups] not find upstream %V %p", name, duscf);
784 |
785 | ngx_str_set(rv, "not found uptream");
786 | return NGX_HTTP_NOT_FOUND;
787 | }
788 |
789 | ngx_dyups_mark_upstream_delete(duscf);
790 |
791 | ngx_str_set(rv, "success");
792 |
793 | return NGX_HTTP_OK;
794 | }
795 |
796 |
797 | static ngx_int_t
798 | ngx_http_dyups_do_delete(ngx_http_request_t *r, ngx_array_t *resource)
799 | {
800 | ngx_str_t *value, name, rv;
801 | ngx_int_t status, rc;
802 | ngx_buf_t *b;
803 | ngx_chain_t out;
804 |
805 | rc = ngx_http_discard_request_body(r);
806 | if (rc != NGX_OK) {
807 | return rc;
808 | }
809 |
810 | if (resource->nelts != 2) {
811 | ngx_str_set(&rv, "not support this interface");
812 | status = NGX_HTTP_NOT_ALLOWED;
813 | goto finish;
814 | }
815 |
816 | value = resource->elts;
817 |
818 | if (value[0].len != 8
819 | || ngx_strncasecmp(value[0].data, (u_char *) "upstream", 8) != 0)
820 | {
821 | ngx_str_set(&rv, "not support this api");
822 | status = NGX_HTTP_NOT_ALLOWED;
823 | goto finish;
824 | }
825 |
826 | name = value[1];
827 |
828 | status = ngx_dyups_delete_upstream(&name, &rv);
829 |
830 | finish:
831 |
832 | r->headers_out.status = status;
833 | r->headers_out.content_length_n = rv.len;
834 |
835 | rc = ngx_http_send_header(r);
836 | if (rc == NGX_ERROR || rc > NGX_OK) {
837 | return rc;
838 | }
839 |
840 | if (rv.len == 0) {
841 | return ngx_http_send_special(r, NGX_HTTP_FLUSH);
842 | }
843 |
844 | b = ngx_create_temp_buf(r->pool, rv.len);
845 | if (b == NULL) {
846 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
847 | }
848 |
849 | b->pos = rv.data;
850 | b->last = rv.data + rv.len;
851 | b->last_buf = 1;
852 |
853 | out.buf = b;
854 | out.next = NULL;
855 |
856 | return ngx_http_output_filter(r, &out);
857 | }
858 |
859 |
860 | static ngx_int_t
861 | ngx_http_dyups_interface_read_body(ngx_http_request_t *r)
862 | {
863 | ngx_int_t rc;
864 |
865 | rc = ngx_http_read_client_request_body(r, ngx_http_dyups_body_handler);
866 |
867 | if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
868 | return rc;
869 | }
870 |
871 | return NGX_DONE;
872 | }
873 |
874 |
875 | static void
876 | ngx_http_dyups_body_handler(ngx_http_request_t *r)
877 | {
878 | ngx_str_t *value, rv, name;
879 | ngx_int_t status;
880 | ngx_buf_t *body;
881 | ngx_array_t *res;
882 |
883 | ngx_str_set(&rv, "");
884 |
885 | if (r->method != NGX_HTTP_POST) {
886 | status = NGX_HTTP_NOT_ALLOWED;
887 | goto finish;
888 | }
889 |
890 | res = ngx_dyups_parse_path(r->pool, &r->uri);
891 | if (res == NULL) {
892 | ngx_str_set(&rv, "out of memory");
893 | status = NGX_HTTP_INTERNAL_SERVER_ERROR;
894 | goto finish;
895 | }
896 |
897 | if (r->request_body == NULL || r->request_body->bufs == NULL) {
898 | status = NGX_HTTP_NO_CONTENT;
899 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
900 | "[dyups] interface no content");
901 | ngx_str_set(&rv, "no content\n");
902 | goto finish;
903 | }
904 |
905 | if (r->request_body->temp_file) {
906 |
907 | body = ngx_http_dyups_read_body_from_file(r);
908 | } else {
909 |
910 | body = ngx_http_dyups_read_body(r);
911 | }
912 |
913 | if (body == NULL) {
914 | status = NGX_HTTP_INTERNAL_SERVER_ERROR;
915 | ngx_str_set(&rv, "out of memory\n");
916 | goto finish;
917 | }
918 |
919 | if (res->nelts != 2) {
920 | ngx_str_set(&rv, "not support this interface");
921 | status = NGX_HTTP_NOT_FOUND;
922 | goto finish;
923 | }
924 |
925 | /*
926 | url: /upstream
927 | body: server ip:port weight
928 | */
929 |
930 | value = res->elts;
931 |
932 | if (value[0].len != 8
933 | || ngx_strncasecmp(value[0].data, (u_char *) "upstream", 8) != 0)
934 | {
935 | ngx_str_set(&rv, "not support this api");
936 | status = NGX_HTTP_NOT_FOUND;
937 | goto finish;
938 | }
939 |
940 | name = value[1];
941 |
942 | ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
943 | "[dyups] post upstream name: %V", &name);
944 |
945 | status = ngx_dyups_update_upstream(&name, body, &rv);
946 |
947 | finish:
948 |
949 | ngx_http_dyups_send_response(r, status, &rv);
950 | }
951 |
952 |
953 | ngx_int_t
954 | ngx_dyups_update_upstream(ngx_str_t *name, ngx_buf_t *buf, ngx_str_t *rv)
955 | {
956 | ngx_int_t status;
957 | ngx_slab_pool_t *shpool;
958 | ngx_http_dyups_main_conf_t *dmcf;
959 |
960 | dmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,
961 | ngx_http_dyups_module);
962 | shpool = ngx_dyups_global_ctx.shpool;
963 |
964 | if (!ngx_http_dyups_api_enable) {
965 | ngx_str_set(rv, "API disabled\n");
966 | return NGX_HTTP_NOT_ALLOWED;
967 | }
968 |
969 | ngx_shmtx_lock(&shpool->mutex);
970 |
971 | status = ngx_dyups_sandbox_update(buf, rv);
972 | if (status != NGX_HTTP_OK) {
973 | goto finish;
974 | }
975 |
976 | status = ngx_dyups_do_update(name, buf, rv);
977 | if (status == NGX_HTTP_OK) {
978 | if (ngx_dyups_shm_update_ups(name, buf)) {
979 | ngx_str_set(rv, "alert: update success but save to shm failed\n");
980 | status = NGX_HTTP_INTERNAL_SERVER_ERROR;
981 | }
982 | }
983 |
984 | finish:
985 |
986 | ngx_shmtx_unlock(&shpool->mutex);
987 |
988 | return status;
989 | }
990 |
991 |
992 | static ngx_int_t
993 | ngx_dyups_do_update(ngx_str_t *name, ngx_buf_t *buf, ngx_str_t *rv)
994 | {
995 | ngx_int_t rc, idx;
996 | ngx_http_dyups_srv_conf_t *duscf;
997 | ngx_http_dyups_main_conf_t *dumcf;
998 | ngx_http_upstream_srv_conf_t **uscfp;
999 | ngx_http_upstream_main_conf_t *umcf;
1000 |
1001 | umcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,
1002 | ngx_http_upstream_module);
1003 | dumcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,
1004 | ngx_http_dyups_module);
1005 |
1006 | duscf = ngx_dyups_find_upstream(name, &idx);
1007 | if (duscf) {
1008 | ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,
1009 | "[dyups] upstream reuse, idx: [%i]", idx);
1010 |
1011 | if (!duscf->deleted) {
1012 | ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,
1013 | "[dyups] upstream delete first");
1014 | ngx_dyups_mark_upstream_delete(duscf);
1015 |
1016 | duscf = ngx_dyups_find_upstream(name, &idx);
1017 |
1018 | ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,
1019 | "[dyups] find another, idx: [%i]", idx);
1020 | }
1021 | }
1022 |
1023 | if (idx == -1) {
1024 | /* need create a new upstream */
1025 |
1026 | ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
1027 | "[dyups] create upstream %V", name);
1028 |
1029 | duscf = ngx_array_push(&dumcf->dy_upstreams);
1030 | if (duscf == NULL) {
1031 | ngx_str_set(rv, "out of memory");
1032 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
1033 | }
1034 |
1035 | uscfp = ngx_array_push(&umcf->upstreams);
1036 | if (uscfp == NULL) {
1037 | ngx_str_set(rv, "out of memory");
1038 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
1039 | }
1040 |
1041 | ngx_memzero(duscf, sizeof(ngx_http_dyups_srv_conf_t));
1042 | idx = umcf->upstreams.nelts - 1;
1043 | }
1044 |
1045 | duscf->idx = idx;
1046 | rc = ngx_dyups_init_upstream(duscf, name, idx);
1047 |
1048 | if (rc != NGX_OK) {
1049 | ngx_str_set(rv, "init upstream failed");
1050 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
1051 | }
1052 |
1053 | /* init upstream */
1054 | rc = ngx_dyups_add_server(duscf, buf);
1055 | if (rc != NGX_OK) {
1056 | ngx_str_set(rv, "add server failed");
1057 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
1058 | }
1059 |
1060 | ngx_str_set(rv, "success");
1061 |
1062 | return NGX_HTTP_OK;
1063 | }
1064 |
1065 |
1066 | static ngx_int_t
1067 | ngx_dyups_sandbox_update(ngx_buf_t *buf, ngx_str_t *rv)
1068 | {
1069 | ngx_int_t rc;
1070 | ngx_str_t dumy;
1071 |
1072 | ngx_str_t sandbox = ngx_string("_dyups_upstream_sandbox_");
1073 |
1074 | rc = ngx_dyups_do_update(&sandbox, buf, rv);
1075 |
1076 | (void) ngx_dyups_do_delete(&sandbox, &dumy);
1077 |
1078 | return rc;
1079 | }
1080 |
1081 |
1082 | static char *
1083 | ngx_dyups_parse_upstream(ngx_conf_t *cf, ngx_buf_t *buf)
1084 | {
1085 | char *rc;
1086 | ngx_buf_t b;
1087 | ngx_str_t s;
1088 | ngx_uint_t i;
1089 | ngx_hash_t vh, vh_prev;
1090 | ngx_array_t va, va_prev;
1091 | ngx_conf_file_t conf_file;
1092 | ngx_http_variable_t *v;
1093 | ngx_hash_keys_arrays_t vk;
1094 | ngx_http_core_main_conf_t *cmcf;
1095 |
1096 | b = *buf; /* avoid modifying @buf */
1097 |
1098 | ngx_memzero(&conf_file, sizeof(ngx_conf_file_t));
1099 | conf_file.file.fd = NGX_INVALID_FILE;
1100 | conf_file.buffer = &b;
1101 |
1102 | cf->conf_file = &conf_file;
1103 |
1104 | rc = ngx_conf_parse(cf, NULL);
1105 | if (rc != NGX_CONF_OK) {
1106 | return rc;
1107 | }
1108 |
1109 | cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
1110 |
1111 | va_prev = cmcf->variables;
1112 | vh_prev = cmcf->variables_hash;
1113 |
1114 | ngx_memzero(&va, sizeof(va));
1115 | ngx_memzero(&vh, sizeof(vh));
1116 | ngx_memzero(&vk, sizeof(vk));
1117 |
1118 | cmcf->variables = va;
1119 | cmcf->variables_hash = vh;
1120 | cmcf->variables_keys = &vk;
1121 |
1122 | v = va_prev.elts;
1123 | for (i = 0; i < va_prev.nelts; i++) {
1124 |
1125 | if (v[i].get_handler) {
1126 | continue;
1127 | }
1128 |
1129 | s.len = v[i].name.len;
1130 | s.data = ngx_pstrdup(ngx_cycle->pool, &v[i].name);
1131 | if (!s.data) {
1132 | rc = NGX_CONF_ERROR;
1133 | break;
1134 | }
1135 |
1136 | /*
1137 | * variable name will be assign to cmcf->variables[idx].name directly
1138 | * so the lifetime of v[i].name should be the same as cmcf
1139 | */
1140 | v[i].name = s;
1141 |
1142 | cmcf->variables.elts = &v[i];
1143 | cmcf->variables.nelts = 1;
1144 | if (ngx_http_variables_init_vars(cf) != NGX_OK) {
1145 | rc = NGX_CONF_ERROR;
1146 | break;
1147 | }
1148 | }
1149 |
1150 | cmcf->variables = va_prev;
1151 | cmcf->variables_hash = vh_prev;
1152 | cmcf->variables_keys = NULL;
1153 |
1154 | return rc;
1155 | }
1156 |
1157 |
1158 | static ngx_int_t
1159 | ngx_dyups_add_server(ngx_http_dyups_srv_conf_t *duscf, ngx_buf_t *buf)
1160 | {
1161 | ngx_conf_t cf;
1162 | ngx_http_upstream_init_pt init;
1163 | ngx_http_upstream_srv_conf_t *uscf;
1164 | ngx_http_dyups_upstream_srv_conf_t *dscf;
1165 |
1166 | uscf = duscf->upstream;
1167 |
1168 | if (uscf->servers == NULL) {
1169 | uscf->servers = ngx_array_create(duscf->pool, 4,
1170 | sizeof(ngx_http_upstream_server_t));
1171 | if (uscf->servers == NULL) {
1172 | return NGX_ERROR;
1173 | }
1174 | }
1175 |
1176 | ngx_memzero(&cf, sizeof(ngx_conf_t));
1177 | cf.name = "dyups_init_module_conf";
1178 | cf.pool = duscf->pool;
1179 | cf.cycle = (ngx_cycle_t *) ngx_cycle;
1180 | cf.module_type = NGX_HTTP_MODULE;
1181 | cf.cmd_type = NGX_HTTP_UPS_CONF;
1182 | cf.log = ngx_cycle->log;
1183 | cf.ctx = duscf->ctx;
1184 | cf.args = ngx_array_create(duscf->pool, 10, sizeof(ngx_str_t));
1185 | if (cf.args == NULL) {
1186 | return NGX_ERROR;
1187 | }
1188 |
1189 | if (ngx_dyups_parse_upstream(&cf, buf) != NGX_CONF_OK) {
1190 | return NGX_ERROR;
1191 | }
1192 |
1193 | ngx_memzero(&cf, sizeof(ngx_conf_t));
1194 | cf.name = "dyups_init_upstream";
1195 | cf.cycle = (ngx_cycle_t *) ngx_cycle;
1196 | cf.pool = duscf->pool;
1197 | cf.module_type = NGX_HTTP_MODULE;
1198 | cf.cmd_type = NGX_HTTP_MAIN_CONF;
1199 | cf.log = ngx_cycle->log;
1200 | cf.ctx = duscf->ctx;
1201 |
1202 | init = uscf->peer.init_upstream ? uscf->peer.init_upstream:
1203 | ngx_http_upstream_init_round_robin;
1204 |
1205 | if (init(&cf, uscf) != NGX_OK) {
1206 | return NGX_ERROR;
1207 | }
1208 |
1209 | dscf = uscf->srv_conf[ngx_http_dyups_module.ctx_index];
1210 | dscf->init = uscf->peer.init;
1211 |
1212 | uscf->peer.init = ngx_http_dyups_init_peer;
1213 |
1214 | return NGX_OK;
1215 | }
1216 |
1217 |
1218 | static ngx_http_dyups_srv_conf_t *
1219 | ngx_dyups_find_upstream(ngx_str_t *name, ngx_int_t *idx)
1220 | {
1221 | ngx_uint_t i;
1222 | ngx_http_dyups_srv_conf_t *duscfs, *duscf, *duscf_del;
1223 | ngx_http_dyups_main_conf_t *dumcf;
1224 | ngx_http_upstream_srv_conf_t *uscf;
1225 |
1226 | dumcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,
1227 | ngx_http_dyups_module);
1228 | *idx = -1;
1229 | duscf_del = NULL;
1230 |
1231 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1232 | "[dyups] find dynamic upstream");
1233 |
1234 | duscfs = dumcf->dy_upstreams.elts;
1235 | for (i = 0; i < dumcf->dy_upstreams.nelts; i++) {
1236 |
1237 | duscf = &duscfs[i];
1238 | if (!duscf->dynamic) {
1239 | continue;
1240 | }
1241 |
1242 | if (duscf->deleted == NGX_DYUPS_DELETING) {
1243 |
1244 | ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,
1245 | "[dyups] find upstream idx: %ui ref: %ui "
1246 | "on %V deleting",
1247 | i, *(duscf->ref), &duscf->upstream->host);
1248 |
1249 | if (*(duscf->ref) == 0) {
1250 | ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
1251 | "[dyups] free dynamic upstream in find upstream"
1252 | " %ui", duscf->idx);
1253 |
1254 | duscf->deleted = NGX_DYUPS_DELETED;
1255 |
1256 | if (duscf->pool) {
1257 | ngx_destroy_pool(duscf->pool);
1258 | duscf->pool = NULL;
1259 | }
1260 | }
1261 | }
1262 |
1263 | if (duscf->deleted == NGX_DYUPS_DELETING) {
1264 | continue;
1265 | }
1266 |
1267 | if (duscf->deleted == NGX_DYUPS_DELETED) {
1268 | *idx = i;
1269 | duscf_del = duscf;
1270 | continue;
1271 | }
1272 |
1273 | uscf = duscf->upstream;
1274 |
1275 | if (uscf->host.len != name->len
1276 | || ngx_strncasecmp(uscf->host.data, name->data, uscf->host.len)
1277 | != 0)
1278 | {
1279 | continue;
1280 | }
1281 |
1282 | *idx = i;
1283 |
1284 | return duscf;
1285 | }
1286 |
1287 | return duscf_del;
1288 | }
1289 |
1290 |
1291 | static ngx_int_t
1292 | ngx_dyups_init_upstream(ngx_http_dyups_srv_conf_t *duscf, ngx_str_t *name,
1293 | ngx_uint_t index)
1294 | {
1295 | ngx_uint_t mi, m;
1296 | ngx_conf_t cf;
1297 | ngx_module_t **modules;
1298 | ngx_http_module_t *module;
1299 | ngx_http_conf_ctx_t *ctx;
1300 | ngx_http_upstream_srv_conf_t *uscf, **uscfp;
1301 | ngx_http_upstream_main_conf_t *umcf;
1302 | ngx_http_dyups_upstream_srv_conf_t *dscf;
1303 |
1304 | umcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,
1305 | ngx_http_upstream_module);
1306 | uscfp = umcf->upstreams.elts;
1307 |
1308 | duscf->pool = ngx_create_pool(512, ngx_cycle->log);
1309 | if (duscf->pool == NULL) {
1310 | return NGX_ERROR;
1311 | }
1312 |
1313 | uscf = ngx_pcalloc(duscf->pool, sizeof(ngx_http_upstream_srv_conf_t));
1314 | if (uscf == NULL) {
1315 | return NGX_ERROR;
1316 | }
1317 |
1318 | uscf->flags = NGX_HTTP_UPSTREAM_CREATE
1319 | |NGX_HTTP_UPSTREAM_WEIGHT
1320 | #ifdef NGX_HTTP_UPSTREAM_MAX_CONNS
1321 | |NGX_HTTP_UPSTREAM_MAX_CONNS
1322 | #endif
1323 | |NGX_HTTP_UPSTREAM_MAX_FAILS
1324 | |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
1325 | |NGX_HTTP_UPSTREAM_DOWN
1326 | |NGX_HTTP_UPSTREAM_BACKUP;
1327 |
1328 | uscf->host.data = ngx_pstrdup(duscf->pool, name);
1329 | if (uscf->host.data == NULL) {
1330 | return NGX_ERROR;
1331 | }
1332 |
1333 | uscf->host.len = name->len;
1334 | uscf->file_name = (u_char *) "dynamic_upstream";
1335 | uscf->line = 0;
1336 | uscf->port = 0;
1337 | #if nginx_version < 1011006
1338 | uscf->default_port = 0;
1339 | #endif
1340 | uscfp[index] = uscf;
1341 |
1342 | duscf->dynamic = 1;
1343 | duscf->upstream = uscf;
1344 |
1345 | ngx_memzero(&cf, sizeof(ngx_conf_t));
1346 | cf.module_type = NGX_HTTP_MODULE;
1347 | cf.cmd_type = NGX_HTTP_MAIN_CONF;
1348 | cf.pool = duscf->pool;
1349 | cf.ctx = ngx_cycle->conf_ctx[ngx_http_module.index];
1350 | cf.cycle = (ngx_cycle_t *) ngx_cycle;
1351 |
1352 | ctx = ngx_pcalloc(duscf->pool, sizeof(ngx_http_conf_ctx_t));
1353 | if (ctx == NULL) {
1354 | return NGX_ERROR;
1355 | }
1356 |
1357 | ctx->main_conf = ((ngx_http_conf_ctx_t *)
1358 | ngx_cycle->conf_ctx[ngx_http_module.index])->main_conf;
1359 |
1360 | ctx->srv_conf = ngx_pcalloc(cf.pool, sizeof(void *) * ngx_http_max_module);
1361 | if (ctx->srv_conf == NULL) {
1362 | return NGX_ERROR;
1363 | }
1364 |
1365 | ctx->srv_conf[ngx_http_upstream_module.ctx_index] = uscf;
1366 | uscf->srv_conf = ctx->srv_conf;
1367 |
1368 | #if nginx_version >= 1009011
1369 | modules = ngx_cycle->modules;
1370 | #else
1371 | modules = ngx_modules;
1372 | #endif
1373 |
1374 | for (m = 0; modules[m]; m++) {
1375 | if (modules[m]->type != NGX_HTTP_MODULE) {
1376 | continue;
1377 | }
1378 |
1379 | if (modules[m]->index == ngx_http_core_module.index) {
1380 | continue;
1381 | }
1382 |
1383 | module = modules[m]->ctx;
1384 | mi = modules[m]->ctx_index;
1385 |
1386 | if (module->create_srv_conf) {
1387 | ctx->srv_conf[mi] = module->create_srv_conf(&cf);
1388 | if (ctx->srv_conf[mi] == NULL) {
1389 | return NGX_ERROR;
1390 | }
1391 | }
1392 | }
1393 |
1394 | dscf = uscf->srv_conf[ngx_http_dyups_module.ctx_index];
1395 | duscf->ref = &dscf->ref;
1396 | duscf->ctx = ctx;
1397 | duscf->deleted = 0;
1398 |
1399 | ngx_dyups_add_upstream_top_filter(umcf, uscf);
1400 |
1401 | return NGX_OK;
1402 | }
1403 |
1404 |
1405 | static void
1406 | ngx_dyups_mark_upstream_delete(ngx_http_dyups_srv_conf_t *duscf)
1407 | {
1408 | ngx_uint_t i;
1409 | ngx_http_upstream_server_t *us;
1410 | ngx_http_upstream_srv_conf_t *uscf, **uscfp;
1411 | ngx_http_upstream_main_conf_t *umcf;
1412 |
1413 | uscf = duscf->upstream;
1414 | umcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,
1415 | ngx_http_upstream_module);
1416 | uscfp = umcf->upstreams.elts;
1417 |
1418 | ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
1419 | "[dyups] delete upstream \"%V\"", &duscf->upstream->host);
1420 |
1421 | ngx_dyups_del_upstream_top_filter(umcf, uscf);
1422 |
1423 | us = uscf->servers->elts;
1424 | for (i = 0; i < uscf->servers->nelts; i++) {
1425 | us[i].down = 1;
1426 | }
1427 |
1428 | uscfp[duscf->idx] = &ngx_http_dyups_deleted_upstream;
1429 | duscf->deleted = NGX_DYUPS_DELETING;
1430 | }
1431 |
1432 |
1433 | static void
1434 | ngx_http_dyups_send_response(ngx_http_request_t *r, ngx_int_t status,
1435 | ngx_str_t *content)
1436 | {
1437 | ngx_int_t rc;
1438 | ngx_buf_t *b;
1439 | ngx_chain_t out;
1440 |
1441 | r->headers_out.status = status;
1442 | r->headers_out.content_length_n = content->len;
1443 |
1444 | rc = ngx_http_send_header(r);
1445 | if (rc == NGX_ERROR || rc > NGX_OK) {
1446 | ngx_http_finalize_request(r, rc);
1447 | return;
1448 | }
1449 |
1450 | if (content->len == 0) {
1451 | ngx_http_finalize_request(r, ngx_http_send_special(r, NGX_HTTP_FLUSH));
1452 | return;
1453 | }
1454 |
1455 | b = ngx_create_temp_buf(r->pool, content->len);
1456 | if (b == NULL) {
1457 | ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
1458 | return;
1459 | }
1460 |
1461 | b->pos = content->data;
1462 | b->last = content->data + content->len;
1463 | b->last_buf = 1;
1464 |
1465 | out.buf = b;
1466 | out.next = NULL;
1467 |
1468 | ngx_http_finalize_request(r, ngx_http_output_filter(r, &out));
1469 | }
1470 |
1471 |
1472 | static ngx_buf_t *
1473 | ngx_http_dyups_read_body(ngx_http_request_t *r)
1474 | {
1475 | size_t len;
1476 | ngx_buf_t *buf, *next, *body;
1477 | ngx_chain_t *cl;
1478 |
1479 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1480 | "[dyups] interface read post body");
1481 |
1482 | cl = r->request_body->bufs;
1483 | buf = cl->buf;
1484 |
1485 | if (cl->next == NULL) {
1486 |
1487 | return buf;
1488 |
1489 | } else {
1490 |
1491 | next = cl->next->buf;
1492 | len = (buf->last - buf->pos) + (next->last - next->pos);
1493 |
1494 | body = ngx_create_temp_buf(r->pool, len);
1495 | if (body == NULL) {
1496 | return NULL;
1497 | }
1498 |
1499 | body->last = ngx_cpymem(body->last, buf->pos, buf->last - buf->pos);
1500 | body->last = ngx_cpymem(body->last, next->pos, next->last - next->pos);
1501 | }
1502 |
1503 | return body;
1504 | }
1505 |
1506 |
1507 | static ngx_buf_t *
1508 | ngx_http_dyups_read_body_from_file(ngx_http_request_t *r)
1509 | {
1510 | size_t len;
1511 | ssize_t size;
1512 | ngx_buf_t *buf, *body;
1513 | ngx_chain_t *cl;
1514 |
1515 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1516 | "[dyups] interface read post body from file");
1517 |
1518 | len = 0;
1519 | cl = r->request_body->bufs;
1520 |
1521 | while (cl) {
1522 |
1523 | buf = cl->buf;
1524 |
1525 | if (buf->in_file) {
1526 | len += buf->file_last - buf->file_pos;
1527 |
1528 | } else {
1529 | len += buf->last - buf->pos;
1530 | }
1531 |
1532 | cl = cl->next;
1533 | }
1534 |
1535 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1536 | "[dyups] interface read post body file size %ui", len);
1537 |
1538 | body = ngx_create_temp_buf(r->pool, len);
1539 | if (body == NULL) {
1540 | return NULL;
1541 | }
1542 |
1543 | cl = r->request_body->bufs;
1544 |
1545 | while (cl) {
1546 |
1547 | buf = cl->buf;
1548 |
1549 | if (buf->in_file) {
1550 |
1551 | size = ngx_read_file(buf->file, body->last,
1552 | buf->file_last - buf->file_pos, buf->file_pos);
1553 |
1554 | if (size == NGX_ERROR) {
1555 | return NULL;
1556 | }
1557 |
1558 | body->last += size;
1559 |
1560 | } else {
1561 |
1562 | body->last = ngx_cpymem(body->last, buf->pos, buf->last - buf->pos);
1563 | }
1564 |
1565 | cl = cl->next;
1566 | }
1567 |
1568 | return body;
1569 | }
1570 |
1571 |
1572 | ngx_array_t *
1573 | ngx_dyups_parse_path(ngx_pool_t *pool, ngx_str_t *path)
1574 | {
1575 | u_char *p, *last, *end;
1576 | ngx_str_t *str;
1577 | ngx_array_t *array;
1578 |
1579 | array = ngx_array_create(pool, 8, sizeof(ngx_str_t));
1580 | if (array == NULL) {
1581 | return NULL;
1582 | }
1583 |
1584 | p = path->data + 1;
1585 | last = path->data + path->len;
1586 |
1587 | while(p < last) {
1588 | end = ngx_strlchr(p, last, '/');
1589 | str = ngx_array_push(array);
1590 |
1591 | if (str == NULL) {
1592 | return NULL;
1593 | }
1594 |
1595 | if (end) {
1596 | str->data = p;
1597 | str->len = end - p;
1598 |
1599 | } else {
1600 | str->data = p;
1601 | str->len = last - p;
1602 |
1603 | }
1604 |
1605 | p += str->len + 1;
1606 | }
1607 |
1608 | #if (NGX_DEBUG)
1609 | ngx_str_t *arg;
1610 | ngx_uint_t i;
1611 |
1612 | arg = array->elts;
1613 | for (i = 0; i < array->nelts; i++) {
1614 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1615 | "[dyups] res[%i]:%V", i, &arg[i]);
1616 | }
1617 | #endif
1618 |
1619 | return array;
1620 | }
1621 |
1622 |
1623 | static ngx_int_t
1624 | ngx_http_dyups_init_peer(ngx_http_request_t *r,
1625 | ngx_http_upstream_srv_conf_t *us)
1626 | {
1627 | ngx_int_t rc;
1628 | ngx_pool_cleanup_t *cln;
1629 | ngx_http_dyups_ctx_t *ctx;
1630 | ngx_http_dyups_upstream_srv_conf_t *dscf;
1631 |
1632 | dscf = us->srv_conf[ngx_http_dyups_module.ctx_index];
1633 |
1634 | rc = dscf->init(r, us);
1635 |
1636 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1637 | "[dyups] dynamic upstream init peer: %i",
1638 | rc);
1639 |
1640 | if (rc != NGX_OK) {
1641 | return rc;
1642 | }
1643 |
1644 | ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_dyups_ctx_t));
1645 | if (ctx == NULL) {
1646 | return NGX_ERROR;
1647 | }
1648 |
1649 | ctx->scf = dscf;
1650 | ctx->data = r->upstream->peer.data;
1651 | ctx->get = r->upstream->peer.get;
1652 | ctx->free = r->upstream->peer.free;
1653 |
1654 | r->upstream->peer.data = ctx;
1655 | r->upstream->peer.get = ngx_http_dyups_get_peer;
1656 | r->upstream->peer.free = ngx_http_dyups_free_peer;
1657 |
1658 | #if (NGX_HTTP_SSL)
1659 | r->upstream->peer.set_session = ngx_http_dyups_set_peer_session;
1660 | r->upstream->peer.save_session = ngx_http_dyups_save_peer_session;
1661 | #endif
1662 |
1663 | cln = ngx_pool_cleanup_add(r->pool, 0);
1664 | if (cln == NULL) {
1665 | return NGX_ERROR;
1666 | }
1667 |
1668 | dscf->ref++;
1669 |
1670 | cln->handler = ngx_http_dyups_clean_request;
1671 | cln->data = &dscf->ref;
1672 |
1673 | return NGX_OK;
1674 | }
1675 |
1676 |
1677 | static ngx_int_t
1678 | ngx_http_dyups_get_peer(ngx_peer_connection_t *pc, void *data)
1679 | {
1680 | ngx_http_dyups_ctx_t *ctx = data;
1681 |
1682 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1683 | "[dyups] dynamic upstream get handler count %i",
1684 | ctx->scf->ref);
1685 |
1686 | return ctx->get(pc, ctx->data);
1687 | }
1688 |
1689 |
1690 | static void
1691 | ngx_http_dyups_free_peer(ngx_peer_connection_t *pc, void *data,
1692 | ngx_uint_t state)
1693 | {
1694 | ngx_http_dyups_ctx_t *ctx = data;
1695 |
1696 | ngx_pool_cleanup_t *cln;
1697 |
1698 |
1699 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1700 | "[dyups] dynamic upstream free handler count %i",
1701 | ctx->scf->ref);
1702 |
1703 | /* upstream connect failed */
1704 | if (pc->connection == NULL) {
1705 | goto done;
1706 | }
1707 |
1708 | if (pc->cached) {
1709 | goto done;
1710 | }
1711 |
1712 | ctx->scf->ref++;
1713 |
1714 | cln = ngx_pool_cleanup_add(pc->connection->pool, 0);
1715 | if (cln == NULL) {
1716 | ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
1717 | "[dyups] dynamic upstream free peer may cause memleak %i",
1718 | ctx->scf->ref);
1719 | goto done;
1720 | }
1721 |
1722 | cln->handler = ngx_http_dyups_clean_request;
1723 | cln->data = &ctx->scf->ref;
1724 |
1725 | done:
1726 |
1727 | ctx->free(pc, ctx->data, state);
1728 | }
1729 |
1730 |
1731 | static void
1732 | ngx_http_dyups_clean_request(void *data)
1733 | {
1734 | ngx_uint_t *ref = data;
1735 |
1736 | (*ref)--;
1737 |
1738 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1739 | "[dyups] http clean request count %i", *ref);
1740 | }
1741 |
1742 |
1743 | static ngx_int_t
1744 | ngx_dyups_shm_delete_ups(ngx_str_t *name)
1745 | {
1746 | uint32_t hash;
1747 | ngx_slab_pool_t *shpool;
1748 | ngx_dyups_shctx_t *sh;
1749 | ngx_dyups_upstream_t *ups;
1750 |
1751 | sh = ngx_dyups_global_ctx.sh;
1752 | shpool = ngx_dyups_global_ctx.shpool;
1753 |
1754 | hash = ngx_crc32_short(name->data, name->len);
1755 | ups = (ngx_dyups_upstream_t *)
1756 | ngx_str_rbtree_lookup(&sh->rbtree, name, hash);
1757 |
1758 | if (ups) {
1759 | sh->version++;
1760 | ngx_rbtree_delete(&sh->rbtree, &ups->node);
1761 | ngx_dyups_shm_free_ups(shpool, ups);
1762 | }
1763 |
1764 | return NGX_OK;
1765 | }
1766 |
1767 |
1768 | static ngx_int_t
1769 | ngx_dyups_shm_update_ups(ngx_str_t *name, ngx_buf_t *body)
1770 | {
1771 | ngx_slab_pool_t *shpool;
1772 | ngx_dyups_shctx_t *sh;
1773 | ngx_dyups_upstream_t *ups;
1774 |
1775 | sh = ngx_dyups_global_ctx.sh;
1776 | shpool = ngx_dyups_global_ctx.shpool;
1777 |
1778 | ups = ngx_slab_alloc_locked(shpool, sizeof(ngx_dyups_upstream_t));
1779 | if (ups == NULL) {
1780 | goto failed;
1781 | }
1782 |
1783 | ngx_memzero(ups, sizeof(ngx_dyups_upstream_t));
1784 |
1785 | ups->name.data = ngx_slab_alloc_locked(shpool, name->len);
1786 | if (ups->name.data == NULL) {
1787 | goto failed;
1788 | }
1789 |
1790 | ngx_memcpy(ups->name.data, name->data, name->len);
1791 | ups->name.len = name->len;
1792 |
1793 | if (body) {
1794 | ups->content.data = ngx_slab_alloc_locked(shpool,
1795 | body->last - body->pos);
1796 | if (ups->content.data == NULL) {
1797 | goto failed;
1798 | }
1799 |
1800 | ngx_memcpy(ups->content.data, body->pos, body->last - body->pos);
1801 | ups->content.len = body->last - body->pos;
1802 |
1803 | } else {
1804 | ups->content.data = NULL;
1805 | ups->content.len = 0;
1806 | }
1807 |
1808 | sh->version++;
1809 |
1810 | if (sh->version == 0) {
1811 | sh->version = 1;
1812 | }
1813 |
1814 | ngx_dyups_shm_delete_ups(&ups->name);
1815 |
1816 | ups->node.key = ngx_crc32_short(ups->name.data, ups->name.len);
1817 | ngx_rbtree_insert(&sh->rbtree, &ups->node);
1818 |
1819 | ups = (ngx_dyups_upstream_t *)
1820 | ngx_str_rbtree_lookup(&sh->rbtree, &ups->name, ups->node.key);
1821 |
1822 | ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
1823 | "[dyups] update %V, version: %ui",
1824 | &ups->name, sh->version);
1825 |
1826 | return NGX_OK;
1827 |
1828 | failed:
1829 |
1830 | if (ups) {
1831 | ngx_dyups_shm_free_ups(shpool, ups);
1832 | }
1833 |
1834 | return NGX_ERROR;
1835 | }
1836 |
1837 |
1838 | static void
1839 | ngx_dyups_shm_free_ups(ngx_slab_pool_t *shpool, ngx_dyups_upstream_t *ups)
1840 | {
1841 | if (ups->name.data) {
1842 | ngx_slab_free_locked(shpool, ups->name.data);
1843 | }
1844 |
1845 | if (ups->content.data) {
1846 | ngx_slab_free_locked(shpool, ups->content.data);
1847 | }
1848 |
1849 | ngx_slab_free_locked(shpool, ups);
1850 | }
1851 |
1852 | static ngx_int_t
1853 | ngx_http_variable_dyups(ngx_http_request_t *r, ngx_http_variable_value_t *v,
1854 | uintptr_t data)
1855 | {
1856 | ngx_str_t *name = (ngx_str_t *) data;
1857 |
1858 | size_t len;
1859 | u_char *low, *p;
1860 | uint32_t hash;
1861 | ngx_int_t rc;
1862 | ngx_str_t key, rv, uname;
1863 | ngx_slab_pool_t *shpool;
1864 | ngx_dyups_shctx_t *sh;
1865 | ngx_dyups_upstream_t *ups;
1866 | ngx_http_variable_value_t *vv;
1867 |
1868 | len = name->len - (sizeof("dyups_") - 1);
1869 | p = name->data + sizeof("dyups_") - 1;
1870 |
1871 | low = ngx_pnalloc(r->pool, len);
1872 | if (low == NULL) {
1873 | return NGX_ERROR;
1874 | }
1875 |
1876 | hash = ngx_hash_strlow(low, p, len);
1877 |
1878 | key.len = len;
1879 | key.data = low;
1880 |
1881 | vv = ngx_http_get_variable(r, &key, hash);
1882 |
1883 | v->data = vv->data;
1884 | v->len = vv->len;
1885 | v->valid = 1;
1886 | v->no_cacheable = 0;
1887 | v->not_found = 0;
1888 |
1889 | uname.data = v->data;
1890 | uname.len = v->len;
1891 |
1892 | sh = ngx_dyups_global_ctx.sh;
1893 | if (sh->version == ngx_dyups_global_ctx.version) {
1894 | return NGX_OK;
1895 | }
1896 |
1897 | shpool = ngx_dyups_global_ctx.shpool;
1898 | ngx_shmtx_lock(&shpool->mutex);
1899 |
1900 | ngx_http_dyups_reload();
1901 |
1902 | hash = ngx_crc32_short(uname.data, uname.len);
1903 | ups = (ngx_dyups_upstream_t *)
1904 | ngx_str_rbtree_lookup(&sh->rbtree, &uname, hash);
1905 |
1906 | if (!ups) {
1907 | rc = ngx_dyups_do_delete(&uname, &rv);
1908 | ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
1909 | "[dyups] sync del: %V rv: %V rc: %i",
1910 | &uname, &rv, rc);
1911 | }
1912 |
1913 | ngx_shmtx_unlock(&shpool->mutex);
1914 |
1915 | return NGX_OK;
1916 | }
1917 |
1918 |
1919 | #if (NGX_HTTP_SSL)
1920 |
1921 | static ngx_int_t
1922 | ngx_http_dyups_set_peer_session(ngx_peer_connection_t *pc, void *data)
1923 | {
1924 | ngx_http_dyups_ctx_t *ctx = data;
1925 |
1926 | ngx_int_t rc;
1927 | ngx_ssl_session_t *ssl_session;
1928 |
1929 | ssl_session = ctx->ssl_session;
1930 | rc = ngx_ssl_set_session(pc->connection, ssl_session);
1931 |
1932 | #if OPENSSL_VERSION_NUMBER >= 0x10100003L
1933 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
1934 | "set session: %p", ssl_session);
1935 |
1936 | #else
1937 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
1938 | "set session: %p:%d", ssl_session,
1939 | ssl_session ? ssl_session->references : 0);
1940 | #endif
1941 |
1942 | return rc;
1943 | }
1944 |
1945 |
1946 | static void
1947 | ngx_http_dyups_save_peer_session(ngx_peer_connection_t *pc, void *data)
1948 | {
1949 | ngx_http_dyups_ctx_t *ctx = data;
1950 |
1951 | ngx_ssl_session_t *old_ssl_session, *ssl_session;
1952 |
1953 | ssl_session = ngx_ssl_get_session(pc->connection);
1954 |
1955 | if (ssl_session == NULL) {
1956 | return;
1957 | }
1958 |
1959 | #if OPENSSL_VERSION_NUMBER >= 0x10100003L
1960 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
1961 | "save session: %p", ssl_session);
1962 |
1963 | #else
1964 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
1965 | "save session: %p:%d", ssl_session,
1966 | ssl_session->references);
1967 | #endif
1968 |
1969 | old_ssl_session = ctx->ssl_session;
1970 | ctx->ssl_session = ssl_session;
1971 |
1972 | if (old_ssl_session) {
1973 |
1974 | #if OPENSSL_VERSION_NUMBER >= 0x10100003L
1975 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
1976 | "old session: %p", old_ssl_session);
1977 |
1978 | #else
1979 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
1980 | "old session: %p:%d", old_ssl_session,
1981 | old_ssl_session->references);
1982 | #endif
1983 |
1984 | ngx_ssl_free_session(old_ssl_session);
1985 | }
1986 | }
1987 |
1988 | #endif
1989 |
1990 |
1991 | static ngx_int_t
1992 | ngx_dyups_add_upstream_filter(ngx_http_upstream_main_conf_t *umcf,
1993 | ngx_http_upstream_srv_conf_t *uscf)
1994 | {
1995 | #if (NGX_HTTP_UPSTREAM_RBTREE)
1996 | uscf->node.key = ngx_crc32_short(uscf->host.data, uscf->host.len);
1997 | ngx_rbtree_insert(&umcf->rbtree, &uscf->node);
1998 | #endif
1999 |
2000 | return NGX_OK;
2001 | }
2002 |
2003 |
2004 | static ngx_int_t
2005 | ngx_dyups_del_upstream_filter(ngx_http_upstream_main_conf_t *umcf,
2006 | ngx_http_upstream_srv_conf_t *uscf)
2007 | {
2008 | #if (NGX_HTTP_UPSTREAM_RBTREE)
2009 | ngx_rbtree_delete(&umcf->rbtree, &uscf->node);
2010 | #endif
2011 | return NGX_OK;
2012 | }
2013 |
--------------------------------------------------------------------------------
/t/addhosts.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | for i in `seq 10000`
4 | do
5 | echo "curl -i -d 'server 127.0.0.1:8088;' 127.0.0.1:8081/upstream/dyhost$i";
6 | curl -i -d "server 127.0.0.1:8088;" 127.0.0.1:8081/upstream/dyhost$i;echo "\n";
7 | done
8 |
--------------------------------------------------------------------------------
/t/curlhosts.sh:
--------------------------------------------------------------------------------
1 | for i in `seq 10000`
2 | do
3 | echo "curl -i -H "Host" 127.0.0.1:8081/upstream/dyhost"$i;
4 | curl -i -H "Host: dyhost$i" 127.0.0.1:8080;echo "\n";
5 | done
6 |
--------------------------------------------------------------------------------
/t/delhosts.sh:
--------------------------------------------------------------------------------
1 | for i in `seq 10000`
2 | do
3 | echo "curl -i -X DELETE 127.0.0.1:8081/upstream/dyhost$i";
4 | curl -i -X DELETE 127.0.0.1:8081/upstream/dyhost$i;echo "\n";
5 | done
6 |
--------------------------------------------------------------------------------
/t/dyups.t:
--------------------------------------------------------------------------------
1 | use warnings;
2 | use strict;
3 |
4 | use Test::More;
5 |
6 | BEGIN { use FindBin; chdir($FindBin::Bin); }
7 |
8 |
9 | use lib 'lib';
10 | use File::Path;
11 | use Test::Nginx;
12 |
13 |
14 | my $NGINX = defined $ENV{TEST_NGINX_BINARY} ? $ENV{TEST_NGINX_BINARY}
15 | : '../nginx/objs/nginx';
16 | my $t = Test::Nginx->new()->plan(76);
17 |
18 | sub mhttp_get($;$;$;%) {
19 | my ($url, $host, $port, %extra) = @_;
20 | return mhttp(<{_testdir};
54 |
55 | if (defined $conf) {
56 | my $c = `cat $conf`;
57 | $self->write_file_expand('nginx.conf', $c);
58 | }
59 |
60 | my $pid = fork();
61 | die "Unable to fork(): $!\n" unless defined $pid;
62 |
63 | if ($pid == 0) {
64 | my @globals = $self->{_test_globals} ?
65 | () : ('-g', "pid $testdir/nginx.pid; "
66 | . "error_log $testdir/error.log debug;");
67 | exec($NGINX, '-c', "$testdir/nginx.conf", '-p', "$testdir",
68 | @globals) or die "Unable to exec(): $!\n";
69 | }
70 |
71 | # wait for nginx to start
72 |
73 | $self->waitforfile("$testdir/nginx.pid")
74 | or die "Can't start nginx";
75 |
76 | $self->{_started} = 1;
77 | return $self;
78 | }
79 |
80 | ###############################################################################
81 |
82 | select STDERR; $| = 1;
83 | select STDOUT; $| = 1;
84 |
85 | warn "your test dir is ".$t->testdir();
86 |
87 | $t->write_file_expand('nginx.conf', <<'EOF');
88 |
89 | %%TEST_GLOBALS%%
90 |
91 | daemon off;
92 |
93 | worker_processes 1;
94 |
95 | events {
96 | accept_mutex off;
97 | }
98 |
99 | http {
100 |
101 | upstream host1 {
102 | server 127.0.0.1:8088;
103 | }
104 |
105 | upstream host2 {
106 | server 127.0.0.1:8089;
107 | }
108 |
109 | server {
110 | listen 8080;
111 |
112 | location / {
113 | proxy_pass http://$host;
114 | }
115 | }
116 |
117 | server {
118 | listen 8088;
119 | location / {
120 | return 200 "8088";
121 | }
122 | }
123 |
124 | server {
125 | listen unix:/tmp/dyupssocket;
126 | location / {
127 | return 200 "unix";
128 | }
129 | }
130 |
131 | server {
132 | listen 8089;
133 | location / {
134 | return 200 "8089";
135 | }
136 | }
137 |
138 | server {
139 | listen 8081;
140 | location / {
141 | dyups_interface;
142 | }
143 | }
144 | }
145 | EOF
146 |
147 | mrun($t);
148 |
149 | ###############################################################################
150 | my $rep;
151 | my $body;
152 |
153 | $rep = qr/
154 | host1
155 | host2
156 | /m;
157 | like(mhttp_get('/list', 'localhost', 8081), $rep, '2013-02-26 16:51:46');
158 | $rep = qr/
159 | host1
160 | server 127.0.0.1:8088 weight=1 .*
161 |
162 | host2
163 | server 127.0.0.1:8089 weight=1 .*
164 | /m;
165 | like(mhttp_get('/detail', 'localhost', 8081), $rep, '2013-02-26 17:30:07');
166 | like(mhttp_get('/upstream/host1', 'localhost', 8081), qr/server 127.0.0.1:8088/m, '2013-02-26 17:35:19');
167 |
168 | ###############################################################################
169 |
170 | like(mhttp_post('/upstream/dyhost', 'server 127.0.0.1:8088;', 8081), qr/success/m, '2013-02-26 16:51:51');
171 | like(mhttp_get('/', 'dyhost', 8080), qr/8088/m, '2013-02-26 16:51:54');
172 | like(mhttp_get('/', 'host1', 8080), qr/8088/m, '2013-02-26 17:36:42');
173 | like(mhttp_get('/', 'host2', 8080), qr/8089/m, '2013-02-26 17:36:46');
174 |
175 | $rep = qr/
176 | host1
177 | host2
178 | dyhost
179 | /m;
180 | like(mhttp_get('/list', 'localhost', 8081), $rep, '2013-02-26 17:02:13');
181 |
182 | $rep = qr/
183 | host1
184 | server 127.0.0.1:8088 weight=1 .*
185 |
186 | host2
187 | server 127.0.0.1:8089 weight=1 .*
188 |
189 | dyhost
190 | server 127.0.0.1:8088 weight=1 .*
191 | /m;
192 | like(mhttp_get('/detail', 'localhost', 8081), $rep, '2013-02-26 17:36:59');
193 |
194 |
195 | like(mhttp_post('/upstream/dyhost', 'server 127.0.0.1:8088;server 127.0.0.1:8089;', 8081), qr/success/m, '2013-02-26 17:40:24');
196 | like(mhttp_get('/', 'dyhost', 8080), qr/8088|8089/m, '2013-02-26 17:40:28');
197 | like(mhttp_get('/', 'dyhost', 8080), qr/8089|8088/m, '2013-02-26 17:40:32');
198 | like(mhttp_get('/', 'host1', 8080), qr/8088|8089/m, '2013-02-26 17:40:36');
199 | like(mhttp_get('/', 'host2', 8080), qr/8089|8088/m, '2013-02-26 17:40:39');
200 |
201 | $rep = qr/
202 | host1
203 | server 127.0.0.1:8088 weight=1 .*
204 |
205 | host2
206 | server 127.0.0.1:8089 weight=1 .*
207 |
208 | dyhost
209 | server 127.0.0.1:8088 weight=1 .*
210 | server 127.0.0.1:8089 weight=1 .*
211 | /m;
212 | like(mhttp_get('/detail', 'localhost', 8081), $rep, '2013-02-26 17:41:09');
213 |
214 |
215 | like(mhttp_delete('/upstream/dyhost', 8081), qr/success/m, '2013-02-26 16:51:57');
216 | like(mhttp_get('/', 'dyhost', 8080), qr/502/m, '2013-02-26 16:52:00');
217 |
218 | $rep = qr/
219 | host1
220 | host2
221 | /m;
222 | like(mhttp_get('/list', 'localhost', 8081), $rep, '2013-02-26 17:00:54');
223 |
224 | $rep = qr/
225 | host1
226 | server 127.0.0.1:8088 weight=1 .*
227 |
228 | host2
229 | server 127.0.0.1:8089 weight=1 .*
230 | /m;
231 | like(mhttp_get('/detail', 'localhost', 8081), $rep, '2013-02-26 17:42:27');
232 |
233 | like(mhttp_delete('/upstream/dyhost', 8081), qr/404/m, '2013-02-26 17:44:34');
234 |
235 | like(mhttp_delete('/upstream/host1', 8081), qr/success/m, '2013-02-26 17:08:00');
236 | like(mhttp_get('/', 'host1', 8080), qr/502/m, '2013-02-26 17:08:04');
237 |
238 | $rep = qr/
239 | host2
240 | server 127.0.0.1:8089 weight=1 .*
241 | /m;
242 | like(mhttp_get('/detail', 'localhost', 8081), $rep, '2013-02-26 17:45:03');
243 |
244 | like(mhttp_post('/upstream/dyhost', 'server 127.0.0.1:8088;', 8081), qr/success/m, '2013-02-26 17:05:20');
245 | like(mhttp_get('/', 'dyhost', 8080), qr/8088/m, '2013-02-26 17:05:30');
246 |
247 | $rep = qr/
248 | host2
249 | server 127.0.0.1:8089 weight=1 .*
250 |
251 | dyhost
252 | server 127.0.0.1:8088 weight=1 .*
253 | /m;
254 | like(mhttp_get('/detail', 'localhost', 8081), $rep, '2013-06-20 17:46:03');
255 |
256 | like(mhttp_post('/upstream/dyhost', 'server 127.0.0.1:8088 weight=3; server 127.0.0.1:8089 weight=1;', 8081), qr/success/m, '2013-02-28 16:27:45');
257 | like(mhttp_get('/', 'dyhost', 8080), qr/8088|8089/m, '2013-02-28 16:27:49');
258 | like(mhttp_get('/', 'dyhost', 8080), qr/8088|8089/m, '2013-02-28 16:27:52');
259 | like(mhttp_get('/', 'dyhost', 8080), qr/8088|8089/m, '2013-02-28 16:28:44');
260 |
261 | like(mhttp_post('/upstream/dyhost', 'server 127.0.0.1:8088; server 127.0.0.1:18089 backup;', 8081), qr/success/m, '2013-02-28 16:23:41');
262 | like(mhttp_get('/', 'dyhost', 8080), qr/8088/m, '2013-02-28 16:23:48');
263 | like(mhttp_get('/', 'dyhost', 8080), qr/8088/m, '2013-02-28 16:25:32');
264 | like(mhttp_get('/', 'dyhost', 8080), qr/8088/m, '2013-02-28 16:25:35');
265 |
266 | like(mhttp_post('/upstream/dyhost', 'ip_hash;server 127.0.0.1:8088; server 127.0.0.1:8089;', 8081), qr/success/m, '2013-03-04 15:53:41');
267 | like(mhttp_get('/', 'dyhost', 8080), qr/8088/m, '2013-03-04 15:53:44');
268 | like(mhttp_get('/', 'dyhost', 8080), qr/8088/m, '2013-03-04 15:53:46');
269 | like(mhttp_get('/', 'dyhost', 8080), qr/8088/m, '2013-03-04 15:53:49');
270 |
271 | like(mhttp_post('/upstream/dyhost', 'ip_hash aaa;server 127.0.0.1:8088; server 127.0.0.1:8089;', 8081), qr/add server failed/m, '2013-03-05 15:36:40');
272 | like(mhttp_post('/upstream/dyhost', 'ip_hash;aaserver 127.0.0.1:8088; server 127.0.0.1:8089;', 8081), qr/add server failed/m, '2013-03-05 15:37:25');
273 |
274 | like(mhttp_post('/upstream/dyhost', 'server unix:/tmp/dyupssocket;', 8081), qr/success/m, '2013-03-05 16:13:11');
275 | like(mhttp_get('/', 'dyhost', 8080), qr/unix/m, '2013-03-05 16:13:23');
276 |
277 | like(mhttp_post('/upstream/dyhost', 'server 127.0.0.1:8088;check interval=3000 rise=2 fall=5 timeout=1000 type=http default_down=false;', 8081),
278 | qr/success/m, '2013-03-25 10:29:48');
279 | like(mhttp_get('/', 'dyhost', 8080), qr/8088/m, '2013-03-25 10:39:02');
280 | like(mhttp_delete('/upstream/dyhost', 8081), qr/success/m, '2013-03-25 10:39:28');
281 |
282 | like(mhttp_post('/upstream/dyhost', 'server 127.0.0.1:8088;check interval=1000 rise=2 fall=5 timeout=1000 type=http default_down=true;', 8081),
283 | qr/success/m, '2013-03-25 10:49:44');
284 | like(mhttp_get('/', 'dyhost', 8080), qr/502/m, '2013-03-25 10:49:47');
285 | sleep(2);
286 | like(mhttp_get('/', 'dyhost', 8080), qr/8088/m, '2013-03-25 10:50:41');
287 | like(mhttp_delete('/upstream/dyhost', 8081), qr/success/m, '2013-03-25 10:49:51');
288 |
289 |
290 | like(mhttp_post('/upstream/host1', 'server 127.0.0.1:8089;', 8081), qr/success/m, '2014-06-15 07:45:30');
291 | like(mhttp_get('/', 'host1', 8080), qr/8089/m, '2014-06-15 07:45:33');
292 |
293 | like(mhttp_post('/upstream/host1', 'server 127.0.0.1:8088;', 8081), qr/success/m, '2014-06-15 07:45:40');
294 | like(mhttp_get('/', 'host1', 8080), qr/8088/m, '2014-06-15 07:45:43');
295 |
296 |
297 |
298 | $t->stop();
299 | unlink("/tmp/dyupssocket");
300 |
301 | ##############################################################################
302 |
303 | $t->write_file_expand('nginx.conf', <<'EOF');
304 |
305 | %%TEST_GLOBALS%%
306 |
307 | daemon off;
308 |
309 | worker_processes auto;
310 |
311 | events {
312 | accept_mutex off;
313 | }
314 |
315 | http {
316 |
317 | upstream host1 {
318 | server 127.0.0.1:8088;
319 | }
320 |
321 | upstream host2 {
322 | server 127.0.0.1:8089;
323 | }
324 |
325 | server {
326 | listen 8080;
327 |
328 | location / {
329 | proxy_pass http://$host;
330 | }
331 | }
332 |
333 | server {
334 | listen 8088;
335 | location / {
336 | return 200 "8088";
337 | }
338 | }
339 |
340 | server {
341 | listen unix:/tmp/dyupssocket;
342 | location / {
343 | return 200 "unix";
344 | }
345 | }
346 |
347 | server {
348 | listen 8089;
349 | location / {
350 | return 200 "8089";
351 | }
352 | }
353 |
354 | server {
355 | listen 8081;
356 | location / {
357 | dyups_interface;
358 | }
359 | }
360 | }
361 | EOF
362 |
363 | mrun($t);
364 |
365 |
366 | $rep = qr/
367 | host1
368 | host2
369 | /m;
370 | like(mhttp_get('/list', 'localhost', 8081), $rep, '2013-02-26 16:51:46');
371 | $rep = qr/
372 | host1
373 | server 127.0.0.1:8088 weight=1 .*
374 |
375 | host2
376 | server 127.0.0.1:8089 weight=1 .*
377 | /m;
378 | like(mhttp_get('/detail', 'localhost', 8081), $rep, '2013-02-26 17:30:07');
379 | like(mhttp_get('/upstream/host1', 'localhost', 8081), qr/server 127.0.0.1:8088/m, '2013-02-26 17:35:19');
380 |
381 | ###############################################################################
382 |
383 | like(mhttp_post('/upstream/dyhost', 'server 127.0.0.1:8088;', 8081), qr/success/m, '2013-02-26 16:51:51');
384 |
385 | $rep = qr/
386 | host1
387 | host2
388 | dyhost
389 | /m;
390 | like(mhttp_get('/list', 'localhost', 8081), $rep, '2013-02-26 17:02:13');
391 |
392 | $rep = qr/
393 | host1
394 | server 127.0.0.1:8088 weight=1 .*
395 |
396 | host2
397 | server 127.0.0.1:8089 weight=1 .*
398 |
399 | dyhost
400 | server 127.0.0.1:8088 weight=1 .*
401 | /m;
402 | like(mhttp_get('/detail', 'localhost', 8081), $rep, '2013-02-26 17:36:59');
403 |
404 |
405 | like(mhttp_post('/upstream/dyhost', 'server 127.0.0.1:8088;server 127.0.0.1:8089;', 8081), qr/success/m, '2013-02-26 17:40:24');
406 |
407 | $rep = qr/
408 | host1
409 | server 127.0.0.1:8088 weight=1 .*
410 |
411 | host2
412 | server 127.0.0.1:8089 weight=1 .*
413 |
414 | dyhost
415 | server 127.0.0.1:8088 weight=1 .*
416 | server 127.0.0.1:8089 weight=1 .*
417 | /m;
418 | like(mhttp_get('/detail', 'localhost', 8081), $rep, '2013-02-26 17:41:09');
419 |
420 |
421 | $rep = qr/
422 | host1
423 | host2
424 | /m;
425 | like(mhttp_get('/list', 'localhost', 8081), $rep, '2013-02-26 17:00:54');
426 |
427 | $rep = qr/
428 | host1
429 | server 127.0.0.1:8088 weight=1 .*
430 |
431 | host2
432 | server 127.0.0.1:8089 weight=1 .*
433 | /m;
434 | like(mhttp_get('/detail', 'localhost', 8081), $rep, '2013-02-26 17:42:27');
435 |
436 | $rep = qr/
437 | host2
438 | server 127.0.0.1:8089 weight=1 .*
439 | /m;
440 | like(mhttp_get('/detail', 'localhost', 8081), $rep, '2013-02-26 17:45:03');
441 |
442 | like(mhttp_post('/upstream/dyhost', 'server 127.0.0.1:8088;', 8081), qr/success/m, '2013-02-26 17:05:20');
443 |
444 | $rep = qr/
445 | host2
446 | server 127.0.0.1:8089 weight=1 .*
447 |
448 | dyhost
449 | server 127.0.0.1:8088 weight=1 .*
450 | /m;
451 | like(mhttp_get('/detail', 'localhost', 8081), $rep, '2013-02-26 17:46:03');
452 |
453 | like(mhttp_post('/upstream/dyhost', 'server 127.0.0.1:8088 weight=3; server 127.0.0.1:8089 weight=1;', 8081), qr/success/m, '2013-02-28 16:27:45');
454 |
455 | like(mhttp_post('/upstream/dyhost', 'server 127.0.0.1:8088; server 127.0.0.1:18089 backup;', 8081), qr/success/m, '2013-02-28 16:23:41');
456 |
457 | like(mhttp_post('/upstream/dyhost', 'ip_hash;server 127.0.0.1:8088; server 127.0.0.1:8089;', 8081), qr/success/m, '2013-03-04 15:53:41');
458 |
459 | like(mhttp_post('/upstream/dyhost', 'ip_hash aaa;server 127.0.0.1:8088; server 127.0.0.1:8089;', 8081), qr/add server failed/m, '2013-03-05 15:36:40');
460 | like(mhttp_post('/upstream/dyhost', 'ip_hash;aaserver 127.0.0.1:8088; server 127.0.0.1:8089;', 8081), qr/add server failed/m, '2013-03-05 15:37:25');
461 |
462 | like(mhttp_post('/upstream/dyhost', 'server unix:/tmp/dyupssocket;', 8081), qr/success/m, '2013-03-05 16:13:11');
463 |
464 | $t->stop();
465 | unlink("/tmp/dyupssocket");
466 |
467 | ##############################################################################
468 |
469 | ##############################################################################
470 |
471 | $t->write_file_expand('nginx.conf', <<'EOF');
472 |
473 | %%TEST_GLOBALS%%
474 |
475 | daemon off;
476 |
477 | worker_processes auto;
478 |
479 | events {
480 | accept_mutex off;
481 | }
482 |
483 | http {
484 |
485 | server {
486 | listen 8080;
487 |
488 | location / {
489 | proxy_pass http://$host;
490 | }
491 | }
492 |
493 | server {
494 | listen 8088;
495 | location / {
496 | return 200 "8088";
497 | }
498 | }
499 |
500 | server {
501 | listen unix:/tmp/dyupssocket;
502 | location / {
503 | return 200 "unix";
504 | }
505 | }
506 |
507 | server {
508 | listen 8089;
509 | location / {
510 | return 200 "8089";
511 | }
512 | }
513 |
514 | server {
515 | listen 8081;
516 | location / {
517 | dyups_interface;
518 | }
519 |
520 | location /lua/add {
521 | content_by_lua '
522 | local dyups = require "ngx.dyups"
523 | local status, rv = dyups.update("dyhost", [[server 127.0.0.1:8088;]]);
524 | ngx.print(status, rv)
525 | ';
526 | }
527 |
528 | location /lua/delete {
529 | content_by_lua '
530 | local dyups = require "ngx.dyups"
531 | local status, rv = dyups.delete("dyhost");
532 | ngx.print(status, rv)
533 | ';
534 | }
535 | }
536 | }
537 | EOF
538 |
539 | mrun($t);
540 |
541 |
542 | like(mhttp_get('/lua/add', 'localhost', 8081), qr/200success/m, '5/ 5 11:04:49 2014');
543 | sleep(1);
544 | like(mhttp_get('/', 'dyhost', 8080), qr/8088/m, '5/ 5 11:04:42 2014');
545 | like(mhttp_get('/lua/delete', 'localhost', 8081), qr/200success/m, '5/ 5 11:08:08 2014');
546 | sleep(1);
547 | like(mhttp_get('/', 'dyhost', 8080), qr/502/m, '5/ 5 11:08:16 2014');
548 |
549 |
550 | $t->stop();
551 | unlink("/tmp/dyupssocket");
552 |
553 |
554 | ###############################################################################
555 |
556 | sub mhttp($;$;%) {
557 | my ($request, $port, %extra) = @_;
558 | my $reply;
559 | eval {
560 | local $SIG{ALRM} = sub { die "timeout\n" };
561 | local $SIG{PIPE} = sub { die "sigpipe\n" };
562 | alarm(2);
563 | my $s = IO::Socket::INET->new(
564 | Proto => "tcp",
565 | PeerAddr => "127.0.0.1:$port"
566 | );
567 | log_out($request);
568 | $s->print($request);
569 | local $/;
570 | select undef, undef, undef, $extra{sleep} if $extra{sleep};
571 | return '' if $extra{aborted};
572 | $reply = $s->getline();
573 | alarm(0);
574 | };
575 | alarm(0);
576 | if ($@) {
577 | log_in("died: $@");
578 | return undef;
579 | }
580 | log_in($reply);
581 | return $reply;
582 | }
583 |
--------------------------------------------------------------------------------