├── .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 | --------------------------------------------------------------------------------